Merge changes Id1120acb,Ifc6afcda,I8b5da04a,I9389ad9e,I35d60f68 * changes: Update SPIRV-Tools to 8aa423930 Squashed 'third_party/SPIRV-Tools/' changes from c3f22f7cb..8aa423930 VulkanUnitTests: Fix SPIR-V validation issue Update SPIRV-Headers to 204cd131c Squashed 'third_party/SPIRV-Headers/' changes from af64a9e82..204cd131c
diff --git a/tests/VulkanUnitTests/unittests.cpp b/tests/VulkanUnitTests/unittests.cpp index 4055ce1..1c37046 100644 --- a/tests/VulkanUnitTests/unittests.cpp +++ b/tests/VulkanUnitTests/unittests.cpp
@@ -1564,19 +1564,21 @@ "OpBranchConditional %33 %35 %34\n" "%35 = OpLabel\n" "%36 = OpIEqual %20 %28 %30\n" - "OpSelectionMerge %32 None\n" - "OpBranchConditional %36 %37 %32\n" - "%37 = OpLabel\n" - "%38 = OpAccessChain %22 %6 %13 %27\n" - "%39 = OpLoad %12 %38\n" + "OpSelectionMerge %37 None\n" + "OpBranchConditional %36 %38 %37\n" + "%38 = OpLabel\n" + "%39 = OpAccessChain %22 %6 %13 %27\n" + "%40 = OpLoad %12 %39\n" "OpBranch %34\n" + "%37 = OpLabel\n" + "OpBranch %32\n" "%32 = OpLabel\n" "%31 = OpIAdd %14 %30 %23\n" "OpBranch %29\n" "%34 = OpLabel\n" - "%40 = OpPhi %12 %13 %29 %39 %37\n" // %39: phi - "%41 = OpAccessChain %22 %9 %13 %27\n" - "OpStore %41 %40\n" + "%41 = OpPhi %12 %13 %29 %40 %38\n" // %40: phi + "%42 = OpAccessChain %22 %9 %13 %27\n" + "OpStore %42 %41\n" "OpReturn\n" "OpFunctionEnd\n"; test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i; });
diff --git a/third_party/SPIRV-Headers/include/spirv/spir-v.xml b/third_party/SPIRV-Headers/include/spirv/spir-v.xml index 523460d..bcffd61 100644 --- a/third_party/SPIRV-Headers/include/spirv/spir-v.xml +++ b/third_party/SPIRV-Headers/include/spirv/spir-v.xml
@@ -111,6 +111,7 @@ <ids type="opcode" start="5696" end="5823" vendor="Intel" comment="Contact ben.ashbaugh@intel.com"/> <ids type="opcode" start="5824" end="5951" vendor="Intel" comment="Contact michael.kinsner@intel.com"/> <ids type="opcode" start="5952" end="6015" vendor="Codeplay" comment="Contact victor@codeplay.com"/> + <ids type="opcode" start="6016" end="6079" vendor="Khronos" comment="Contact @tobski"/> <!-- Opcodes & enumerants reservable for future use. To get a block, allocate multiples of 64 starting at the lowest available point in this block and add a corresponding <ids> tag immediately above. Make @@ -119,7 +120,7 @@ <!-- Example new block: <ids type="opcode" start="XXXX" end="XXXX+64n-1" vendor="Add vendor" comment="Contact TBD"/> --> - <ids type="opcode" start="6016" end="4294967295" comment="Opcode range reservable for future use by vendors"/> + <ids type="opcode" start="6080" end="4294967295" comment="Opcode range reservable for future use by vendors"/> <!-- SECTION: SPIR-V Loop Control Bit Reservations -->
diff --git a/third_party/SPIRV-Tools/BUILD.bazel b/third_party/SPIRV-Tools/BUILD.bazel index dc1ca60..3046781 100644 --- a/third_party/SPIRV-Tools/BUILD.bazel +++ b/third_party/SPIRV-Tools/BUILD.bazel
@@ -2,6 +2,7 @@ ":build_defs.bzl", "COMMON_COPTS", "DEBUGINFO_GRAMMAR_JSON_FILE", + "CLDEBUGINFO100_GRAMMAR_JSON_FILE", "TEST_COPTS", "base_test", "generate_core_tables", @@ -38,12 +39,6 @@ srcs = ["utils/generate_language_headers.py"], ) -generate_core_tables("1.0") - -generate_core_tables("1.1") - -generate_core_tables("1.2") - generate_core_tables("unified1") generate_enum_string_mapping("unified1") @@ -62,8 +57,12 @@ generate_vendor_tables("debuginfo") +generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_") + generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) +generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) + py_binary( name = "generate_registry_tables", srcs = ["utils/generate_registry_tables.py"], @@ -96,16 +95,15 @@ name = "generated_headers", hdrs = [ ":gen_build_version", - ":gen_core_tables_1.0", - ":gen_core_tables_1.1", - ":gen_core_tables_1.2", ":gen_core_tables_unified1", ":gen_enum_string_mapping", ":gen_extinst_lang_headers_DebugInfo", + ":gen_extinst_lang_headers_OpenCLDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", ":gen_registry_tables", ":gen_vendor_tables_debuginfo", + ":gen_vendor_tables_opencl_debuginfo_100", ":gen_vendor_tables_spv_amd_gcn_shader", ":gen_vendor_tables_spv_amd_shader_ballot", ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter",
diff --git a/third_party/SPIRV-Tools/BUILD.gn b/third_party/SPIRV-Tools/BUILD.gn index 5c8eb9a..3c85c4e 100644 --- a/third_party/SPIRV-Tools/BUILD.gn +++ b/third_party/SPIRV-Tools/BUILD.gn
@@ -32,7 +32,8 @@ "${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" - extinst_file = "source/extinst.debuginfo.grammar.json" + debuginfo_insts_file = "source/extinst.debuginfo.grammar.json" + cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json" sources = [ core_json_file, @@ -47,7 +48,9 @@ "--core-insts-output", rebase_path(core_insts_file, root_build_dir), "--extinst-debuginfo-grammar", - rebase_path(extinst_file, root_build_dir), + rebase_path(debuginfo_insts_file, root_build_dir), + "--extinst-cldebuginfo100-grammar", + rebase_path(cldebuginfo100_insts_file, root_build_dir), "--operand-kinds-output", rebase_path(operand_kinds_file, root_build_dir), ] @@ -64,7 +67,8 @@ core_json_file = "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" - debug_insts_file = "source/extinst.debuginfo.grammar.json" + debuginfo_insts_file = "source/extinst.debuginfo.grammar.json" + cldebuginfo100_insts_file = "source/extinst.opencl.debuginfo.100.grammar.json" extension_enum_file = "${target_gen_dir}/extension_enum.inc" extension_map_file = "${target_gen_dir}/enum_string_mapping.inc" @@ -72,7 +76,9 @@ "--spirv-core-grammar", rebase_path(core_json_file, root_build_dir), "--extinst-debuginfo-grammar", - rebase_path(debug_insts_file, root_build_dir), + rebase_path(debuginfo_insts_file, root_build_dir), + "--extinst-cldebuginfo100-grammar", + rebase_path(cldebuginfo100_insts_file, root_build_dir), "--extension-enum-output", rebase_path(extension_enum_file, root_build_dir), "--enum-string-mapping-output", @@ -100,7 +106,6 @@ "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json" glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc" - debug_insts_file = "source/extinst.debuginfo.grammar.json" args = [ "--spirv-core-grammar", @@ -109,8 +114,6 @@ rebase_path(glsl_json_file, root_build_dir), "--glsl-insts-output", rebase_path(glsl_insts_file, root_build_dir), - "--extinst-debuginfo-grammar", - rebase_path(debug_insts_file, root_build_dir), ] inputs = [ core_json_file, @@ -134,7 +137,6 @@ "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" opengl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json" opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc" - debug_insts_file = "source/extinst.debuginfo.grammar.json" args = [ "--spirv-core-grammar", @@ -143,8 +145,6 @@ rebase_path(opengl_json_file, root_build_dir), "--opencl-insts-output", rebase_path(opencl_insts_file, root_build_dir), - "--extinst-debuginfo-grammar", - rebase_path(debug_insts_file, root_build_dir), ] inputs = [ core_json_file, @@ -164,13 +164,12 @@ name = invoker.name extinst_output_base = "${target_gen_dir}/${name}" - debug_insts_file = "source/extinst.debuginfo.grammar.json" args = [ "--extinst-name", "${name}", "--extinst-grammar", - rebase_path(debug_insts_file, root_build_dir), + rebase_path(invoker.grammar_file, root_build_dir), "--extinst-output-base", rebase_path(extinst_output_base, root_build_dir), ] @@ -198,6 +197,8 @@ rebase_path(extinst_vendor_grammar, root_build_dir), "--vendor-insts-output", rebase_path(extinst_file, root_build_dir), + "--vendor-operand-kind-prefix", + invoker.operand_kind_prefix ] inputs = [ extinst_vendor_grammar, @@ -256,21 +257,28 @@ spvtools_opencl_tables("opencl1-0") { version = "1.0" } -spvtools_language_header("unified1") { +spvtools_language_header("debuginfo") { name = "DebugInfo" + grammar_file = "source/extinst.debuginfo.grammar.json" +} +spvtools_language_header("cldebuginfo100") { + name = "OpenCLDebugInfo100" + grammar_file = "source/extinst.opencl.debuginfo.100.grammar.json" } spvtools_vendor_tables = [ - "spv-amd-shader-explicit-vertex-parameter", - "spv-amd-shader-trinary-minmax", - "spv-amd-gcn-shader", - "spv-amd-shader-ballot", - "debuginfo", + ["spv-amd-shader-explicit-vertex-parameter", ""], + ["spv-amd-shader-trinary-minmax", ""], + ["spv-amd-gcn-shader", ""], + ["spv-amd-shader-ballot", ""], + ["debuginfo", ""], + ["opencl.debuginfo.100", "CLDEBUG100_"], ] -foreach(table, spvtools_vendor_tables) { +foreach(table_def, spvtools_vendor_tables) { spvtools_vendor_table(table) { - name = table + name = table_def[0] + operand_kind_prefix = table_def[1] } } @@ -320,10 +328,12 @@ ":spvtools_core_tables_unified1", ":spvtools_generators_inc", ":spvtools_glsl_tables_glsl1-0", - ":spvtools_language_header_unified1", + ":spvtools_language_header_debuginfo", + ":spvtools_language_header_cldebuginfo100", ":spvtools_opencl_tables_opencl1-0", ] - foreach(target_name, spvtools_vendor_tables) { + foreach(table_def, spvtools_vendor_tables) { + target_name = table_def[0] deps += [ ":spvtools_vendor_tables_$target_name" ] } @@ -818,6 +828,7 @@ "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", @@ -864,7 +875,8 @@ deps = [ ":spvtools", - ":spvtools_language_header_unified1", + ":spvtools_language_header_debuginfo", + ":spvtools_language_header_cldebuginfo100", ":spvtools_val", "//testing/gmock", "//testing/gtest",
diff --git a/third_party/SPIRV-Tools/CHANGES b/third_party/SPIRV-Tools/CHANGES index 5263f12..2f2968e 100644 --- a/third_party/SPIRV-Tools/CHANGES +++ b/third_party/SPIRV-Tools/CHANGES
@@ -1,6 +1,9 @@ Revision history for SPIRV-Tools -v2019.5-dev 2019-10-21 +v2020.1-dev 2019-12-11 + - Start v2020.1-dev + +v2019.5 2019-12-11 - General: - Export SPIRV-Tools targets on installation - SPIRV-Tools support for SPIR-V 1.5 (#2865) @@ -12,6 +15,12 @@ - Improved CMake install step. (#2963) - Add fuzzer for spirv-dis call path (#2977) - Ensure timestamp does not vary with timezone. (#2982) + - Add a vscode extension for SPIR-V disassembly files (#2987) + - Add iOS as a supported platform (#3001) + - utils/vscode: Add SPIR-V language server support + - Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054) + - Permit the debug instructions in WebGPU SPIR-V (#3063) + - Add support for Fuchsia. (#3062) - Optimizer - Add descriptor array scalar replacement (#2742) - Add pass to wrap OpKill in a function call (#2790) @@ -24,6 +33,10 @@ - Better handling of OpKill in continues (#2842,#2922,#2933) - Enable OpTypeCooperativeMatrix specialization (#2927) - Support constant-folding UConvert and SConvert (#2960) + - Update Offset to ConstOffset bitmask if operand is constant. (#3024) + - Improve RegisterSizePasses (#3059) + - Folding: perform add and sub on mismatched integer types (#3084) + - Graphics robust access: use signed clamp (#3073) Fixes: - Instrument: Fix version 2 output record write for tess eval shaders. (#2782) - Instrument: Add support for Buffer Device Address extension (#2792) @@ -37,10 +50,12 @@ - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862) - Relaxed bitcast with pointers (#2878) - Validate physical storage buffer restrictions (#2930) - - Add SPV_KHR_shader_clock validation (#2879) + - Add SPV_KHR_shader_clock validation (#2879, #3013) - Validate that selections are structured (#2962) - Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980) - Check that derivatives operate on 32-bit values (#2983) + - Validate array stride does not cause overlap (#3028) + - Validate nested constructs (#3068) Fixes: - Fix validation of constant matrices (#2794) - Update "remquor" validation @@ -63,6 +78,12 @@ - Add fuzzer pass to change loop controls (#2949) - Add fuzzer pass to change function controls (#2951) - Add fuzzer pass to add NoContraction decorations (#2950) + - Add missing functionality for matrix composites (#2974) + - Fuzzer pass to adjust memory access operands (#2968) + - Transformation to extract from a composite object (#2991) + - Vector shuffle transformation (#3015) + - Improve debugging facilities (#3074) + - Function outlining fuzzer pass (#3078) v2019.4 2019-08-08
diff --git a/third_party/SPIRV-Tools/CMakeLists.txt b/third_party/SPIRV-Tools/CMakeLists.txt index 19108f3..6ed56a8 100644 --- a/third_party/SPIRV-Tools/CMakeLists.txt +++ b/third_party/SPIRV-Tools/CMakeLists.txt
@@ -29,7 +29,6 @@ set(SPIRV_TOOLS "SPIRV-Tools") include(GNUInstallDirs) -include(cmake/setup_build.cmake) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_STANDARD 11) @@ -54,6 +53,8 @@ set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") add_definitions(-DSPIRV_FREEBSD) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") + add_definitions(-DSPIRV_FUCHSIA) else() message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!") endif() @@ -215,7 +216,7 @@ endmacro() else() macro(spvtools_config_package_dir TARGET PATH) - set(${PATH} lib/cmake/${TARGET}) + set(${PATH} ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET}) endmacro() endif()
diff --git a/third_party/SPIRV-Tools/build_defs.bzl b/third_party/SPIRV-Tools/build_defs.bzl index 483fd2a..5d913a1 100644 --- a/third_party/SPIRV-Tools/build_defs.bzl +++ b/third_party/SPIRV-Tools/build_defs.bzl
@@ -40,6 +40,7 @@ }) DEBUGINFO_GRAMMAR_JSON_FILE = "source/extinst.debuginfo.grammar.json" +CLDEBUGINFO100_GRAMMAR_JSON_FILE = "source/extinst.opencl.debuginfo.100.grammar.json" def generate_core_tables(version = None): if not version: @@ -47,6 +48,7 @@ grammars = [ "@spirv_headers//:spirv_core_grammar_" + version, DEBUGINFO_GRAMMAR_JSON_FILE, + CLDEBUGINFO100_GRAMMAR_JSON_FILE, ] outs = [ "core.insts-{}.inc".format(version), @@ -61,8 +63,9 @@ "$(location :generate_grammar_tables) " + "--spirv-core-grammar=$(location {0}) " + "--extinst-debuginfo-grammar=$(location {1}) " + - "--core-insts-output=$(location {2}) " + - "--operand-kinds-output=$(location {3})" + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--core-insts-output=$(location {3}) " + + "--operand-kinds-output=$(location {4})" ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], @@ -74,6 +77,7 @@ grammars = [ "@spirv_headers//:spirv_core_grammar_" + version, DEBUGINFO_GRAMMAR_JSON_FILE, + CLDEBUGINFO100_GRAMMAR_JSON_FILE, ] outs = [ "extension_enum.inc", @@ -88,8 +92,9 @@ "$(location :generate_grammar_tables) " + "--spirv-core-grammar=$(location {0}) " + "--extinst-debuginfo-grammar=$(location {1}) " + - "--extension-enum-output=$(location {2}) " + - "--enum-string-mapping-output=$(location {3})" + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--extension-enum-output=$(location {3}) " + + "--enum-string-mapping-output=$(location {4})" ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], @@ -137,13 +142,14 @@ visibility = ["//visibility:private"], ) -def generate_vendor_tables(extension = None): +def generate_vendor_tables(extension, operand_kind_prefix = ""): if not extension: fail("Must specify extension", "extension") - extension_rule = extension.replace("-", "_") + extension_rule = extension.replace("-", "_").replace(".", "_") grammars = ["source/extinst.{}.grammar.json".format(extension)] outs = ["{}.insts.inc".format(extension)] - fmtargs = grammars + outs + prefices = [operand_kind_prefix] + fmtargs = grammars + outs + prefices native.genrule( name = "gen_vendor_tables_" + extension_rule, srcs = grammars, @@ -151,7 +157,8 @@ cmd = ( "$(location :generate_grammar_tables) " + "--extinst-vendor-grammar=$(location {0}) " + - "--vendor-insts-output=$(location {1})" + "--vendor-insts-output=$(location {1}) " + + "--vendor-operand-kind-prefix={2}" ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"],
diff --git a/third_party/SPIRV-Tools/cmake/setup_build.cmake b/third_party/SPIRV-Tools/cmake/setup_build.cmake deleted file mode 100644 index 6ba4c53..0000000 --- a/third_party/SPIRV-Tools/cmake/setup_build.cmake +++ /dev/null
@@ -1,20 +0,0 @@ -# Find nosetests; see spirv_add_nosetests() for opting in to nosetests in a -# specific directory. -find_program(NOSETESTS_EXE NAMES nosetests PATHS $ENV{PYTHON_PACKAGE_PATH}) -if (NOT NOSETESTS_EXE) - message(STATUS "SPIRV-Tools: nosetests was not found - python support code will not be tested") -else() - message(STATUS "SPIRV-Tools: nosetests found - python support code will be tested") -endif() - -# Run nosetests on file ${PREFIX}_nosetest.py. Nosetests will look for classes -# and functions whose names start with "nosetest". The test name will be -# ${PREFIX}_nosetests. -function(spirv_add_nosetests PREFIX) - if(NOT "${SPIRV_SKIP_TESTS}" AND NOSETESTS_EXE) - add_test( - NAME ${PREFIX}_nosetests - COMMAND ${NOSETESTS_EXE} -m "^[Nn]ose[Tt]est" -v - ${CMAKE_CURRENT_SOURCE_DIR}/${PREFIX}_nosetest.py) - endif() -endfunction()
diff --git a/third_party/SPIRV-Tools/external/CMakeLists.txt b/third_party/SPIRV-Tools/external/CMakeLists.txt index 3190f4b..8bde13c 100644 --- a/third_party/SPIRV-Tools/external/CMakeLists.txt +++ b/third_party/SPIRV-Tools/external/CMakeLists.txt
@@ -108,6 +108,9 @@ set(protobuf_BUILD_TESTS OFF CACHE BOOL "Disable protobuf tests") set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Do not build protobuf static runtime") if (IS_DIRECTORY ${PROTOBUF_DIR}) + if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang) + add_definitions(-Wno-inconsistent-missing-override) + endif() add_subdirectory(${PROTOBUF_DIR} EXCLUDE_FROM_ALL) else() message(FATAL_ERROR
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp index 681d008..2dcb333 100644 --- a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp +++ b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
@@ -35,10 +35,9 @@ // generated by InstrumentPass::GenDebugStreamWrite. This method is utilized // by InstBindlessCheckPass. // -// kInst2* values support version 2 of the output record format. These should -// be used if available and version 2 is enabled. Version 1 is DEPRECATED. -// Specifically, version 1 uses two words for the stage-specific section of -// the output record; version 2 uses three words. +// kInst2* values support version 2 of the output record format and were used +// for the transition to this format. These values have now been transferred +// to the original kInst* values. The kInst2* values are therefore DEPRECATED. // // The first member of the debug output buffer contains the next available word // in the data stream to be written. Shaders will atomically read and update @@ -124,7 +123,7 @@ static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2; // Size of Common and Stage-specific Members -static const int kInstStageOutCnt = kInstCommonOutCnt + 2; +static const int kInstStageOutCnt = kInstCommonOutCnt + 3; static const int kInst2StageOutCnt = kInstCommonOutCnt + 3; // Validation Error Code Offset @@ -160,6 +159,10 @@ // A buffer address unalloc error will output the 64-bit pointer in // two 32-bit pieces, lower bits first. +static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1; +static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2; +static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3; + static const int kInst2BuffAddrUnallocOutDescPtrLo = kInst2StageOutCnt + 1; static const int kInst2BuffAddrUnallocOutDescPtrHi = kInst2StageOutCnt + 2; static const int kInst2BuffAddrUnallocOutCnt = kInst2StageOutCnt + 3;
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h index dd2526b..5dcb81a 100644 --- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h +++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
@@ -225,13 +225,23 @@ // A sequence of zero or more pairs of (Id, Literal integer) LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER), - // The following are concrete enum types. + // The following are concrete enum types from the DebugInfo extended + // instruction set. SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, // DebugInfo Sec 3.2. A mask. SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // DebugInfo Sec 3.3 SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, // DebugInfo Sec 3.4 SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, // DebugInfo Sec 3.5 SPV_OPERAND_TYPE_DEBUG_OPERATION, // DebugInfo Sec 3.6 + // The following are concrete enum types from the OpenCL.DebugInfo.100 + // extended instruction set. + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS, // Sec 3.2. A Mask + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // Sec 3.3 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE, // Sec 3.4 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER, // Sec 3.5 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, // Sec 3.6 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY, // Sec 3.7 + // This is a sentinel value, and does not represent an operand type. // It should come last. SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, @@ -248,6 +258,12 @@ SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, SPV_EXT_INST_TYPE_DEBUGINFO, + SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + + // Multiple distinct extended instruction set types could return this + // value, if they are prefixed with NonSemantic. and are otherwise + // unrecognised + SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN, SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t) } spv_ext_inst_type_t; @@ -627,6 +643,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( spv_fuzzer_options options, uint32_t shrinker_step_limit); +// Enables running the validator after every pass is applied during a fuzzing +// run. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options); + // Encodes the given SPIR-V assembly text to its binary representation. The // length parameter specifies the number of bytes for text. Encoded binary will // be stored into *binary. Any error will be written into *diagnostic if
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp index b09fdd4..ceadef8 100644 --- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp +++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
@@ -229,6 +229,11 @@ spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); } + // See spvFuzzerOptionsEnableFuzzerPassValidation. + void enable_fuzzer_pass_validation() { + spvFuzzerOptionsEnableFuzzerPassValidation(options_); + } + private: spv_fuzzer_options options_; };
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp index 509051d..ba8cbaf 100644 --- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp +++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -685,9 +685,9 @@ // any resulting half precision values back to float32 as needed. No variables // are changed. No image operations are changed. // -// Best if run late since it will generate better code with unneeded function -// scope loads and stores and composite inserts and extracts removed. Also best -// if followed by instruction simplification, redundancy elimination and DCE. +// Best if run after function scope store/load and composite operation +// eliminations are run. Also best if followed by instruction simplification, +// redundancy elimination and DCE. Optimizer::PassToken CreateConvertRelaxedToHalfPass(); // Create relax float ops pass. @@ -748,7 +748,7 @@ // |version| specifies the buffer record format. Optimizer::PassToken CreateInstBindlessCheckPass( uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false, - bool input_init_enable = false, uint32_t version = 1); + bool input_init_enable = false, uint32_t version = 2); // Create a pass to instrument physical buffer address checking // This pass instruments all physical buffer address references to check that @@ -827,6 +827,16 @@ // of range. (The module is already invalid if that is the case.) // - TODO(dneto): The OpImageTexelPointer coordinate component is not 32-bits // wide. +// +// NOTE: Access chain indices are always treated as signed integers. So +// if an array has a fixed size of more than 2^31 elements, then elements +// from 2^31 and above are never accessible with a 32-bit index, +// signed or unsigned. For this case, this pass will clamp the index +// between 0 and at 2^31-1, inclusive. +// Similarly, if an array has more then 2^15 element and is accessed with +// a 16-bit index, then elements from 2^15 and above are not accessible. +// In this case, the pass will clamp the index between 0 and 2^15-1 +// inclusive. Optimizer::PassToken CreateGraphicsRobustAccessPass(); // Create descriptor scalar replacement pass.
diff --git a/third_party/SPIRV-Tools/source/CMakeLists.txt b/third_party/SPIRV-Tools/source/CMakeLists.txt index f3b5942..4e7e10c 100644 --- a/third_party/SPIRV-Tools/source/CMakeLists.txt +++ b/third_party/SPIRV-Tools/source/CMakeLists.txt
@@ -20,6 +20,7 @@ # For now, assume the DebugInfo grammar file is in the current directory. # It might migrate to SPIRV-Headers. set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json") +set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.debuginfo.100.grammar.json") # macro() definitions are used in the following because we need to append .inc # file paths into some global lists (*_CPP_DEPENDS). And those global lists are @@ -33,9 +34,13 @@ COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} --spirv-core-grammar=${GRAMMAR_JSON_FILE} --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} --core-insts-output=${GRAMMAR_INSTS_INC_FILE} --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE} - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} + ${GRAMMAR_JSON_FILE} + ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.") list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE}) list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE}) @@ -50,9 +55,13 @@ COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} --spirv-core-grammar=${GRAMMAR_JSON_FILE} --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE} --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} + ${GRAMMAR_JSON_FILE} + ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.") list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE}) list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}) @@ -101,13 +110,14 @@ list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) endmacro(spvtools_opencl_tables) -macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME) +macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") add_custom_command(OUTPUT ${INSTS_FILE} COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} --extinst-vendor-grammar=${GRAMMAR_FILE} --vendor-insts-output=${INSTS_FILE} + --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX} DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE} COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.") add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE}) @@ -134,12 +144,14 @@ spvtools_enum_string_mapping("unified1") spvtools_opencl_tables("unified1") spvtools_glsl_tables("unified1") -spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp") -spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm") -spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs") -spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb") -spvtools_vendor_tables("debuginfo" "debuginfo") +spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "") +spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "") +spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") +spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") +spvtools_vendor_tables("debuginfo" "debuginfo" "") +spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) spvtools_vimsyntax("unified1" "1.0") add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})
diff --git a/third_party/SPIRV-Tools/source/binary.cpp b/third_party/SPIRV-Tools/source/binary.cpp index 1d31283..0463061 100644 --- a/third_party/SPIRV-Tools/source/binary.cpp +++ b/third_party/SPIRV-Tools/source/binary.cpp
@@ -477,9 +477,22 @@ assert(SpvOpExtInst == opcode); assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE); spv_ext_inst_desc ext_inst; - if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst)) - return diagnostic() << "Invalid extended instruction number: " << word; - spvPushOperandTypes(ext_inst->operandTypes, expected_operands); + if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + // if we know about this ext inst, push the expected operands + spvPushOperandTypes(ext_inst->operandTypes, expected_operands); + } else { + // if we don't know this extended instruction and the set isn't + // non-semantic, we cannot process further + if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) { + return diagnostic() + << "Invalid extended instruction number: " << word; + } else { + // for non-semantic instruction sets, we know the form of all such + // extended instructions contains a series of IDs as parameters + expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } + } } break; case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { @@ -623,7 +636,12 @@ case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: - case SPV_OPERAND_TYPE_DEBUG_OPERATION: { + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: { // A single word that is a plain enum value. // Map an optional operand type to its corresponding concrete type. @@ -647,6 +665,7 @@ case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: { // This operand is a mask.
diff --git a/third_party/SPIRV-Tools/source/disassemble.cpp b/third_party/SPIRV-Tools/source/disassemble.cpp index c116f50..4b3972b 100644 --- a/third_party/SPIRV-Tools/source/disassemble.cpp +++ b/third_party/SPIRV-Tools/source/disassemble.cpp
@@ -217,10 +217,18 @@ break; case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { spv_ext_inst_desc ext_inst; - if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst)) - assert(false && "should have caught this earlier"); SetRed(); - stream_ << ext_inst->name; + if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + stream_ << ext_inst->name; + } else { + if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) { + assert(false && "should have caught this earlier"); + } else { + // for non-semantic instruction sets we can just print the number + stream_ << word; + } + } } break; case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { spv_opcode_desc opcode_desc; @@ -272,7 +280,12 @@ case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: - case SPV_OPERAND_TYPE_DEBUG_OPERATION: { + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: { spv_operand_desc entry; if (grammar_.lookupOperand(operand.type, word, &entry)) assert(false && "should have caught this earlier"); @@ -285,6 +298,7 @@ case SPV_OPERAND_TYPE_MEMORY_ACCESS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: EmitMaskOperand(operand.type, word); break; default:
diff --git a/third_party/SPIRV-Tools/source/ext_inst.cpp b/third_party/SPIRV-Tools/source/ext_inst.cpp index 0499e23..e332f0d 100644 --- a/third_party/SPIRV-Tools/source/ext_inst.cpp +++ b/third_party/SPIRV-Tools/source/ext_inst.cpp
@@ -28,6 +28,7 @@ #include "debuginfo.insts.inc" #include "glsl.std.450.insts.inc" +#include "opencl.debuginfo.100.insts.inc" #include "opencl.std.insts.inc" #include "spirv-tools/libspirv.h" @@ -51,6 +52,8 @@ ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries}, {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries), debuginfo_entries}, + {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, }; static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0), @@ -116,9 +119,24 @@ if (!strcmp("DebugInfo", name)) { return SPV_EXT_INST_TYPE_DEBUGINFO; } + if (!strcmp("OpenCL.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; + } + // ensure to add any known non-semantic extended instruction sets + // above this point, and update spvExtInstIsNonSemantic() + if (!strncmp("NonSemantic.", name, 12)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN; + } return SPV_EXT_INST_TYPE_NONE; } +bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { + if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) { + return true; + } + return false; +} + spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, const spv_ext_inst_type_t type, const char* name,
diff --git a/third_party/SPIRV-Tools/source/ext_inst.h b/third_party/SPIRV-Tools/source/ext_inst.h index a821cc2..b42d82b 100644 --- a/third_party/SPIRV-Tools/source/ext_inst.h +++ b/third_party/SPIRV-Tools/source/ext_inst.h
@@ -21,6 +21,9 @@ // Gets the type of the extended instruction set with the specified name. spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name); +// Returns true if the extended instruction set is non-semantic +bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type); + // Finds the named extented instruction of the given type in the given extended // instruction table. On success, returns SPV_SUCCESS and writes a handle of // the instruction entry into *entry.
diff --git a/third_party/SPIRV-Tools/source/extinst.opencl.debuginfo.100.grammar.json b/third_party/SPIRV-Tools/source/extinst.opencl.debuginfo.100.grammar.json new file mode 100644 index 0000000..08062be --- /dev/null +++ b/third_party/SPIRV-Tools/source/extinst.opencl.debuginfo.100.grammar.json
@@ -0,0 +1,632 @@ +{ + "copyright" : [ + "Copyright (c) 2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 200, + "revision" : 2, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "LiteralInteger", "name" : "'DWARF Version'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "SourceLanguage", "name" : "'Language'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "StorageClass", "name" : "'Storage Class'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugCompositeType", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Child'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "DebugOperation", "name" : "'OpCode'" }, + { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + }, + { + "opname" : "DebugImportedEntity", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugImportedEntity", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Entity'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugSource", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + }, + { + "enumerant" : "FlagIsEnumClass", + "value" : "0x4000" + }, + { + "enumerant" : "FlagTypePassByValue", + "value" : "0x8000" + }, + { + "enumerant" : "FlagTypePassByReference", + "value" : "0x10000" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "3" + }, + { + "enumerant" : "Signed", + "value" : "4" + }, + { + "enumerant" : "SignedChar", + "value" : "5" + }, + { + "enumerant" : "Unsigned", + "value" : "6" + }, + { + "enumerant" : "UnsignedChar", + "value" : "7" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + }, + { + "enumerant" : "AtomicType", + "value" : "3" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Fragment", + "value" : "9", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugImportedEntity", + "enumerants" : [ + { + "enumerant" : "ImportedModule", + "value" : "0" + }, + { + "enumerant" : "ImportedDeclaration", + "value" : "1" + } + ] + } + ] +}
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt index 97f8976..bc7d453 100644 --- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt +++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -47,12 +47,15 @@ fuzzer_pass_apply_id_synonyms.h fuzzer_pass_construct_composites.h fuzzer_pass_copy_objects.h + fuzzer_pass_merge_blocks.h fuzzer_pass_obfuscate_constants.h + fuzzer_pass_outline_functions.h fuzzer_pass_permute_blocks.h fuzzer_pass_split_blocks.h fuzzer_util.h id_use_descriptor.h instruction_descriptor.h + instruction_message.h protobufs/spirvfuzz_protobufs.h pseudo_random_generator.h random_generator.h @@ -60,18 +63,29 @@ shrinker.h transformation.h transformation_add_constant_boolean.h + transformation_add_constant_composite.h transformation_add_constant_scalar.h transformation_add_dead_break.h transformation_add_dead_continue.h + transformation_add_function.h + transformation_add_global_undef.h + transformation_add_global_variable.h transformation_add_no_contraction_decoration.h + transformation_add_type_array.h transformation_add_type_boolean.h transformation_add_type_float.h + transformation_add_type_function.h transformation_add_type_int.h + transformation_add_type_matrix.h transformation_add_type_pointer.h + transformation_add_type_struct.h + transformation_add_type_vector.h transformation_composite_construct.h transformation_composite_extract.h transformation_copy_object.h + transformation_merge_blocks.h transformation_move_block_down.h + transformation_outline_function.h transformation_replace_boolean_constant_with_constant_binary.h transformation_replace_constant_with_uniform.h transformation_replace_id_with_synonym.h @@ -101,30 +115,44 @@ fuzzer_pass_apply_id_synonyms.cpp fuzzer_pass_construct_composites.cpp fuzzer_pass_copy_objects.cpp + fuzzer_pass_merge_blocks.cpp fuzzer_pass_obfuscate_constants.cpp + fuzzer_pass_outline_functions.cpp fuzzer_pass_permute_blocks.cpp fuzzer_pass_split_blocks.cpp fuzzer_util.cpp id_use_descriptor.cpp instruction_descriptor.cpp + instruction_message.cpp pseudo_random_generator.cpp random_generator.cpp replayer.cpp shrinker.cpp transformation.cpp transformation_add_constant_boolean.cpp + transformation_add_constant_composite.cpp transformation_add_constant_scalar.cpp transformation_add_dead_break.cpp transformation_add_dead_continue.cpp + transformation_add_function.cpp + transformation_add_global_undef.cpp + transformation_add_global_variable.cpp transformation_add_no_contraction_decoration.cpp + transformation_add_type_array.cpp transformation_add_type_boolean.cpp transformation_add_type_float.cpp + transformation_add_type_function.cpp transformation_add_type_int.cpp + transformation_add_type_matrix.cpp transformation_add_type_pointer.cpp + transformation_add_type_struct.cpp + transformation_add_type_vector.cpp transformation_composite_construct.cpp transformation_composite_extract.cpp transformation_copy_object.cpp + transformation_merge_blocks.cpp transformation_move_block_down.cpp + transformation_outline_function.cpp transformation_replace_boolean_constant_with_constant_binary.cpp transformation_replace_constant_with_uniform.cpp transformation_replace_id_with_synonym.cpp
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp index 01b4258..95913d0 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -31,7 +31,9 @@ #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" #include "source/fuzz/fuzzer_pass_construct_composites.h" #include "source/fuzz/fuzzer_pass_copy_objects.h" +#include "source/fuzz/fuzzer_pass_merge_blocks.h" #include "source/fuzz/fuzzer_pass_obfuscate_constants.h" +#include "source/fuzz/fuzzer_pass_outline_functions.h" #include "source/fuzz/fuzzer_pass_permute_blocks.h" #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" @@ -65,13 +67,26 @@ } // namespace struct Fuzzer::Impl { - explicit Impl(spv_target_env env) : target_env(env) {} + explicit Impl(spv_target_env env, uint32_t random_seed, + bool validate_after_each_pass) + : target_env(env), + seed(random_seed), + validate_after_each_fuzzer_pass(validate_after_each_pass) {} - const spv_target_env target_env; // Target environment. - MessageConsumer consumer; // Message consumer. + bool ApplyPassAndCheckValidity(FuzzerPass* pass, + const opt::IRContext& ir_context, + const spvtools::SpirvTools& tools) const; + + const spv_target_env target_env; // Target environment. + const uint32_t seed; // Seed for random number generator. + bool validate_after_each_fuzzer_pass; // Determines whether the validator + // should be invoked after every fuzzer pass. + MessageConsumer consumer; // Message consumer. }; -Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {} +Fuzzer::Fuzzer(spv_target_env env, uint32_t seed, + bool validate_after_each_fuzzer_pass) + : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {} Fuzzer::~Fuzzer() = default; @@ -79,10 +94,27 @@ impl_->consumer = std::move(c); } +bool Fuzzer::Impl::ApplyPassAndCheckValidity( + FuzzerPass* pass, const opt::IRContext& ir_context, + const spvtools::SpirvTools& tools) const { + pass->Apply(); + if (validate_after_each_fuzzer_pass) { + std::vector<uint32_t> binary_to_validate; + ir_context.module()->ToBinary(&binary_to_validate, false); + if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) { + consumer(SPV_MSG_INFO, nullptr, {}, + "Binary became invalid during fuzzing (set a breakpoint to " + "inspect); stopping."); + return false; + } + } + return true; +} + Fuzzer::FuzzerResultStatus Fuzzer::Run( const std::vector<uint32_t>& binary_in, const protobufs::FactSequence& initial_facts, - spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out, + std::vector<uint32_t>* binary_out, protobufs::TransformationSequence* transformation_sequence_out) const { // Check compatibility between the library version being linked with and the // header files being used. @@ -108,10 +140,8 @@ impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size()); assert(ir_context); - // Make a PRNG, either from a given seed or from a random device. - PseudoRandomGenerator random_generator( - options->has_random_seed ? options->random_seed - : static_cast<uint32_t>(std::random_device()())); + // Make a PRNG from the seed passed to the fuzzer on creation. + PseudoRandomGenerator random_generator(impl_->seed); // 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 @@ -128,9 +158,13 @@ // Add some essential ingredients to the module if they are not already // present, such as boolean constants. - FuzzerPassAddUsefulConstructs(ir_context.get(), &fact_manager, - &fuzzer_context, transformation_sequence_out) - .Apply(); + FuzzerPassAddUsefulConstructs add_useful_constructs( + ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); + if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context, + tools)) { + return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; + } // Apply some semantics-preserving passes. std::vector<std::unique_ptr<FuzzerPass>> passes; @@ -150,9 +184,15 @@ MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass<FuzzerPassMergeBlocks>(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass<FuzzerPassObfuscateConstants>(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass<FuzzerPassOutlineFunctions>(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); @@ -168,7 +208,11 @@ (is_first || fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) { is_first = false; - passes[fuzzer_context.RandomIndex(passes)]->Apply(); + if (!impl_->ApplyPassAndCheckValidity( + passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context, + tools)) { + return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; + } } // Now apply some passes that it does not make sense to apply repeatedly, @@ -186,15 +230,13 @@ MaybeAddPass<FuzzerPassAdjustSelectionControls>( &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass<FuzzerPassAddNoContractionDecorations>( + &final_passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); for (auto& pass : final_passes) { - pass->Apply(); - } - - if (fuzzer_context.ChooseEven()) { - FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager, - &fuzzer_context, - transformation_sequence_out) - .Apply(); + if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) { + return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule; + } } // Encode the module as a binary.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer.h index a257c2a..c1d2dee 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.h +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.h
@@ -32,11 +32,16 @@ enum class FuzzerResultStatus { kComplete, kFailedToCreateSpirvToolsInterface, + kFuzzerPassLedToInvalidModule, kInitialBinaryInvalid, }; - // Constructs a fuzzer from the given target environment. - explicit Fuzzer(spv_target_env env); + // Constructs a fuzzer from the given target environment |env|. |seed| is a + // seed for pseudo-random number generation. + // |validate_after_each_fuzzer_pass| controls whether the validator will be + // invoked after every fuzzer pass is applied. + explicit Fuzzer(spv_target_env env, uint32_t seed, + bool validate_after_each_fuzzer_pass); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; @@ -51,14 +56,13 @@ void SetMessageConsumer(MessageConsumer consumer); // Transforms |binary_in| to |binary_out| by running a number of randomized - // fuzzer passes, controlled via |options|. Initial facts about the input - // binary and the context in which it will execute are provided via - // |initial_facts|. The transformation sequence that was applied is returned - // via |transformation_sequence_out|. + // fuzzer passes. Initial facts about the input binary and the context in + // which it will execute are provided via |initial_facts|. The transformation + // sequence that was applied is returned via |transformation_sequence_out|. FuzzerResultStatus Run( const std::vector<uint32_t>& binary_in, const protobufs::FactSequence& initial_facts, - spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out, + std::vector<uint32_t>* binary_out, protobufs::TransformationSequence* transformation_sequence_out) const; private:
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp index 356cb35..98585d9 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -34,10 +34,12 @@ 90}; const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20, 90}; -const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50}; const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50}; +const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50}; +const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95}; const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50}; const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90}; +const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90}; const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95}; @@ -81,10 +83,13 @@ chance_of_constructing_composite_ = ChooseBetweenMinAndMax(kChanceOfConstructingComposite); chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); + chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks); chance_of_moving_block_down_ = ChooseBetweenMinAndMax(kChanceOfMovingBlockDown); chance_of_obfuscating_constant_ = ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant); + chance_of_outlining_function_ = + ChooseBetweenMinAndMax(kChanceOfOutliningFunction); chance_of_replacing_id_with_synonym_ = ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h index c8242e6..619c131 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -81,10 +81,14 @@ return chance_of_constructing_composite_; } uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; } + uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; } uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; } uint32_t GetChanceOfObfuscatingConstant() { return chance_of_obfuscating_constant_; } + uint32_t GetChanceOfOutliningFunction() { + return chance_of_outlining_function_; + } uint32_t GetChanceOfReplacingIdWithSynonym() { return chance_of_replacing_id_with_synonym_; } @@ -119,8 +123,10 @@ uint32_t chance_of_adjusting_selection_control_; uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; + uint32_t chance_of_merging_blocks_; uint32_t chance_of_moving_block_down_; uint32_t chance_of_obfuscating_constant_; + uint32_t chance_of_outlining_function_; uint32_t chance_of_replacing_id_with_synonym_; uint32_t chance_of_splitting_block_;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp index 9eb5631..ff0adab 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -148,7 +148,6 @@ transformation.Apply(GetIRContext(), GetFactManager()); *GetTransformations()->add_transformation() = transformation.ToMessage(); - // Indicate that one instruction was added. }); }
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp new file mode 100644 index 0000000..ca1bfb3 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -0,0 +1,65 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_merge_blocks.h" + +#include <vector> + +#include "source/fuzz/transformation_merge_blocks.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default; + +void FuzzerPassMergeBlocks::Apply() { + // First we populate a sequence of transformations that we might consider + // applying. + std::vector<TransformationMergeBlocks> potential_transformations; + // We do this by considering every block of every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We probabilistically decide to ignore some blocks. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMergingBlocks())) { + continue; + } + // For other blocks, we add a transformation to merge the block into its + // predecessor if that transformation would be applicable. + TransformationMergeBlocks transformation(block.id()); + if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { + potential_transformations.push_back(transformation); + } + } + } + + while (!potential_transformations.empty()) { + uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations); + auto transformation = potential_transformations.at(index); + potential_transformations.erase(potential_transformations.begin() + index); + if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = transformation.ToMessage(); + } + } +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h new file mode 100644 index 0000000..457e591 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.h
@@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for merging blocks in the module. +class FuzzerPassMergeBlocks : public FuzzerPass { + public: + FuzzerPassMergeBlocks(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassMergeBlocks(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp new file mode 100644 index 0000000..d59c195 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -0,0 +1,99 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_outline_functions.h" + +#include <vector> + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_outline_function.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default; + +void FuzzerPassOutlineFunctions::Apply() { + std::vector<opt::Function*> original_functions; + for (auto& function : *GetIRContext()->module()) { + original_functions.push_back(&function); + } + for (auto& function : original_functions) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfOutliningFunction())) { + continue; + } + std::vector<opt::BasicBlock*> blocks; + for (auto& block : *function) { + blocks.push_back(&block); + } + auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)]; + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function); + auto postdominator_analysis = + GetIRContext()->GetPostDominatorAnalysis(function); + std::vector<opt::BasicBlock*> candidate_exit_blocks; + for (auto postdominates_entry_block = entry_block; + postdominates_entry_block != nullptr; + postdominates_entry_block = postdominator_analysis->ImmediateDominator( + postdominates_entry_block)) { + if (dominator_analysis->Dominates(entry_block, + postdominates_entry_block)) { + candidate_exit_blocks.push_back(postdominates_entry_block); + } + } + if (candidate_exit_blocks.empty()) { + continue; + } + auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex( + candidate_exit_blocks)]; + + auto region_blocks = TransformationOutlineFunction::GetRegionBlocks( + GetIRContext(), entry_block, exit_block); + std::map<uint32_t, uint32_t> input_id_to_fresh_id; + for (auto id : TransformationOutlineFunction::GetRegionInputIds( + GetIRContext(), region_blocks, exit_block)) { + input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId(); + } + std::map<uint32_t, uint32_t> output_id_to_fresh_id; + for (auto id : TransformationOutlineFunction::GetRegionOutputIds( + GetIRContext(), region_blocks, exit_block)) { + output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId(); + } + TransformationOutlineFunction transformation( + entry_block->id(), exit_block->id(), + /*new_function_struct_return_type_id*/ + GetFuzzerContext()->GetFreshId(), + /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(), + /*new_function_id*/ GetFuzzerContext()->GetFreshId(), + /*new_function_region_entry_block*/ + GetFuzzerContext()->GetFreshId(), + /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(), + /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(), + /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id), + /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id)); + if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) { + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = transformation.ToMessage(); + } + } +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h new file mode 100644 index 0000000..5448e7d --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.h
@@ -0,0 +1,40 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for outlining single-entry single-exit regions of a control +// flow graph into their own functions. +class FuzzerPassOutlineFunctions : public FuzzerPass { + public: + FuzzerPassOutlineFunctions( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassOutlineFunctions(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp index 4654682..1c39da0 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -14,6 +14,8 @@ #include "source/fuzz/fuzzer_util.h" +#include "source/opt/build_module.h" + namespace spvtools { namespace fuzz { @@ -180,108 +182,6 @@ return block->end(); } -bool NewEdgeRespectsUseDefDominance(opt::IRContext* context, - opt::BasicBlock* bb_from, - opt::BasicBlock* bb_to) { - assert(bb_from->terminator()->opcode() == SpvOpBranch); - - // If there is *already* an edge from |bb_from| to |bb_to|, then adding - // another edge is fine from a dominance point of view. - if (bb_from->terminator()->GetSingleWordInOperand(0) == bb_to->id()) { - return true; - } - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the - // solution below to determining whether a new edge respects dominance - // rules is incomplete. Test - // TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the - // problem. In practice, this limitation does not bite too often, and the - // worst it does is leads to SPIR-V that spirv-val rejects. - - // Let us assume that the module being manipulated is valid according to the - // rules of the SPIR-V language. - // - // Suppose that some block Y is dominated by |bb_to| (which includes the case - // where Y = |bb_to|). - // - // Suppose that Y uses an id i that is defined in some other block X. - // - // Because the module is valid, X must dominate Y. We are concerned about - // whether an edge from |bb_from| to |bb_to| could *stop* X from dominating - // Y. - // - // Because |bb_to| dominates Y, a new edge from |bb_from| to |bb_to| can - // only affect whether X dominates Y if X dominates |bb_to|. - // - // So let us assume that X does dominate |bb_to|, so that we have: - // - // (X defines i) dominates |bb_to| dominates (Y uses i) - // - // The new edge from |bb_from| to |bb_to| will stop the definition of i in X - // from dominating the use of i in Y exactly when the new edge will stop X - // from dominating |bb_to|. - // - // Now, the block X that we are worried about cannot dominate |bb_from|, - // because in that case X would still dominate |bb_to| after we add an edge - // from |bb_from| to |bb_to|. - // - // Also, it cannot be that X = |bb_to|, because nothing can stop a block - // from dominating itself. - // - // So we are looking for a block X such that: - // - // - X strictly dominates |bb_to| - // - X does not dominate |bb_from| - // - X defines an id i - // - i is used in some block Y - // - |bb_to| dominates Y - - // Walk the dominator tree backwards, starting from the immediate dominator - // of |bb_to|. We can stop when we find a block that also dominates - // |bb_from|. - auto dominator_analysis = context->GetDominatorAnalysis(bb_from->GetParent()); - for (auto dominator = dominator_analysis->ImmediateDominator(bb_to); - dominator != nullptr && - !dominator_analysis->Dominates(dominator, bb_from); - dominator = dominator_analysis->ImmediateDominator(dominator)) { - // |dominator| is a candidate for block X in the above description. - // We now look through the instructions for a candidate instruction i. - for (auto& inst : *dominator) { - // Consider all the uses of this instruction. - if (!context->get_def_use_mgr()->WhileEachUse( - &inst, - [bb_to, context, dominator_analysis]( - opt::Instruction* user, uint32_t operand_index) -> bool { - // If this use is in an OpPhi, we need to check that dominance - // of the relevant *parent* block is not spoiled. Otherwise we - // need to check that dominance of the block containing the use - // is not spoiled. - opt::BasicBlock* use_block_or_phi_parent = - user->opcode() == SpvOpPhi - ? context->cfg()->block( - user->GetSingleWordOperand(operand_index + 1)) - : context->get_instr_block(user); - - // There might not be any relevant block, e.g. if the use is in - // a decoration; in this case the new edge is unproblematic. - if (use_block_or_phi_parent == nullptr) { - return true; - } - - // With reference to the above discussion, - // |use_block_or_phi_parent| is a candidate for the block Y. - // If |bb_to| dominates this block, the new edge would be - // problematic. - return !dominator_analysis->Dominates(bb_to, - use_block_or_phi_parent); - })) { - return false; - } - } - } - return true; -} - bool BlockIsReachableInItsFunction(opt::IRContext* context, opt::BasicBlock* bb) { auto enclosing_function = bb->GetParent(); @@ -399,6 +299,24 @@ return array_length_constant->GetU32(); } +bool IsValid(opt::IRContext* context) { + std::vector<uint32_t> binary; + context->module()->ToBinary(&binary, false); + return SpirvTools(context->grammar().target_env()).Validate(binary); +} + +std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) { + std::vector<uint32_t> binary; + context->module()->ToBinary(&binary, false); + return BuildModule(context->grammar().target_env(), nullptr, binary.data(), + binary.size()); +} + +bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) { + auto type = ir_context->get_type_mgr()->GetType(id); + return type && !type->AsFunction(); +} + } // namespace fuzzerutil } // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h index 1569df0..af3eb1b 100644 --- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h +++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -69,14 +69,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction( opt::BasicBlock* block, const opt::Instruction* inst); -// The function determines whether adding an edge from |bb_from| to |bb_to| - -// is legitimate with respect to the SPIR-V rule that a definition must -// dominate all of its uses. This is because adding such an edge can change -// dominance in the control flow graph, potentially making the module invalid. -bool NewEdgeRespectsUseDefDominance(opt::IRContext* context, - opt::BasicBlock* bb_from, - opt::BasicBlock* bb_to); - // Returns true if and only if there is a path to |bb| from the entry block of // the function that contains |bb|. bool BlockIsReachableInItsFunction(opt::IRContext* context, @@ -117,6 +109,17 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction, opt::IRContext* context); +// Returns true if and only if |context| is valid, according to the validator. +bool IsValid(opt::IRContext* context); + +// Returns a clone of |context|, by writing |context| to a binary and then +// parsing it again. +std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context); + +// Returns true if and only if |id| is the id of a type that is not a function +// type. +bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id); + } // namespace fuzzerutil } // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp b/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp new file mode 100644 index 0000000..b217a21 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/instruction_message.cpp
@@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/instruction_message.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +protobufs::Instruction MakeInstructionMessage( + SpvOp opcode, uint32_t result_type_id, uint32_t result_id, + const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>& + input_operands) { + protobufs::Instruction result; + result.set_opcode(opcode); + result.set_result_type_id(result_type_id); + result.set_result_id(result_id); + for (auto& operand : input_operands) { + auto operand_message = result.add_input_operand(); + operand_message->set_operand_type(operand.first); + for (auto operand_word : operand.second) { + operand_message->add_operand_data(operand_word); + } + } + return result; +} + +std::unique_ptr<opt::Instruction> InstructionFromMessage( + opt::IRContext* ir_context, + const protobufs::Instruction& instruction_message) { + // First, update the module's id bound with respect to the new instruction, + // if it has a result id. + if (instruction_message.result_id()) { + fuzzerutil::UpdateModuleIdBound(ir_context, + instruction_message.result_id()); + } + // Now create a sequence of input operands from the input operand data in the + // protobuf message. + opt::Instruction::OperandList in_operands; + for (auto& operand_message : instruction_message.input_operand()) { + opt::Operand::OperandData operand_data; + for (auto& word : operand_message.operand_data()) { + operand_data.push_back(word); + } + in_operands.push_back( + {static_cast<spv_operand_type_t>(operand_message.operand_type()), + operand_data}); + } + // Create and return the instruction. + return MakeUnique<opt::Instruction>( + ir_context, static_cast<SpvOp>(instruction_message.opcode()), + instruction_message.result_type_id(), instruction_message.result_id(), + in_operands); +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/instruction_message.h b/third_party/SPIRV-Tools/source/fuzz/instruction_message.h new file mode 100644 index 0000000..ed339aa --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/instruction_message.h
@@ -0,0 +1,44 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ +#define SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ + +#include <memory> + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Creates an Instruction protobuf message from its component parts. +protobufs::Instruction MakeInstructionMessage( + SpvOp opcode, uint32_t result_type_id, uint32_t result_id, + const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>& + input_operands); + +// Creates and returns an opt::Instruction from protobuf message +// |instruction_message|, relative to |ir_context|. In the process, the module +// id bound associated with |ir_context| is updated to be at least as large as +// the result id (if any) associated with the new instruction. +std::unique_ptr<opt::Instruction> InstructionFromMessage( + opt::IRContext* ir_context, + const protobufs::Instruction& instruction_message); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto index b33c2e5..dbd1fb8 100644 --- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -21,6 +21,16 @@ package spvtools.fuzz.protobufs; +message UInt32Pair { + + // A pair of uint32s; useful for defining mappings. + + uint32 first = 1; + + uint32 second = 2; + +} + message InstructionDescriptor { // Describes an instruction in some block of a function with respect to a @@ -116,6 +126,37 @@ } +message InstructionOperand { + + // Represents an operand to a SPIR-V instruction. + + // The type of the operand. + uint32 operand_type = 1; + + // The data associated with the operand. For most operands (e.g. ids, + // storage classes and literals) this will be a single word. + repeated uint32 operand_data = 2; + +} + +message Instruction { + + // Represents a SPIR-V instruction. + + // The instruction's opcode (e.g. OpLabel). + uint32 opcode = 1; + + // The id of the instruction's result type; 0 if there is no result type. + uint32 result_type_id = 2; + + // The id of the instruction's result; 0 if there is no result. + uint32 result_id = 3; + + // Zero or more input operands. + repeated InstructionOperand input_operand = 4; + +} + message FactSequence { repeated Fact fact = 1; } @@ -190,6 +231,17 @@ TransformationSetMemoryOperandsMask set_memory_operands_mask = 20; TransformationCompositeExtract composite_extract = 21; TransformationVectorShuffle vector_shuffle = 22; + TransformationOutlineFunction outline_function = 23; + TransformationMergeBlocks merge_blocks = 24; + TransformationAddTypeVector add_type_vector = 25; + TransformationAddTypeArray add_type_array = 26; + TransformationAddTypeMatrix add_type_matrix = 27; + TransformationAddTypeStruct add_type_struct = 28; + TransformationAddTypeFunction add_type_function = 29; + TransformationAddConstantComposite add_constant_composite = 30; + TransformationAddGlobalVariable add_global_variable = 31; + TransformationAddGlobalUndef add_global_undef = 32; + TransformationAddFunction add_function = 33; // Add additional option using the next available number. } } @@ -206,6 +258,21 @@ } +message TransformationAddConstantComposite { + + // Adds a constant of the given composite type to the module. + + // Fresh id for the composite + uint32 fresh_id = 1; + + // A composite type id + uint32 type_id = 2; + + // Constituent ids for the composite + repeated uint32 constituent_id = 3; + +} + message TransformationAddConstantScalar { // Adds a constant of the given scalar type @@ -262,6 +329,43 @@ } +message TransformationAddFunction { + + // Adds a SPIR-V function to the module. + + // The series of instructions that comprise the function. + repeated Instruction instruction = 1; + +} + +message TransformationAddGlobalUndef { + + // Adds an undefined value of a given type to the module at global scope. + + // Fresh id for the undefined value + uint32 fresh_id = 1; + + // The type of the undefined value + uint32 type_id = 2; + +} + +message TransformationAddGlobalVariable { + + // Adds a global variable of the given type to the module, with Private + // storage class and optionally with an initializer. + + // Fresh id for the global variable + uint32 fresh_id = 1; + + // The type of the global variable + uint32 type_id = 2; + + // Optional initializer; 0 if there is no initializer + uint32 initializer_id = 3; + +} + message TransformationAddNoContractionDecoration { // Applies OpDecorate NoContraction to the given result id @@ -271,6 +375,21 @@ } +message TransformationAddTypeArray { + + // Adds an array type of the given element type and size to the module + + // Fresh id for the array type + uint32 fresh_id = 1; + + // The array's element type + uint32 element_type_id = 2; + + // The array's size + uint32 size_id = 3; + +} + message TransformationAddTypeBoolean { // Adds OpTypeBool to the module @@ -292,6 +411,21 @@ } +message TransformationAddTypeFunction { + + // Adds a function type to the module + + // Fresh id for the function type + uint32 fresh_id = 1; + + // The function's return type + uint32 return_type_id = 2; + + // The function's argument types + repeated uint32 argument_type_id = 3; + +} + message TransformationAddTypeInt { // Adds OpTypeInt to the module with the given width and signedness @@ -307,6 +441,22 @@ } +message TransformationAddTypeMatrix { + + // Adds a matrix type to the module + + // Fresh id for the matrix type + uint32 fresh_id = 1; + + // The matrix's column type, which must be a floating-point vector (as per + // the "data rules" in the SPIR-V specification). + uint32 column_type_id = 2; + + // The matrix's column count + uint32 column_count = 3; + +} + message TransformationAddTypePointer { // Adds OpTypePointer to the module, with the given storage class and base @@ -323,6 +473,33 @@ } +message TransformationAddTypeStruct { + + // Adds a struct type to the module + + // Fresh id for the struct type + uint32 fresh_id = 1; + + // The struct's member types + repeated uint32 member_type_id = 3; + +} + +message TransformationAddTypeVector { + + // Adds a vector type to the module + + // Fresh id for the vector type + uint32 fresh_id = 1; + + // The vector's component type + uint32 component_type_id = 2; + + // The vector's component count + uint32 component_count = 3; + +} + message TransformationCompositeConstruct { // A transformation that introduces an OpCompositeConstruct instruction to @@ -380,6 +557,16 @@ } +message TransformationMergeBlocks { + + // A transformation that merges a block with its predecessor. + + // The id of the block that is to be merged with its predecessor; the merged + // block will have the *predecessor's* id. + uint32 block_id = 1; + +} + message TransformationMoveBlockDown { // A transformation that moves a basic block to be one position lower in @@ -389,6 +576,53 @@ uint32 block_id = 1; } +message TransformationOutlineFunction { + + // A transformation that outlines a single-entry single-exit region of a + // control flow graph into a separate function, and replaces the region with + // a call to that function. + + // Id of the entry block of the single-entry single-exit region to be outlined + uint32 entry_block = 1; + + // Id of the exit block of the single-entry single-exit region to be outlined + uint32 exit_block = 2; + + // Id of a struct that will store the return values of the new function + uint32 new_function_struct_return_type_id = 3; + + // A fresh id for the type of the outlined function + uint32 new_function_type_id = 4; + + // A fresh id for the outlined function itself + uint32 new_function_id = 5; + + // A fresh id to represent the block in the outlined function that represents + // the first block of the outlined region. + uint32 new_function_region_entry_block = 6; + + // A fresh id for the result of the OpFunctionCall instruction that will call + // the outlined function + uint32 new_caller_result_id = 7; + + // A fresh id to capture the return value of the outlined function - the + // argument to OpReturn + uint32 new_callee_result_id = 8; + + // Ids defined outside the region and used inside the region will become + // parameters to the outlined function. This is a mapping from used ids to + // fresh parameter ids. + repeated UInt32Pair input_id_to_fresh_id = 9; + + // Ids defined inside the region and used outside the region will become + // fresh ids defined by the outlined function, which get copied into the + // function's struct return value and then copied into their destination ids + // by the caller. This is a mapping from original ids to corresponding fresh + // ids. + repeated UInt32Pair output_id_to_fresh_id = 10; + +} + message TransformationReplaceBooleanConstantWithConstantBinary { // A transformation to capture replacing a use of a boolean constant with
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp index d8fc92f..1489f85 100644 --- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -17,18 +17,29 @@ #include <cassert> #include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" #include "source/fuzz/transformation_add_constant_scalar.h" #include "source/fuzz/transformation_add_dead_break.h" #include "source/fuzz/transformation_add_dead_continue.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" #include "source/fuzz/transformation_add_no_contraction_decoration.h" +#include "source/fuzz/transformation_add_type_array.h" #include "source/fuzz/transformation_add_type_boolean.h" #include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" #include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" #include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" #include "source/fuzz/transformation_composite_construct.h" #include "source/fuzz/transformation_composite_extract.h" #include "source/fuzz/transformation_copy_object.h" +#include "source/fuzz/transformation_merge_blocks.h" #include "source/fuzz/transformation_move_block_down.h" +#include "source/fuzz/transformation_outline_function.h" #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" #include "source/fuzz/transformation_replace_constant_with_uniform.h" #include "source/fuzz/transformation_replace_id_with_synonym.h" @@ -51,6 +62,9 @@ case protobufs::Transformation::TransformationCase::kAddConstantBoolean: return MakeUnique<TransformationAddConstantBoolean>( message.add_constant_boolean()); + case protobufs::Transformation::TransformationCase::kAddConstantComposite: + return MakeUnique<TransformationAddConstantComposite>( + message.add_constant_composite()); case protobufs::Transformation::TransformationCase::kAddConstantScalar: return MakeUnique<TransformationAddConstantScalar>( message.add_constant_scalar()); @@ -59,20 +73,39 @@ case protobufs::Transformation::TransformationCase::kAddDeadContinue: return MakeUnique<TransformationAddDeadContinue>( message.add_dead_continue()); + case protobufs::Transformation::TransformationCase::kAddFunction: + return MakeUnique<TransformationAddFunction>(message.add_function()); + case protobufs::Transformation::TransformationCase::kAddGlobalUndef: + return MakeUnique<TransformationAddGlobalUndef>( + message.add_global_undef()); + case protobufs::Transformation::TransformationCase::kAddGlobalVariable: + return MakeUnique<TransformationAddGlobalVariable>( + message.add_global_variable()); case protobufs::Transformation::TransformationCase:: kAddNoContractionDecoration: return MakeUnique<TransformationAddNoContractionDecoration>( message.add_no_contraction_decoration()); + case protobufs::Transformation::TransformationCase::kAddTypeArray: + return MakeUnique<TransformationAddTypeArray>(message.add_type_array()); case protobufs::Transformation::TransformationCase::kAddTypeBoolean: return MakeUnique<TransformationAddTypeBoolean>( message.add_type_boolean()); case protobufs::Transformation::TransformationCase::kAddTypeFloat: return MakeUnique<TransformationAddTypeFloat>(message.add_type_float()); + case protobufs::Transformation::TransformationCase::kAddTypeFunction: + return MakeUnique<TransformationAddTypeFunction>( + message.add_type_function()); case protobufs::Transformation::TransformationCase::kAddTypeInt: return MakeUnique<TransformationAddTypeInt>(message.add_type_int()); + case protobufs::Transformation::TransformationCase::kAddTypeMatrix: + return MakeUnique<TransformationAddTypeMatrix>(message.add_type_matrix()); case protobufs::Transformation::TransformationCase::kAddTypePointer: return MakeUnique<TransformationAddTypePointer>( message.add_type_pointer()); + case protobufs::Transformation::TransformationCase::kAddTypeStruct: + return MakeUnique<TransformationAddTypeStruct>(message.add_type_struct()); + case protobufs::Transformation::TransformationCase::kAddTypeVector: + return MakeUnique<TransformationAddTypeVector>(message.add_type_vector()); case protobufs::Transformation::TransformationCase::kCompositeConstruct: return MakeUnique<TransformationCompositeConstruct>( message.composite_construct()); @@ -81,8 +114,13 @@ message.composite_extract()); case protobufs::Transformation::TransformationCase::kCopyObject: return MakeUnique<TransformationCopyObject>(message.copy_object()); + case protobufs::Transformation::TransformationCase::kMergeBlocks: + return MakeUnique<TransformationMergeBlocks>(message.merge_blocks()); case protobufs::Transformation::TransformationCase::kMoveBlockDown: return MakeUnique<TransformationMoveBlockDown>(message.move_block_down()); + case protobufs::Transformation::TransformationCase::kOutlineFunction: + return MakeUnique<TransformationOutlineFunction>( + message.outline_function()); case protobufs::Transformation::TransformationCase:: kReplaceBooleanConstantWithConstantBinary: return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp new file mode 100644 index 0000000..7ba1ea4 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
@@ -0,0 +1,130 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_composite.h" + +#include <vector> + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantComposite::TransformationAddConstantComposite( + const spvtools::fuzz::protobufs::TransformationAddConstantComposite& + message) + : message_(message) {} + +TransformationAddConstantComposite::TransformationAddConstantComposite( + uint32_t fresh_id, uint32_t type_id, + const std::vector<uint32_t>& constituent_ids) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + for (auto constituent_id : constituent_ids) { + message_.add_constituent_id(constituent_id); + } +} + +bool TransformationAddConstantComposite::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // Check that the given id is fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // Check that the composite type id is an instruction id. + auto composite_type_instruction = + context->get_def_use_mgr()->GetDef(message_.type_id()); + if (!composite_type_instruction) { + return false; + } + // Gather up the operands for the composite constant, in the process checking + // whether the given type really defines a composite. + std::vector<uint32_t> constituent_type_ids; + switch (composite_type_instruction->opcode()) { + case SpvOpTypeArray: + for (uint32_t index = 0; + index < + fuzzerutil::GetArraySize(*composite_type_instruction, context); + index++) { + constituent_type_ids.push_back( + composite_type_instruction->GetSingleWordInOperand(0)); + } + break; + case SpvOpTypeMatrix: + case SpvOpTypeVector: + for (uint32_t index = 0; + index < composite_type_instruction->GetSingleWordInOperand(1); + index++) { + constituent_type_ids.push_back( + composite_type_instruction->GetSingleWordInOperand(0)); + } + break; + case SpvOpTypeStruct: + composite_type_instruction->ForEachInOperand( + [&constituent_type_ids](const uint32_t* member_type_id) { + constituent_type_ids.push_back(*member_type_id); + }); + break; + default: + // Not a composite type. + return false; + } + + // Check that the number of provided operands matches the number of + // constituents required by the type. + if (constituent_type_ids.size() != + static_cast<uint32_t>(message_.constituent_id().size())) { + return false; + } + + // Check that every provided operand refers to an instruction of the + // corresponding constituent type. + for (uint32_t index = 0; index < constituent_type_ids.size(); index++) { + auto constituent_instruction = + context->get_def_use_mgr()->GetDef(message_.constituent_id(index)); + if (!constituent_instruction) { + return false; + } + if (constituent_instruction->type_id() != constituent_type_ids.at(index)) { + return false; + } + } + return true; +} + +void TransformationAddConstantComposite::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::Instruction::OperandList in_operands; + for (auto constituent_id : message_.constituent_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}}); + } + context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( + context, SpvOpConstantComposite, message_.type_id(), message_.fresh_id(), + in_operands)); + 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); +} + +protobufs::Transformation TransformationAddConstantComposite::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_constant_composite() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h new file mode 100644 index 0000000..9a824a0 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
@@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ + +#include <vector> + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantComposite : public Transformation { + public: + explicit TransformationAddConstantComposite( + const protobufs::TransformationAddConstantComposite& message); + + TransformationAddConstantComposite( + uint32_t fresh_id, uint32_t type_id, + const std::vector<uint32_t>& constituent_ids); + + // - |message_.fresh_id| must be a fresh id + // - |message_.type_id| must be the id of a composite type + // - |message_.constituent_id| must refer to ids that match the constituent + // types of this composite type + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpConstantComposite instruction defining a constant of type + // |message_.type_id|, using |message_.constituent_id| as constituents, with + // result id |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantComposite message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp index b244cf4..a37100b 100644 --- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
@@ -175,18 +175,23 @@ return false; } - // Check that adding the break would not violate the property that a - // definition must dominate all of its uses. - return fuzzerutil::NewEdgeRespectsUseDefDominance(context, bb_from, bb_to); + // 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(context); + ApplyImpl(cloned_context.get()); + return fuzzerutil::IsValid(cloned_context.get()); } void TransformationAddDeadBreak::Apply(opt::IRContext* context, FactManager* /*unused*/) const { - fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( - context, context->cfg()->block(message_.from_block()), - context->cfg()->block(message_.to_block()), - message_.break_condition_value(), message_.phi_id()); - + ApplyImpl(context); // Invalidate all analyses context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); } @@ -197,5 +202,13 @@ return result; } +void TransformationAddDeadBreak::ApplyImpl( + spvtools::opt::IRContext* context) const { + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + context, context->cfg()->block(message_.from_block()), + context->cfg()->block(message_.to_block()), + message_.break_condition_value(), message_.phi_id()); +} + } // namespace fuzz } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h index 10d2cec..81a2c99 100644 --- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
@@ -67,6 +67,14 @@ bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* 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* context) const; + protobufs::TransformationAddDeadBreak message_; };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp index e644b88..0aacc5b 100644 --- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
@@ -106,34 +106,30 @@ return false; } - // Check that adding the continue would not violate the property that a - // definition must dominate all of its uses. - if (!fuzzerutil::NewEdgeRespectsUseDefDominance( - context, bb_from, context->cfg()->block(continue_block))) { + // Check whether the data passed to extend OpPhi instructions is appropriate. + if (!fuzzerutil::PhiIdsOkForNewEdge(context, bb_from, + context->cfg()->block(continue_block), + message_.phi_id())) { return false; } - // The transformation is good if and only if the given phi ids are sufficient - // to extend relevant OpPhi instructions in the continue block. - return fuzzerutil::PhiIdsOkForNewEdge(context, bb_from, - context->cfg()->block(continue_block), - message_.phi_id()); + // 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(context); + ApplyImpl(cloned_context.get()); + return fuzzerutil::IsValid(cloned_context.get()); } void TransformationAddDeadContinue::Apply(opt::IRContext* context, FactManager* /*unused*/) const { - auto bb_from = context->cfg()->block(message_.from_block()); - auto continue_block = - bb_from->IsLoopHeader() - ? bb_from->ContinueBlockId() - : context->GetStructuredCFGAnalysis()->LoopContinueBlock( - message_.from_block()); - assert(continue_block && - "Precondition for this transformation requires that " - "message_.from_block must be in a loop."); - fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( - context, bb_from, context->cfg()->block(continue_block), - message_.continue_condition_value(), message_.phi_id()); + ApplyImpl(context); // Invalidate all analyses context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); } @@ -144,5 +140,19 @@ return result; } +void TransformationAddDeadContinue::ApplyImpl( + spvtools::opt::IRContext* context) const { + auto bb_from = context->cfg()->block(message_.from_block()); + auto continue_block = + bb_from->IsLoopHeader() + ? bb_from->ContinueBlockId() + : context->GetStructuredCFGAnalysis()->LoopContinueBlock( + message_.from_block()); + assert(continue_block && "message_.from_block must be in a loop."); + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + context, bb_from, context->cfg()->block(continue_block), + message_.continue_condition_value(), message_.phi_id()); +} + } // namespace fuzz } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h index df6bb4c..86b4c93 100644 --- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
@@ -64,6 +64,14 @@ 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* context) const; + protobufs::TransformationAddDeadContinue message_; };
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp new file mode 100644 index 0000000..5e53961 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
@@ -0,0 +1,156 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_message.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddFunction::TransformationAddFunction( + const spvtools::fuzz::protobufs::TransformationAddFunction& message) + : message_(message) {} + +TransformationAddFunction::TransformationAddFunction( + const std::vector<protobufs::Instruction>& instructions) { + for (auto& instruction : instructions) { + *message_.add_instruction() = instruction; + } +} + +bool TransformationAddFunction::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // Because checking all the conditions for a function to be valid is a big + // job that the SPIR-V validator can already do, a "try it and see" approach + // is taken here. + + // We first clone the current module, so that we can try adding the new + // function without risking wrecking |context|. + auto cloned_module = fuzzerutil::CloneIRContext(context); + + // We try to add a function to the cloned module, which may fail if + // |message_.instruction| is not sufficiently well-formed. + if (!TryToAddFunction(cloned_module.get())) { + return false; + } + // Having managed to add the new function to the cloned module, we ascertain + // whether the cloned module is still valid. If it is, the transformation is + // applicable. + return fuzzerutil::IsValid(cloned_module.get()); +} + +void TransformationAddFunction::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + auto success = TryToAddFunction(context); + assert(success && "The function should be successfully added."); + (void)(success); // Keep release builds happy (otherwise they may complain + // that |success| is not used). + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationAddFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_function() = message_; + return result; +} + +bool TransformationAddFunction::TryToAddFunction( + opt::IRContext* context) const { + // This function returns false if |message_.instruction| was not well-formed + // enough to actually create a function and add it to |context|. + + // A function must have at least some instructions. + if (message_.instruction().empty()) { + return false; + } + + // A function must start with OpFunction. + auto function_begin = message_.instruction(0); + if (function_begin.opcode() != SpvOpFunction) { + return false; + } + + // Make a function, headed by the OpFunction instruction. + std::unique_ptr<opt::Function> new_function = MakeUnique<opt::Function>( + InstructionFromMessage(context, function_begin)); + + // Keeps track of which instruction protobuf message we are currently + // considering. + uint32_t instruction_index = 1; + const auto num_instructions = + static_cast<uint32_t>(message_.instruction().size()); + + // Iterate through all function parameter instructions, adding parameters to + // the new function. + while (instruction_index < num_instructions && + message_.instruction(instruction_index).opcode() == + SpvOpFunctionParameter) { + new_function->AddParameter(InstructionFromMessage( + context, message_.instruction(instruction_index))); + instruction_index++; + } + + // After the parameters, there needs to be a label. + if (instruction_index == num_instructions || + message_.instruction(instruction_index).opcode() != SpvOpLabel) { + return false; + } + + // Iterate through the instructions block by block until the end of the + // function is reached. + while (instruction_index < num_instructions && + message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) { + // Invariant: we should always be at a label instruction at this point. + assert(message_.instruction(instruction_index).opcode() == SpvOpLabel); + + // Make a basic block using the label instruction, with the new function + // as its parent. + std::unique_ptr<opt::BasicBlock> block = + MakeUnique<opt::BasicBlock>(InstructionFromMessage( + context, message_.instruction(instruction_index))); + block->SetParent(new_function.get()); + + // Consider successive instructions until we hit another label or the end + // of the function, adding each such instruction to the block. + instruction_index++; + while (instruction_index < num_instructions && + message_.instruction(instruction_index).opcode() != + SpvOpFunctionEnd && + message_.instruction(instruction_index).opcode() != SpvOpLabel) { + block->AddInstruction(InstructionFromMessage( + context, message_.instruction(instruction_index))); + instruction_index++; + } + // Add the block to the new function. + new_function->AddBasicBlock(std::move(block)); + } + // Having considered all the blocks, we should be at the last instruction and + // it needs to be OpFunctionEnd. + if (instruction_index != num_instructions - 1 || + message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) { + return false; + } + // Set the function's final instruction, add the function to the module and + // report success. + new_function->SetFunctionEnd( + InstructionFromMessage(context, message_.instruction(instruction_index))); + context->AddFunction(std::move(new_function)); + return true; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h new file mode 100644 index 0000000..fee2732 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
@@ -0,0 +1,70 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddFunction : public Transformation { + public: + explicit TransformationAddFunction( + const protobufs::TransformationAddFunction& message); + + explicit TransformationAddFunction( + const std::vector<protobufs::Instruction>& instructions); + + // - |message_.instruction| must correspond to a sufficiently well-formed + // sequence of instructions that a function can be created from them + // - Adding the created function to the module must lead to a valid module. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds the function defined by |message_.instruction| to the module + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Attempts to create a function from the series of instructions in + // |message_.instruction| and add it to |context|. Returns false if this is + // not possible due to the messages not respecting the basic structure of a + // function, e.g. if there is no OpFunction instruction or no blocks; in this + // case |context| is left in an indeterminate state. + // + // Otherwise returns true. Whether |context| is valid after addition of the + // function depends on the contents of |message_.instruction|. + // + // Intended usage: + // - Perform a dry run of this method on a clone of a module, and use + // the validator to check whether the resulting module is valid. Working + // on a clone means it does not matter if the function fails to be cleanly + // added, or leads to an invalid module. + // - If the dry run succeeds, run the method on the real module of interest, + // to add the function. + bool TryToAddFunction(opt::IRContext* context) const; + + protobufs::TransformationAddFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp new file mode 100644 index 0000000..f9585b3 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.cpp
@@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_undef.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddGlobalUndef::TransformationAddGlobalUndef( + const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message) + : message_(message) {} + +TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id, + uint32_t type_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); +} + +bool TransformationAddGlobalUndef::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + auto type = context->get_type_mgr()->GetType(message_.type_id()); + // The type must exist, and must not be a function type. + return type && !type->AsFunction(); +} + +void TransformationAddGlobalUndef::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( + context, SpvOpUndef, 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); +} + +protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_global_undef() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h new file mode 100644 index 0000000..550d9f6 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_undef.h
@@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddGlobalUndef : public Transformation { + public: + explicit TransformationAddGlobalUndef( + const protobufs::TransformationAddGlobalUndef& message); + + TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a non-function type + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpUndef instruction to the module, with |message_.type_id| as its + // type. The instruction has result id |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddGlobalUndef message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp new file mode 100644 index 0000000..cea268c --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
@@ -0,0 +1,131 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_variable.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddGlobalVariable::TransformationAddGlobalVariable( + const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message) + : message_(message) {} + +TransformationAddGlobalVariable::TransformationAddGlobalVariable( + uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_initializer_id(initializer_id); +} + +bool TransformationAddGlobalVariable::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // The type id must correspond to a type. + auto type = context->get_type_mgr()->GetType(message_.type_id()); + if (!type) { + return false; + } + // That type must be a pointer type ... + auto pointer_type = type->AsPointer(); + if (!pointer_type) { + return false; + } + // ... with Private storage class. + if (pointer_type->storage_class() != SpvStorageClassPrivate) { + return false; + } + if (message_.initializer_id()) { + // The initializer id must be the id of a constant. Check this with the + // constant manager. + auto constant_id = context->get_constant_mgr()->GetConstantsFromIds( + {message_.initializer_id()}); + if (constant_id.empty()) { + return false; + } + assert(constant_id.size() == 1 && + "We asked for the constant associated with a single id; we should " + "get a single constant."); + // The type of the constant must match the pointee type of the pointer. + if (pointer_type->pointee_type() != constant_id[0]->type()) { + return false; + } + } + return true; +} + +void TransformationAddGlobalVariable::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::Instruction::OperandList input_operands; + input_operands.push_back( + {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}); + if (message_.initializer_id()) { + input_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}); + } + context->module()->AddGlobalValue( + MakeUnique<opt::Instruction>(context, SpvOpVariable, message_.type_id(), + message_.fresh_id(), input_operands)); + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + + if (PrivateGlobalsMustBeDeclaredInEntryPointInterfaces(context)) { + // Conservatively add this global to the interface of every entry point in + // the module. This means that the global is available for other + // transformations to use. + // + // A downside of this is that the global will be in the interface even if it + // ends up never being used. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit + // this if a more thorough approach to entry point interfaces is taken. + for (auto& entry_point : context->module()->entry_points()) { + entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {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); +} + +protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_global_variable() = message_; + return result; +} + +bool TransformationAddGlobalVariable:: + PrivateGlobalsMustBeDeclaredInEntryPointInterfaces( + opt::IRContext* context) { + // TODO(afd): We capture the universal environments for which this requirement + // holds. The check should be refined on demand for other target + // environments. + switch (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: + return false; + default: + return true; + } +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h new file mode 100644 index 0000000..ca63e68 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
@@ -0,0 +1,60 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddGlobalVariable : public Transformation { + public: + explicit TransformationAddGlobalVariable( + const protobufs::TransformationAddGlobalVariable& message); + + TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, + uint32_t initializer_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a pointer type with Private storage + // class + // - |message_.initializer_id| must either be 0 or the id of a constant whose + // type is the pointee type of |message_.type_id| + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds a global variable with Private storage class to the module, with type + // |message_.type_id| and either no initializer or |message_.initializer_id| + // as an initializer, depending on whether |message_.initializer_id| is 0. + // The global variable has result id |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + static bool PrivateGlobalsMustBeDeclaredInEntryPointInterfaces( + opt::IRContext* context); + + protobufs::TransformationAddGlobalVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp new file mode 100644 index 0000000..2074e98 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.cpp
@@ -0,0 +1,88 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_array.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeArray::TransformationAddTypeArray( + const spvtools::fuzz::protobufs::TransformationAddTypeArray& message) + : message_(message) {} + +TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id, + uint32_t element_type_id, + uint32_t size_id) { + message_.set_fresh_id(fresh_id); + message_.set_element_type_id(element_type_id); + message_.set_size_id(size_id); +} + +bool TransformationAddTypeArray::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + auto element_type = + 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. + return false; + } + auto constant = + context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()}); + if (constant.empty()) { + // The size id does not refer to a constant. + return false; + } + assert(constant.size() == 1 && + "Only one constant id was provided, so only one constant should have " + "been returned"); + + auto int_constant = constant[0]->AsIntConstant(); + if (!int_constant) { + // The size constant is not an integer. + return false; + } + // We require that the size constant be a 32-bit value that is positive when + // interpreted as being signed. + return int_constant->words().size() == 1 && int_constant->GetS32() >= 1; +} + +void TransformationAddTypeArray::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + 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()}}); + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands)); + 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); +} + +protobufs::Transformation TransformationAddTypeArray::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_array() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h new file mode 100644 index 0000000..b6e0718 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_array.h
@@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeArray : public Transformation { + public: + explicit TransformationAddTypeArray( + const 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_.size_id| must be the id of a 32-bit integer constant that is + // positive when interpreted as signed. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpTypeArray instruction to the module, with element type given by + // |message_.element_type_id| and size given by |message_.size_id|. The + // result id of the instruction is |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeArray message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp new file mode 100644 index 0000000..4b6717b --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
@@ -0,0 +1,113 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_function.h" + +#include <vector> + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeFunction::TransformationAddTypeFunction( + const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message) + : message_(message) {} + +TransformationAddTypeFunction::TransformationAddTypeFunction( + uint32_t fresh_id, uint32_t return_type_id, + const std::vector<uint32_t>& argument_type_ids) { + message_.set_fresh_id(fresh_id); + message_.set_return_type_id(return_type_id); + for (auto id : argument_type_ids) { + message_.add_argument_type_id(id); + } +} + +bool TransformationAddTypeFunction::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // The return and argument types must be type ids but not not be function + // type ids. + if (!fuzzerutil::IsNonFunctionTypeId(context, message_.return_type_id())) { + return false; + } + for (auto argument_type_id : message_.argument_type_id()) { + if (!fuzzerutil::IsNonFunctionTypeId(context, argument_type_id)) { + return false; + } + } + // Check whether there is already an OpTypeFunction definition that uses + // exactly the same return and argument type ids. (Note that the type manager + // does not allow us to check this, as it does not distinguish between + // function types with different but isomorphic pointer argument types.) + for (auto& inst : context->module()->types_values()) { + if (inst.opcode() != SpvOpTypeFunction) { + // Consider only OpTypeFunction instructions. + continue; + } + if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) { + // Different return types - cannot be the same. + continue; + } + if (inst.NumInOperands() != + 1 + static_cast<uint32_t>(message_.argument_type_id().size())) { + // Different numbers of arguments - cannot be the same. + continue; + } + bool found_argument_mismatch = false; + for (uint32_t index = 1; index < inst.NumInOperands(); index++) { + if (message_.argument_type_id(index - 1) != + inst.GetSingleWordInOperand(index)) { + // Argument mismatch - cannot be the same. + found_argument_mismatch = true; + break; + } + } + if (found_argument_mismatch) { + continue; + } + // Everything matches - the type is already declared. + return false; + } + return true; +} + +void TransformationAddTypeFunction::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}}); + for (auto argument_type_id : message_.argument_type_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}}); + } + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands)); + 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); +} + +protobufs::Transformation TransformationAddTypeFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_function() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h new file mode 100644 index 0000000..2b59661 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
@@ -0,0 +1,59 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ + +#include <vector> + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeFunction : public Transformation { + public: + explicit TransformationAddTypeFunction( + const protobufs::TransformationAddTypeFunction& message); + + TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id, + const std::vector<uint32_t>& argument_type_ids); + + // - |message_.fresh_id| must not be used by the module + // - |message_.return_type_id| and each element of |message_.argument_type_id| + // must be the ids of non-function types + // - The module must not contain an OpTypeFunction instruction defining a + // function type with the signature provided by teh given return and + // argument types + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpTypeFunction instruction to the module, with signature given by + // |message_.return_type_id| and |message_.argument_type_id|. The result id + // for the instruction is |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp new file mode 100644 index 0000000..07ab705 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.cpp
@@ -0,0 +1,71 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_matrix.h" + +#include "fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeMatrix::TransformationAddTypeMatrix( + const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message) + : message_(message) {} + +TransformationAddTypeMatrix::TransformationAddTypeMatrix( + uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) { + message_.set_fresh_id(fresh_id); + message_.set_column_type_id(column_type_id); + message_.set_column_count(column_count); +} + +bool TransformationAddTypeMatrix::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + // The column type must be a floating-point vector. + auto column_type = + context->get_type_mgr()->GetType(message_.column_type_id()); + if (!column_type) { + return false; + } + return column_type->AsVector() && + column_type->AsVector()->element_type()->AsFloat(); +} + +void TransformationAddTypeMatrix::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}}); + in_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}}); + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands)); + 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); +} + +protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_matrix() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h new file mode 100644 index 0000000..ee3caf7 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_matrix.h
@@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeMatrix : public Transformation { + public: + explicit TransformationAddTypeMatrix( + const protobufs::TransformationAddTypeMatrix& message); + + TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t base_type_id, + uint32_t size); + + // - |message_.fresh_id| must be a fresh id + // - |message_.column_type_id| must be the id of a floating-point vector type + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpTypeMatrix instruction to the module, with column type + // |message_.column_type_id| and |message_.column_count| columns, with result + // id |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeMatrix message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp new file mode 100644 index 0000000..1ae8372 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
@@ -0,0 +1,73 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_struct.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeStruct::TransformationAddTypeStruct( + const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message) + : message_(message) {} + +TransformationAddTypeStruct::TransformationAddTypeStruct( + uint32_t fresh_id, const std::vector<uint32_t>& member_type_ids) { + message_.set_fresh_id(fresh_id); + for (auto member_type_id : member_type_ids) { + message_.add_member_type_id(member_type_id); + } +} + +bool TransformationAddTypeStruct::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + for (auto member_type : message_.member_type_id()) { + auto type = 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. + return false; + } + } + return true; +} + +void TransformationAddTypeStruct::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::Instruction::OperandList in_operands; + for (auto member_type : message_.member_type_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}}); + } + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands)); + 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); +} + +protobufs::Transformation TransformationAddTypeStruct::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_struct() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h new file mode 100644 index 0000000..edf3ec6 --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.h
@@ -0,0 +1,54 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ + +#include <vector> + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeStruct : public Transformation { + public: + explicit TransformationAddTypeStruct( + const protobufs::TransformationAddTypeStruct& message); + + TransformationAddTypeStruct(uint32_t fresh_id, + const std::vector<uint32_t>& component_type_ids); + + // - |message_.fresh_id| must be a fresh id + // - |message_.member_type_id| must be a sequence of non-function type ids + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpTypeStruct instruction whose field types are given by + // |message_.member_type_id|, with result id |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeStruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp new file mode 100644 index 0000000..3fdf50b --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
@@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_vector.h" + +#include "fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeVector::TransformationAddTypeVector( + const spvtools::fuzz::protobufs::TransformationAddTypeVector& message) + : message_(message) {} + +TransformationAddTypeVector::TransformationAddTypeVector( + uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) { + message_.set_fresh_id(fresh_id); + message_.set_component_type_id(component_type_id); + message_.set_component_count(component_count); +} + +bool TransformationAddTypeVector::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + auto component_type = + context->get_type_mgr()->GetType(message_.component_type_id()); + if (!component_type) { + return false; + } + return component_type->AsBool() || component_type->AsFloat() || + component_type->AsInteger(); +} + +void TransformationAddTypeVector::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}}); + in_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}); + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands)); + 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); +} + +protobufs::Transformation TransformationAddTypeVector::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_vector() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h new file mode 100644 index 0000000..7b50f6a --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.h
@@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeVector : public Transformation { + public: + explicit TransformationAddTypeVector( + const protobufs::TransformationAddTypeVector& message); + + TransformationAddTypeVector(uint32_t fresh_id, uint32_t base_type_id, + uint32_t size); + + // - |message_.fresh_id| must be a fresh id + // - |message_.component_type_id| must be the id of a scalar type + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds an OpTypeVector instruction to the module, with component type + // |message_.component_type_id| and |message_.component_count| components, + // with result id |message_.fresh_id|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeVector message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp new file mode 100644 index 0000000..316e80d --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.cpp
@@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_blocks.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/block_merge_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationMergeBlocks::TransformationMergeBlocks( + const spvtools::fuzz::protobufs::TransformationMergeBlocks& message) + : message_(message) {} + +TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) { + message_.set_block_id(block_id); +} + +bool TransformationMergeBlocks::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id()); + // The given block must exist. + if (!second_block) { + return false; + } + // The block must have just one predecessor. + auto predecessors = context->cfg()->preds(second_block->id()); + if (predecessors.size() != 1) { + return false; + } + auto first_block = context->cfg()->block(predecessors.at(0)); + + return opt::blockmergeutil::CanMergeWithSuccessor(context, first_block); +} + +void TransformationMergeBlocks::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + auto second_block = fuzzerutil::MaybeFindBlock(context, message_.block_id()); + auto first_block = + context->cfg()->block(context->cfg()->preds(second_block->id()).at(0)); + + auto function = first_block->GetParent(); + // We need an iterator pointing to the predecessor, hence the loop. + for (auto bi = function->begin(); bi != function->end(); ++bi) { + if (bi->id() == first_block->id()) { + assert(opt::blockmergeutil::CanMergeWithSuccessor(context, &*bi) && + "Because 'Apply' should only be invoked if 'IsApplicable' holds, " + "it must be possible to merge |bi| with its successor."); + opt::blockmergeutil::MergeWithSuccessor(context, function, bi); + // Invalidate all analyses, since we have changed the module + // significantly. + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + return; + } + } + assert(false && + "Control should not reach here - we should always find the desired " + "block"); +} + +protobufs::Transformation TransformationMergeBlocks::ToMessage() const { + protobufs::Transformation result; + *result.mutable_merge_blocks() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h new file mode 100644 index 0000000..86216db --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_merge_blocks.h
@@ -0,0 +1,54 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMergeBlocks : public Transformation { + public: + explicit TransformationMergeBlocks( + const protobufs::TransformationMergeBlocks& message); + + TransformationMergeBlocks(uint32_t block_id); + + // - |message_.block_id| must be the id of a block, b + // - b must have a single predecessor, a + // - b must be the sole successor of a + // - Replacing a with the merge of a and b (and removing b) must lead to a + // valid module + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // The contents of b are merged into a, and a's terminator is replaced with + // the terminator of b. Block b is removed from the module. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationMergeBlocks message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp new file mode 100644 index 0000000..1d1d48e --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.cpp
@@ -0,0 +1,938 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_outline_function.h" + +#include <set> + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +std::map<uint32_t, uint32_t> PairSequenceToMap( + const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& + pair_sequence) { + std::map<uint32_t, uint32_t> result; + for (auto& pair : pair_sequence) { + result[pair.first()] = pair.second(); + } + return result; +} + +} // namespace + +TransformationOutlineFunction::TransformationOutlineFunction( + const spvtools::fuzz::protobufs::TransformationOutlineFunction& message) + : message_(message) {} + +TransformationOutlineFunction::TransformationOutlineFunction( + uint32_t entry_block, uint32_t exit_block, + uint32_t new_function_struct_return_type_id, uint32_t new_function_type_id, + uint32_t new_function_id, uint32_t new_function_region_entry_block, + uint32_t new_caller_result_id, uint32_t new_callee_result_id, + std::map<uint32_t, uint32_t>&& input_id_to_fresh_id, + std::map<uint32_t, uint32_t>&& output_id_to_fresh_id) { + message_.set_entry_block(entry_block); + message_.set_exit_block(exit_block); + message_.set_new_function_struct_return_type_id( + new_function_struct_return_type_id); + message_.set_new_function_type_id(new_function_type_id); + message_.set_new_function_id(new_function_id); + message_.set_new_function_region_entry_block(new_function_region_entry_block); + message_.set_new_caller_result_id(new_caller_result_id); + message_.set_new_callee_result_id(new_callee_result_id); + for (auto& entry : input_id_to_fresh_id) { + protobufs::UInt32Pair pair; + pair.set_first(entry.first); + pair.set_second(entry.second); + *message_.add_input_id_to_fresh_id() = pair; + } + for (auto& entry : output_id_to_fresh_id) { + protobufs::UInt32Pair pair; + pair.set_first(entry.first); + pair.set_second(entry.second); + *message_.add_output_id_to_fresh_id() = pair; + } +} + +bool TransformationOutlineFunction::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + std::set<uint32_t> ids_used_by_this_transformation; + + // The various new ids used by the transformation must be fresh and distinct. + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_struct_return_type_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_type_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_region_entry_block(), context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_caller_result_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_callee_result_id(), context, + &ids_used_by_this_transformation)) { + return false; + } + + for (auto& pair : message_.input_id_to_fresh_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), context, &ids_used_by_this_transformation)) { + return false; + } + } + + for (auto& pair : message_.output_id_to_fresh_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), context, &ids_used_by_this_transformation)) { + return false; + } + } + + // The entry and exit block ids must indeed refer to blocks. + for (auto block_id : {message_.entry_block(), message_.exit_block()}) { + auto block_label = context->get_def_use_mgr()->GetDef(block_id); + if (!block_label || block_label->opcode() != SpvOpLabel) { + return false; + } + } + + auto entry_block = context->cfg()->block(message_.entry_block()); + auto exit_block = context->cfg()->block(message_.exit_block()); + + // The entry block cannot start with OpVariable - this would mean that + // outlining would remove a variable from the function containing the region + // being outlined. + if (entry_block->begin()->opcode() == SpvOpVariable) { + return false; + } + + // For simplicity, we do not allow the entry block to be a loop header. + if (entry_block->GetLoopMergeInst()) { + return false; + } + + // For simplicity, we do not allow the exit block to be a merge block or + // continue target. + bool exit_block_is_merge_or_continue = false; + context->get_def_use_mgr()->WhileEachUse( + exit_block->id(), + [&exit_block_is_merge_or_continue]( + const opt::Instruction* use_instruction, + uint32_t /*unused*/) -> bool { + switch (use_instruction->opcode()) { + case SpvOpLoopMerge: + case SpvOpSelectionMerge: + exit_block_is_merge_or_continue = true; + return false; + default: + return true; + } + }); + if (exit_block_is_merge_or_continue) { + return false; + } + + // The entry block cannot start with OpPhi. This is to keep the + // transformation logic simple. (Another transformation to split the OpPhis + // from a block could be applied to avoid this scenario.) + if (entry_block->begin()->opcode() == SpvOpPhi) { + return false; + } + + // The block must be in the same function. + if (entry_block->GetParent() != exit_block->GetParent()) { + return false; + } + + // The entry block must dominate the exit block. + auto dominator_analysis = + context->GetDominatorAnalysis(entry_block->GetParent()); + if (!dominator_analysis->Dominates(entry_block, exit_block)) { + return false; + } + + // The exit block must post-dominate the entry block. + auto postdominator_analysis = + context->GetPostDominatorAnalysis(entry_block->GetParent()); + if (!postdominator_analysis->Dominates(exit_block, entry_block)) { + return false; + } + + // Find all the blocks dominated by |message_.entry_block| and post-dominated + // by |message_.exit_block|. + auto region_set = GetRegionBlocks( + context, entry_block = context->cfg()->block(message_.entry_block()), + exit_block = context->cfg()->block(message_.exit_block())); + + // Check whether |region_set| really is a single-entry single-exit region, and + // also check whether structured control flow constructs and their merge + // and continue constructs are either wholly in or wholly out of the region - + // e.g. avoid the situation where the region contains the head of a loop but + // not the loop's continue construct. + // + // This is achieved by going through every block in the function that contains + // the region. + for (auto& block : *entry_block->GetParent()) { + if (&block == exit_block) { + // It is OK (and typically expected) for the exit block of the region to + // have successors outside the region. It is also OK for the exit block + // to head a structured control flow construct - the block containing the + // call to the outlined function will end up heading this construct if + // outlining takes place. + continue; + } + + if (region_set.count(&block) != 0) { + // The block is in the region and is not the region's exit block. Let's + // see whether all of the block's successors are in the region. If they + // are not, the region is not single-entry single-exit. + bool all_successors_in_region = true; + block.WhileEachSuccessorLabel([&all_successors_in_region, context, + ®ion_set](uint32_t successor) -> bool { + if (region_set.count(context->cfg()->block(successor)) == 0) { + all_successors_in_region = false; + return false; + } + return true; + }); + if (!all_successors_in_region) { + return false; + } + } + + if (auto merge = block.GetMergeInst()) { + // The block is a loop or selection header -- the header and its + // associated merge block had better both be in the region or both be + // outside the region. + auto merge_block = context->cfg()->block(merge->GetSingleWordOperand(0)); + if (region_set.count(&block) != region_set.count(merge_block)) { + return false; + } + } + + if (auto loop_merge = block.GetLoopMergeInst()) { + // Similar to the above, but for the continue target of a loop. + auto continue_target = + context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + if (continue_target != exit_block && + region_set.count(&block) != region_set.count(continue_target)) { + return false; + } + } + } + + // For each region input id, i.e. every id defined outside the region but + // used inside the region, ... + std::map<uint32_t, uint32_t> input_id_to_fresh_id_map = + PairSequenceToMap(message_.input_id_to_fresh_id()); + for (auto id : GetRegionInputIds(context, region_set, exit_block)) { + // There needs to be a corresponding fresh id to be used as a function + // parameter. + if (input_id_to_fresh_id_map.count(id) == 0) { + return false; + } + // Furthermore, no region input id is allowed to be the result of an access + // chain. This is because region input ids will become function parameters, + // and it is not legal to pass an access chain as a function parameter. + if (context->get_def_use_mgr()->GetDef(id)->opcode() == SpvOpAccessChain) { + return false; + } + } + + // For each region output id -- i.e. every id defined inside the region but + // used outside the region -- there needs to be a corresponding fresh id that + // can hold the value for this id computed in the outlined function. + std::map<uint32_t, uint32_t> output_id_to_fresh_id_map = + PairSequenceToMap(message_.output_id_to_fresh_id()); + for (auto id : GetRegionOutputIds(context, region_set, exit_block)) { + if (output_id_to_fresh_id_map.count(id) == 0) { + return false; + } + } + + return true; +} + +void TransformationOutlineFunction::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + // The entry block for the region before outlining. + auto original_region_entry_block = + context->cfg()->block(message_.entry_block()); + + // The exit block for the region before outlining. + auto original_region_exit_block = + context->cfg()->block(message_.exit_block()); + + // The single-entry single-exit region defined by |message_.entry_block| and + // |message_.exit_block|. + std::set<opt::BasicBlock*> region_blocks = GetRegionBlocks( + context, original_region_entry_block, original_region_exit_block); + + // Input and output ids for the region being outlined. + std::vector<uint32_t> region_input_ids = + GetRegionInputIds(context, region_blocks, original_region_exit_block); + std::vector<uint32_t> region_output_ids = + GetRegionOutputIds(context, region_blocks, original_region_exit_block); + + // Maps from input and output ids to fresh ids. + std::map<uint32_t, uint32_t> input_id_to_fresh_id_map = + PairSequenceToMap(message_.input_id_to_fresh_id()); + std::map<uint32_t, uint32_t> output_id_to_fresh_id_map = + PairSequenceToMap(message_.output_id_to_fresh_id()); + + UpdateModuleIdBoundForFreshIds(context, input_id_to_fresh_id_map, + output_id_to_fresh_id_map); + + // Construct a map that associates each output id with its type id. + std::map<uint32_t, uint32_t> output_id_to_type_id; + for (uint32_t output_id : region_output_ids) { + output_id_to_type_id[output_id] = + context->get_def_use_mgr()->GetDef(output_id)->type_id(); + } + + // The region will be collapsed to a single block that calls a function + // containing the outlined region. This block needs to end with whatever + // the exit block of the region ended with before outlining. We thus clone + // the terminator of the region's exit block, and the merge instruction for + // the block if there is one, so that we can append them to the end of the + // collapsed block later. + std::unique_ptr<opt::Instruction> cloned_exit_block_terminator = + std::unique_ptr<opt::Instruction>( + original_region_exit_block->terminator()->Clone(context)); + std::unique_ptr<opt::Instruction> cloned_exit_block_merge = + original_region_exit_block->GetMergeInst() + ? std::unique_ptr<opt::Instruction>( + original_region_exit_block->GetMergeInst()->Clone(context)) + : nullptr; + + // Make a function prototype for the outlined function, which involves + // figuring out its required type. + std::unique_ptr<opt::Function> outlined_function = PrepareFunctionPrototype( + context, region_input_ids, region_output_ids, input_id_to_fresh_id_map); + + // Adapt the region to be outlined so that its input ids are replaced with the + // ids of the outlined function's input parameters, and so that output ids + // are similarly remapped. + RemapInputAndOutputIdsInRegion( + context, *original_region_exit_block, region_blocks, region_input_ids, + region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map); + + // Fill out the body of the outlined function according to the region that is + // being outlined. + PopulateOutlinedFunction(context, *original_region_entry_block, + *original_region_exit_block, region_blocks, + region_output_ids, output_id_to_fresh_id_map, + outlined_function.get()); + + // Collapse the region that has been outlined into a function down to a single + // block that calls said function. + ShrinkOriginalRegion( + context, region_blocks, region_input_ids, region_output_ids, + output_id_to_type_id, outlined_function->type_id(), + std::move(cloned_exit_block_merge), + std::move(cloned_exit_block_terminator), original_region_entry_block); + + // Add the outlined function to the module. + context->module()->AddFunction(std::move(outlined_function)); + + // Major surgery has been conducted on the module, so invalidate all analyses. + context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationOutlineFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_outline_function() = message_; + return result; +} + +bool TransformationOutlineFunction:: + CheckIdIsFreshAndNotUsedByThisTransformation( + uint32_t id, opt::IRContext* context, + std::set<uint32_t>* ids_used_by_this_transformation) const { + if (!fuzzerutil::IsFreshId(context, id)) { + return false; + } + if (ids_used_by_this_transformation->count(id) != 0) { + return false; + } + ids_used_by_this_transformation->insert(id); + return true; +} + +std::vector<uint32_t> TransformationOutlineFunction::GetRegionInputIds( + opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set, + opt::BasicBlock* region_exit_block) { + std::vector<uint32_t> result; + + auto enclosing_function = region_exit_block->GetParent(); + + // Consider each parameter of the function containing the region. + enclosing_function->ForEachParam([context, ®ion_set, &result]( + opt::Instruction* function_parameter) { + // Consider every use of the parameter. + context->get_def_use_mgr()->WhileEachUse( + function_parameter, [context, function_parameter, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) { + // Get the block, if any, in which the parameter is used. + auto use_block = context->get_instr_block(use); + // If the use is in a block that lies within the region, the + // parameter is an input id for the region. + if (use_block && region_set.count(use_block) != 0) { + result.push_back(function_parameter->result_id()); + return false; + } + return true; + }); + }); + + // Consider all definitions in the function that might turn out to be input + // ids. + for (auto& block : *enclosing_function) { + std::vector<opt::Instruction*> candidate_input_ids_for_block; + if (region_set.count(&block) == 0) { + // All instructions in blocks outside the region are candidate's for + // generating input ids. + for (auto& inst : block) { + candidate_input_ids_for_block.push_back(&inst); + } + } else { + // Blocks in the region cannot generate input ids. + continue; + } + + // Consider each candidate input id to check whether it is used in the + // region. + for (auto& inst : candidate_input_ids_for_block) { + context->get_def_use_mgr()->WhileEachUse( + inst, + [context, &inst, region_exit_block, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) -> bool { + + // Find the block in which this id use occurs, recording the id as + // an input id if the block is outside the region, with some + // exceptions detailed below. + auto use_block = context->get_instr_block(use); + + if (!use_block) { + // There might be no containing block, e.g. if the use is in a + // decoration. + return true; + } + + if (region_set.count(use_block) == 0) { + // The use is not in the region: this does not make it an input + // id. + return true; + } + + if (use_block == region_exit_block && use->IsBlockTerminator()) { + // We do not regard uses in the exit block terminator as input + // ids, as this terminator does not get outlined. + return true; + } + + result.push_back(inst->result_id()); + return false; + }); + } + } + return result; +} + +std::vector<uint32_t> TransformationOutlineFunction::GetRegionOutputIds( + opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set, + opt::BasicBlock* region_exit_block) { + std::vector<uint32_t> result; + + // Consider each block in the function containing the region. + for (auto& block : *region_exit_block->GetParent()) { + if (region_set.count(&block) == 0) { + // Skip blocks that are not in the region. + continue; + } + // Consider each use of each instruction defined in the block. + for (auto& inst : block) { + context->get_def_use_mgr()->WhileEachUse( + &inst, + [®ion_set, context, &inst, region_exit_block, &result]( + opt::Instruction* use, uint32_t /*unused*/) -> bool { + + // Find the block in which this id use occurs, recording the id as + // an output id if the block is outside the region, with some + // exceptions detailed below. + auto use_block = context->get_instr_block(use); + + if (!use_block) { + // There might be no containing block, e.g. if the use is in a + // decoration. + return true; + } + + if (region_set.count(use_block) != 0) { + // The use is in the region. + if (use_block != region_exit_block || !use->IsBlockTerminator()) { + // Furthermore, the use is not in the terminator of the region's + // exit block. + return true; + } + } + + result.push_back(inst.result_id()); + return false; + }); + } + } + return result; +} + +std::set<opt::BasicBlock*> TransformationOutlineFunction::GetRegionBlocks( + opt::IRContext* context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block) { + auto enclosing_function = entry_block->GetParent(); + auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + context->GetPostDominatorAnalysis(enclosing_function); + + std::set<opt::BasicBlock*> result; + for (auto& block : *enclosing_function) { + if (dominator_analysis->Dominates(entry_block, &block) && + postdominator_analysis->Dominates(exit_block, &block)) { + result.insert(&block); + } + } + return result; +} + +std::unique_ptr<opt::Function> +TransformationOutlineFunction::PrepareFunctionPrototype( + opt::IRContext* context, const std::vector<uint32_t>& region_input_ids, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const { + uint32_t return_type_id = 0; + uint32_t function_type_id = 0; + + // First, try to find an existing function type that is suitable. This is + // only possible if the region generates no output ids; if it generates output + // ids we are going to make a new struct for those, and since that struct does + // not exist there cannot already be a function type with this struct as its + // return type. + if (region_output_ids.empty()) { + opt::analysis::Void void_type; + return_type_id = context->get_type_mgr()->GetId(&void_type); + std::vector<const opt::analysis::Type*> argument_types; + for (auto id : region_input_ids) { + argument_types.push_back(context->get_type_mgr()->GetType( + context->get_def_use_mgr()->GetDef(id)->type_id())); + } + opt::analysis::Function function_type(&void_type, argument_types); + function_type_id = context->get_type_mgr()->GetId(&function_type); + } + + // If no existing function type was found, we need to create one. + if (function_type_id == 0) { + assert( + ((return_type_id == 0) == !region_output_ids.empty()) && + "We should only have set the return type if there are no output ids."); + // If the region generates output ids, we need to make a struct with one + // field per output id. + if (!region_output_ids.empty()) { + opt::Instruction::OperandList struct_member_types; + for (uint32_t output_id : region_output_ids) { + auto output_id_type = + context->get_def_use_mgr()->GetDef(output_id)->type_id(); + struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}}); + } + // Add a new struct type to the module. + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeStruct, 0, + message_.new_function_struct_return_type_id(), + std::move(struct_member_types))); + // The return type for the function is the newly-created struct. + return_type_id = message_.new_function_struct_return_type_id(); + } + assert( + return_type_id != 0 && + "We should either have a void return type, or have created a struct."); + + // The region's input ids dictate the parameter types to the function. + opt::Instruction::OperandList function_type_operands; + function_type_operands.push_back({SPV_OPERAND_TYPE_ID, {return_type_id}}); + for (auto id : region_input_ids) { + function_type_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {context->get_def_use_mgr()->GetDef(id)->type_id()}}); + } + // Add a new function type to the module, and record that this is the type + // id for the new function. + context->module()->AddType(MakeUnique<opt::Instruction>( + context, SpvOpTypeFunction, 0, message_.new_function_type_id(), + function_type_operands)); + function_type_id = message_.new_function_type_id(); + } + + // Create a new function with |message_.new_function_id| as the function id, + // and the return type and function type prepared above. + std::unique_ptr<opt::Function> outlined_function = + MakeUnique<opt::Function>(MakeUnique<opt::Instruction>( + context, SpvOpFunction, return_type_id, message_.new_function_id(), + opt::Instruction::OperandList( + {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {function_type_id}}}))); + + // Add one parameter to the function for each input id, using the fresh ids + // provided in |input_id_to_fresh_id_map|. + for (auto id : region_input_ids) { + outlined_function->AddParameter(MakeUnique<opt::Instruction>( + context, SpvOpFunctionParameter, + context->get_def_use_mgr()->GetDef(id)->type_id(), + input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList())); + } + + return outlined_function; +} + +void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds( + opt::IRContext* context, + const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map, + const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const { + // Enlarge the module's id bound as needed to accommodate the various fresh + // ids associated with the transformation. + fuzzerutil::UpdateModuleIdBound( + context, message_.new_function_struct_return_type_id()); + fuzzerutil::UpdateModuleIdBound(context, message_.new_function_type_id()); + fuzzerutil::UpdateModuleIdBound(context, message_.new_function_id()); + fuzzerutil::UpdateModuleIdBound(context, + message_.new_function_region_entry_block()); + fuzzerutil::UpdateModuleIdBound(context, message_.new_caller_result_id()); + fuzzerutil::UpdateModuleIdBound(context, message_.new_callee_result_id()); + + for (auto& entry : input_id_to_fresh_id_map) { + fuzzerutil::UpdateModuleIdBound(context, entry.second); + } + + for (auto& entry : output_id_to_fresh_id_map) { + fuzzerutil::UpdateModuleIdBound(context, entry.second); + } +} + +void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( + opt::IRContext* context, const opt::BasicBlock& original_region_exit_block, + const std::set<opt::BasicBlock*>& region_blocks, + const std::vector<uint32_t>& region_input_ids, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map, + const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const { + // Change all uses of input ids inside the region to the corresponding fresh + // ids that will ultimately be parameters of the outlined function. + // This is done by considering each region input id in turn. + for (uint32_t id : region_input_ids) { + // We then consider each use of the input id. + context->get_def_use_mgr()->ForEachUse( + id, [context, id, &input_id_to_fresh_id_map, region_blocks]( + opt::Instruction* use, uint32_t operand_index) { + // Find the block in which this use of the input id occurs. + opt::BasicBlock* use_block = context->get_instr_block(use); + // We want to rewrite the use id if its block occurs in the outlined + // region. + if (region_blocks.count(use_block) != 0) { + // Rewrite this use of the input id. + use->SetOperand(operand_index, {input_id_to_fresh_id_map.at(id)}); + } + }); + } + + // Change each definition of a region output id to define the corresponding + // fresh ids that will store intermediate value for the output ids. Also + // change all uses of the output id located in the outlined region. + // This is done by considering each region output id in turn. + for (uint32_t id : region_output_ids) { + // First consider each use of the output id and update the relevant uses. + context->get_def_use_mgr()->ForEachUse( + id, + [context, &original_region_exit_block, id, &output_id_to_fresh_id_map, + region_blocks](opt::Instruction* use, uint32_t operand_index) { + // Find the block in which this use of the output id occurs. + auto use_block = context->get_instr_block(use); + // We want to rewrite the use id if its block occurs in the outlined + // region, with one exception: the terminator of the exit block of + // the region is going to remain in the original function, so if the + // use appears in such a terminator instruction we leave it alone. + if ( + // The block is in the region ... + region_blocks.count(use_block) != 0 && + // ... and the use is not in the terminator instruction of the + // region's exit block. + !(use_block == &original_region_exit_block && + use->IsBlockTerminator())) { + // Rewrite this use of the output id. + use->SetOperand(operand_index, {output_id_to_fresh_id_map.at(id)}); + } + }); + + // Now change the instruction that defines the output id so that it instead + // defines the corresponding fresh id. We do this after changing all the + // uses so that the definition of the original id is still registered when + // we analyse its uses. + context->get_def_use_mgr()->GetDef(id)->SetResultId( + output_id_to_fresh_id_map.at(id)); + } +} + +void TransformationOutlineFunction::PopulateOutlinedFunction( + opt::IRContext* context, const opt::BasicBlock& original_region_entry_block, + const opt::BasicBlock& original_region_exit_block, + const std::set<opt::BasicBlock*>& region_blocks, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map, + opt::Function* outlined_function) const { + // When we create the exit block for the outlined region, we use this pointer + // to track of it so that we can manipulate it later. + opt::BasicBlock* outlined_region_exit_block = nullptr; + + // The region entry block in the new function is identical to the entry block + // of the region being outlined, except that it has + // |message_.new_function_region_entry_block| as its id. + std::unique_ptr<opt::BasicBlock> outlined_region_entry_block = + MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>( + context, SpvOpLabel, 0, message_.new_function_region_entry_block(), + opt::Instruction::OperandList())); + outlined_region_entry_block->SetParent(outlined_function); + if (&original_region_entry_block == &original_region_exit_block) { + outlined_region_exit_block = outlined_region_entry_block.get(); + } + + for (auto& inst : original_region_entry_block) { + outlined_region_entry_block->AddInstruction( + std::unique_ptr<opt::Instruction>(inst.Clone(context))); + } + outlined_function->AddBasicBlock(std::move(outlined_region_entry_block)); + + // We now go through the single-entry single-exit region defined by the entry + // and exit blocks, adding clones of all blocks to the new function. + + // Consider every block in the enclosing function. + auto enclosing_function = original_region_entry_block.GetParent(); + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + // Skip the region's entry block - we already dealt with it above. + if (region_blocks.count(&*block_it) == 0 || + &*block_it == &original_region_entry_block) { + ++block_it; + continue; + } + // Clone the block so that it can be added to the new function. + auto cloned_block = + std::unique_ptr<opt::BasicBlock>(block_it->Clone(context)); + + // If this is the region's exit block, then the cloned block is the outlined + // region's exit block. + if (&*block_it == &original_region_exit_block) { + assert(outlined_region_exit_block == nullptr && + "We should not yet have encountered the exit block."); + outlined_region_exit_block = cloned_block.get(); + } + + cloned_block->SetParent(outlined_function); + + // Redirect any OpPhi operands whose predecessors are the original region + // entry block to become the new function entry block. + cloned_block->ForEachPhiInst([this](opt::Instruction* phi_inst) { + for (uint32_t predecessor_index = 1; + predecessor_index < phi_inst->NumInOperands(); + predecessor_index += 2) { + if (phi_inst->GetSingleWordInOperand(predecessor_index) == + message_.entry_block()) { + phi_inst->SetInOperand(predecessor_index, + {message_.new_function_region_entry_block()}); + } + } + }); + + outlined_function->AddBasicBlock(std::move(cloned_block)); + block_it = block_it.Erase(); + } + assert(outlined_region_exit_block != nullptr && + "We should have encountered the region's exit block when iterating " + "through the function"); + + // We now need to adapt the exit block for the region - in the new function - + // so that it ends with a return. + + // We first eliminate the merge instruction (if any) and the terminator for + // the cloned exit block. + for (auto inst_it = outlined_region_exit_block->begin(); + inst_it != outlined_region_exit_block->end();) { + if (inst_it->opcode() == SpvOpLoopMerge || + inst_it->opcode() == SpvOpSelectionMerge) { + inst_it = inst_it.Erase(); + } else if (inst_it->IsBlockTerminator()) { + inst_it = inst_it.Erase(); + } else { + ++inst_it; + } + } + + // We now add either OpReturn or OpReturnValue as the cloned exit block's + // terminator. + if (region_output_ids.empty()) { + // The case where there are no region output ids is simple: we just add + // OpReturn. + outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>( + context, SpvOpReturn, 0, 0, opt::Instruction::OperandList())); + } else { + // In the case where there are output ids, we add an OpCompositeConstruct + // instruction to pack all the output values into a struct, and then an + // OpReturnValue instruction to return this struct. + opt::Instruction::OperandList struct_member_operands; + for (uint32_t id : region_output_ids) { + struct_member_operands.push_back( + {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}}); + } + outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>( + context, SpvOpCompositeConstruct, + message_.new_function_struct_return_type_id(), + message_.new_callee_result_id(), struct_member_operands)); + outlined_region_exit_block->AddInstruction(MakeUnique<opt::Instruction>( + context, SpvOpReturnValue, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}}))); + } + + outlined_function->SetFunctionEnd(MakeUnique<opt::Instruction>( + context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList())); +} + +void TransformationOutlineFunction::ShrinkOriginalRegion( + opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks, + const std::vector<uint32_t>& region_input_ids, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& output_id_to_type_id, + uint32_t return_type_id, + std::unique_ptr<opt::Instruction> cloned_exit_block_merge, + std::unique_ptr<opt::Instruction> cloned_exit_block_terminator, + opt::BasicBlock* original_region_entry_block) const { + // Erase all blocks from the original function that are in the outlined + // region, except for the region's entry block. + // + // In the process, identify all references to the exit block of the region, + // as merge blocks, continue targets, or OpPhi predecessors, and rewrite them + // to refer to the region entry block (the single block to which we are + // shrinking the region). + auto enclosing_function = original_region_entry_block->GetParent(); + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + if (&*block_it == original_region_entry_block) { + ++block_it; + } else if (region_blocks.count(&*block_it) == 0) { + // The block is not in the region. Check whether it has the last block + // of the region as an OpPhi predecessor, and if so change the + // predecessor to be the first block of the region (i.e. the block + // containing the call to what was outlined). + assert(block_it->MergeBlockIdIfAny() != message_.exit_block() && + "Outlined region must not end with a merge block"); + assert(block_it->ContinueBlockIdIfAny() != message_.exit_block() && + "Outlined region must not end with a continue target"); + block_it->ForEachPhiInst([this](opt::Instruction* phi_inst) { + for (uint32_t predecessor_index = 1; + predecessor_index < phi_inst->NumInOperands(); + predecessor_index += 2) { + if (phi_inst->GetSingleWordInOperand(predecessor_index) == + message_.exit_block()) { + phi_inst->SetInOperand(predecessor_index, {message_.entry_block()}); + } + } + }); + ++block_it; + } else { + // The block is in the region and is not the region's entry block: kill + // it. + block_it = block_it.Erase(); + } + } + + // Now erase all instructions from the region's entry block, as they have + // been outlined. + for (auto inst_it = original_region_entry_block->begin(); + inst_it != original_region_entry_block->end();) { + inst_it = inst_it.Erase(); + } + + // Now we add a call to the outlined function to the region's entry block. + opt::Instruction::OperandList function_call_operands; + function_call_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.new_function_id()}}); + // The function parameters are the region input ids. + for (auto input_id : region_input_ids) { + function_call_operands.push_back({SPV_OPERAND_TYPE_ID, {input_id}}); + } + + original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>( + context, SpvOpFunctionCall, return_type_id, + message_.new_caller_result_id(), function_call_operands)); + + // If there are output ids, the function call will return a struct. For each + // output id, we add an extract operation to pull the appropriate struct + // member out into an output id. + for (uint32_t index = 0; index < region_output_ids.size(); ++index) { + uint32_t output_id = region_output_ids[index]; + original_region_entry_block->AddInstruction(MakeUnique<opt::Instruction>( + context, SpvOpCompositeExtract, output_id_to_type_id.at(output_id), + output_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}}))); + } + + // Finally, we terminate the block with the merge instruction (if any) that + // used to belong to the region's exit block, and the terminator that used + // to belong to the region's exit block. + if (cloned_exit_block_merge != nullptr) { + original_region_entry_block->AddInstruction( + std::move(cloned_exit_block_merge)); + } + original_region_entry_block->AddInstruction( + std::move(cloned_exit_block_terminator)); +} + +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h new file mode 100644 index 0000000..784499d --- /dev/null +++ b/third_party/SPIRV-Tools/source/fuzz/transformation_outline_function.h
@@ -0,0 +1,222 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ + +#include <map> +#include <set> +#include <vector> + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationOutlineFunction : public Transformation { + public: + explicit TransformationOutlineFunction( + const protobufs::TransformationOutlineFunction& message); + + TransformationOutlineFunction( + uint32_t entry_block, uint32_t exit_block, + uint32_t new_function_struct_return_type_id, + uint32_t new_function_type_id, uint32_t new_function_id, + uint32_t new_function_region_entry_block, uint32_t new_caller_result_id, + uint32_t new_callee_result_id, + std::map<uint32_t, uint32_t>&& input_id_to_fresh_id, + std::map<uint32_t, uint32_t>&& output_id_to_fresh_id); + + // - All the fresh ids occurring in the transformation must be distinct and + // fresh + // - |message_.entry_block| and |message_.exit_block| must form a single-entry + // single-exit control flow graph region + // - |message_.entry_block| must not start with OpVariable + // - |message_.entry_block| must not be a loop header + // - |message_.exit_block| must not be a merge block or the continue target + // of a loop + // - A structured control flow construct must lie either completely within the + // region or completely outside it + // - |message.entry_block| must not start with OpPhi; this is to keep the + // transformation simple - another transformation should be used to split + // a desired entry block that starts with OpPhi if needed + // - |message_.input_id_to_fresh_id| must contain an entry for every id + // defined outside the region but used in the region + // - |message_.output_id_to_fresh_id| must contain an entry for every id + // defined in the region but used outside the region + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // - A new function with id |message_.new_function_id| is added to the module. + // - If the region generates output ids, the return type of this function is + // a new struct type with one field per output id, and with type id + // |message_.new_function_struct_return_type|, otherwise the function return + // types is void and |message_.new_function_struct_return_type| is not used. + // - If the region generates input ids, the new function has one parameter per + // input id. Fresh ids for these parameters are provided by + // |message_.input_id_to_fresh_id|. + // - Unless the type required for the new function is already known, + // |message_.new_function_type_id| is used as the type id for a new function + // type, and the new function uses this type. + // - The new function starts with a dummy block with id + // |message_.new_function_first_block|, which jumps straight to a successor + // block, to avoid violating rules on what the first block in a function may + // look like. + // - The outlined region is replaced with a single block, with the same id + // as |message_.entry_block|, and which calls the new function, passing the + // region's input ids as parameters. The result is stored in + // |message_.new_caller_result_id|, which has type + // |message_.new_function_struct_return_type| (unless there are + // no output ids, in which case the return type is void). The components + // of this returned struct are then copied out into the region's output ids. + // The block ends with the merge instruction (if any) and terminator of + // |message_.exit_block|. + // - The body of the new function is identical to the outlined region, except + // that (a) the region's entry block has id + // |message_.new_function_region_entry_block|, (b) input id uses are + // replaced with parameter accesses, (c) and definitions of output ids are + // replaced with definitions of corresponding fresh ids provided by + // |message_.output_id_to_fresh_id|, and (d) the block of the function + // ends by returning a composite of type + // |message_.new_function_struct_return_type| comprised of all the fresh + // output ids (unless the return type is void, in which case no value is + // returned. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the set of blocks dominated by |entry_block| and post-dominated + // by |exit_block|. + static std::set<opt::BasicBlock*> GetRegionBlocks( + opt::IRContext* context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block); + + // Yields ids that are used in |region_set| and that are either parameters + // to the function containing |region_set|, or are defined by blocks of this + // function that are outside |region_set|. + // + // Special cases: OpPhi instructions in |region_entry_block| and the + // terminator of |region_exit_block| do not get outlined, therefore + // - id uses in OpPhi instructions in |region_entry_block| are ignored + // - id uses in the terminator instruction of |region_exit_block| are ignored + static std::vector<uint32_t> GetRegionInputIds( + opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set, + opt::BasicBlock* region_exit_block); + + // Yields all ids that are defined in |region_set| and used outside + // |region_set|. + // + // Special cases: for similar reasons as for |GetRegionInputIds|, + // - ids defined in the region and used in the terminator of + // |region_exit_block| count as output ids + static std::vector<uint32_t> GetRegionOutputIds( + opt::IRContext* context, const std::set<opt::BasicBlock*>& region_set, + opt::BasicBlock* region_exit_block); + + private: + // A helper method for the applicability check. Returns true if and only if + // |id| is (a) a fresh id for the module, and (b) an id that has not + // previously been subject to this check. We use this to check whether the + // ids given for the transformation are not only fresh but also different from + // one another. + bool CheckIdIsFreshAndNotUsedByThisTransformation( + uint32_t id, opt::IRContext* context, + std::set<uint32_t>* ids_used_by_this_transformation) const; + + // Ensures that the module's id bound is at least the maximum of any fresh id + // associated with the transformation. + void UpdateModuleIdBoundForFreshIds( + opt::IRContext* context, + const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map, + const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const; + + // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert, + // in the region to be outlined, all the input ids in |region_input_ids| and + // the output ids in |region_output_ids| to their fresh counterparts. + // Parameters |region_blocks| provides access to the blocks that must be + // modified, and |original_region_exit_block| allows for some special cases + // where ids should not be remapped. + void RemapInputAndOutputIdsInRegion( + opt::IRContext* context, + const opt::BasicBlock& original_region_exit_block, + const std::set<opt::BasicBlock*>& region_blocks, + const std::vector<uint32_t>& region_input_ids, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map, + const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const; + + // Produce a Function object that has the right function type and parameter + // declarations. The function argument types and parameter ids are dictated + // by |region_input_ids| and |input_id_to_fresh_id_map|. The function return + // type is dictated by |region_output_ids|. + // + // A new struct type to represent the function return type, and a new function + // type for the function, will be added to the module (unless suitable types + // are already present). + std::unique_ptr<opt::Function> PrepareFunctionPrototype( + opt::IRContext* context, const std::vector<uint32_t>& region_input_ids, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const; + + // Creates the body of the outlined function by cloning blocks from the + // original region, given by |region_blocks|, adapting the cloned version + // of |original_region_exit_block| so that it returns something appropriate, + // and patching up branches to |original_region_entry_block| to refer to its + // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are + // used to determine what the function should return. + void PopulateOutlinedFunction( + opt::IRContext* context, + const opt::BasicBlock& original_region_entry_block, + const opt::BasicBlock& original_region_exit_block, + const std::set<opt::BasicBlock*>& region_blocks, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map, + opt::Function* outlined_function) const; + + // Shrinks the outlined region, given by |region_blocks|, down to the single + // block |original_region_entry_block|. This block is itself shrunk to just + // contain: + // - any OpPhi instructions that were originally present + // - a call to the outlined function, with parameters provided by + // |region_input_ids| + // - instructions to route components of the call's return value into + // |region_output_ids| + // - The merge instruction (if any) and terminator of the original region's + // exit block, given by |cloned_exit_block_merge| and + // |cloned_exit_block_terminator| + // Parameters |output_id_to_type_id| and |return_type_id| provide the + // provide types for the region's output ids, and the return type of the + // outlined function: as the module is in an inconsistent state when this + // function is called, this information cannot be gotten from the def-use + // manager. + void ShrinkOriginalRegion( + opt::IRContext* context, std::set<opt::BasicBlock*>& region_blocks, + const std::vector<uint32_t>& region_input_ids, + const std::vector<uint32_t>& region_output_ids, + const std::map<uint32_t, uint32_t>& output_id_to_type_id, + uint32_t return_type_id, + std::unique_ptr<opt::Instruction> cloned_exit_block_merge, + std::unique_ptr<opt::Instruction> cloned_exit_block_terminator, + opt::BasicBlock* original_region_entry_block) const; + + protobufs::TransformationOutlineFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
diff --git a/third_party/SPIRV-Tools/source/operand.cpp b/third_party/SPIRV-Tools/source/operand.cpp index cd26a3d..39d17a6 100644 --- a/third_party/SPIRV-Tools/source/operand.cpp +++ b/third_party/SPIRV-Tools/source/operand.cpp
@@ -29,6 +29,7 @@ // per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195 #include "operand.kinds-unified1.inc" +#include "spirv-tools/libspirv.h" static const spv_operand_table_t kOperandTable = { ARRAY_SIZE(pygen_variable_OperandInfoTable), @@ -228,6 +229,18 @@ return "debug type qualifier"; case SPV_OPERAND_TYPE_DEBUG_OPERATION: return "debug operation"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + return "OpenCL.DebugInfo.100 debug info flags"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + return "OpenCL.DebugInfo.100 debug base type encoding"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + return "OpenCL.DebugInfo.100 debug composite type"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + return "OpenCL.DebugInfo.100 debug type qualifier"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + return "OpenCL.DebugInfo.100 debug operation"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + return "OpenCL.DebugInfo.100 debug imported entity"; // The next values are for values returned from an instruction, not actually // an operand. So the specific strings don't matter. But let's add them @@ -312,6 +325,11 @@ case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: return true; default: break; @@ -328,6 +346,7 @@ case SPV_OPERAND_TYPE_FUNCTION_CONTROL: case SPV_OPERAND_TYPE_MEMORY_ACCESS: case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: return true; default: break;
diff --git a/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp b/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp index 107723d..263a069 100644 --- a/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp +++ b/third_party/SPIRV-Tools/source/opt/block_merge_util.cpp
@@ -49,6 +49,18 @@ return IsMerge(context, block->id()); } +// Returns true if |id| is the continue target of a merge instruction. +bool IsContinue(IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->WhileEachUse( + id, [](Instruction* user, uint32_t index) { + SpvOp op = user->opcode(); + if (op == SpvOpLoopMerge && index == 1u) { + return false; + } + return true; + }); +} + // Removes any OpPhi instructions in |block|, which should have exactly one // predecessor, replacing uses of OpPhi ids with the ids associated with the // predecessor. @@ -86,6 +98,11 @@ return false; } + if (pred_is_merge && IsContinue(context, lab_id)) { + // Cannot merge a continue target with a merge block. + return false; + } + // Don't bother trying to merge unreachable blocks. if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) { if (!dominators->IsReachable(block)) return false;
diff --git a/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.cpp b/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.cpp index 4c02c73..5022e1b 100644 --- a/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.cpp +++ b/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.cpp
@@ -42,7 +42,7 @@ return Pass::IsFloat(ty_id, width); } -bool ConvertToHalfPass::IsRelaxed(Instruction* inst) { +bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) { uint32_t r_id = inst->result_id(); for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false)) if (r_inst->opcode() == SpvOpDecorate && @@ -51,6 +51,12 @@ return false; } +bool ConvertToHalfPass::IsRelaxed(uint32_t id) { + return relaxed_ids_set_.count(id) > 0; +} + +void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); } + analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) { analysis::Float float_ty(width); return context()->get_type_mgr()->GetRegisteredType(&float_ty); @@ -87,16 +93,19 @@ } void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width, - InstructionBuilder* builder) { + Instruction* inst) { Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp); uint32_t ty_id = val_inst->type_id(); uint32_t nty_id = EquivFloatTypeId(ty_id, width); if (nty_id == ty_id) return; Instruction* cvt_inst; + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); if (val_inst->opcode() == SpvOpUndef) - cvt_inst = builder->AddNullaryOp(nty_id, SpvOpUndef); + cvt_inst = builder.AddNullaryOp(nty_id, SpvOpUndef); else - cvt_inst = builder->AddUnaryOp(nty_id, SpvOpFConvert, *val_idp); + cvt_inst = builder.AddUnaryOp(nty_id, SpvOpFConvert, *val_idp); *val_idp = cvt_inst->result_id(); } @@ -153,17 +162,15 @@ bool modified = false; // Convert all float32 based operands to float16 equivalent and change // instruction type to float16 equivalent. - InstructionBuilder builder( - context(), inst, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - inst->ForEachInId([&builder, &modified, this](uint32_t* idp) { + inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); if (!IsFloat(op_inst, 32)) return; - GenConvert(idp, 16, &builder); + GenConvert(idp, 16, inst); modified = true; }); if (IsFloat(inst, 32)) { inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + converted_ids_.insert(inst->result_id()); modified = true; } if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); @@ -171,23 +178,10 @@ } bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { - // Skip if not float32 - if (!IsFloat(inst, 32)) return false; - // Skip if no relaxed operands. - bool relaxed_found = false; - uint32_t ocnt = 0; - inst->ForEachInId([&ocnt, &relaxed_found, this](uint32_t* idp) { - if (ocnt % 2 == 0) { - Instruction* val_inst = get_def_use_mgr()->GetDef(*idp); - if (IsRelaxed(val_inst)) relaxed_found = true; - } - ++ocnt; - }); - if (!relaxed_found) return false; // Add float16 converts of any float32 operands and change type // of phi to float16 equivalent. Operand converts need to be added to // preceeding blocks. - ocnt = 0; + uint32_t ocnt = 0; uint32_t* prev_idp; inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) { if (ocnt % 2 == 0) { @@ -203,65 +197,32 @@ insert_before->opcode() != SpvOpLoopMerge) ++insert_before; } - InstructionBuilder builder(context(), &*insert_before, - IRContext::kAnalysisDefUse | - IRContext::kAnalysisInstrToBlockMapping); - GenConvert(prev_idp, 16, &builder); + GenConvert(prev_idp, 16, &*insert_before); } } ++ocnt; }); inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); get_def_use_mgr()->AnalyzeInstUse(inst); + converted_ids_.insert(inst->result_id()); return true; } -bool ConvertToHalfPass::ProcessExtract(Instruction* inst) { - bool modified = false; - uint32_t comp_id = inst->GetSingleWordInOperand(0); - Instruction* comp_inst = get_def_use_mgr()->GetDef(comp_id); - // If extract is relaxed float32 based type and the composite is a relaxed - // float32 based type, convert it to float16 equivalent. This is slightly - // aggressive and pushes any likely conversion to apply to the whole - // composite rather than apply to each extracted component later. This - // can be a win if the platform can convert the entire composite in the same - // time as one component. It risks converting components that may not be - // used, although empirical data on a large set of real-world shaders seems - // to suggest this is not common and the composite convert is the best choice. - if (IsFloat(inst, 32) && IsRelaxed(inst) && IsFloat(comp_inst, 32) && - IsRelaxed(comp_inst)) { - InstructionBuilder builder( - context(), inst, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - GenConvert(&comp_id, 16, &builder); - inst->SetInOperand(0, {comp_id}); - comp_inst = get_def_use_mgr()->GetDef(comp_id); - modified = true; - } - // If the composite is a float16 based type, make sure the type of the - // extract agrees. - if (IsFloat(comp_inst, 16) && !IsFloat(inst, 16)) { - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); - modified = true; - } - if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); - return modified; -} - bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { // If float32 and relaxed, change to float16 convert - if (IsFloat(inst, 32) && IsRelaxed(inst)) { + if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) { inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); get_def_use_mgr()->AnalyzeInstUse(inst); + converted_ids_.insert(inst->result_id()); } - // If operand and result types are the same, replace result with operand - // and change convert to copy to keep validator happy; DCE will clean it up + // If operand and result types are the same, change FConvert to CopyObject to + // keep validator happy; simplification and DCE will clean it up + // One way this can happen is if an FConvert generated during this pass + // (likely by ProcessPhi) is later encountered here and its operand has been + // changed to half. uint32_t val_id = inst->GetSingleWordInOperand(0); Instruction* val_inst = get_def_use_mgr()->GetDef(val_id); - if (inst->type_id() == val_inst->type_id()) { - context()->ReplaceAllUsesWith(inst->result_id(), val_id); - inst->SetOpcode(SpvOpCopyObject); - } + if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(SpvOpCopyObject); return true; // modified } @@ -270,12 +231,8 @@ // If image reference, only need to convert dref args back to float32 if (dref_image_ops_.count(inst->opcode()) != 0) { uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx); - Instruction* dref_inst = get_def_use_mgr()->GetDef(dref_id); - if (IsFloat(dref_inst, 16) && IsRelaxed(dref_inst)) { - InstructionBuilder builder( - context(), inst, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - GenConvert(&dref_id, 32, &builder); + if (converted_ids_.count(dref_id) > 0) { + GenConvert(&dref_id, 32, inst); inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id}); get_def_use_mgr()->AnalyzeInstUse(inst); modified = true; @@ -288,32 +245,24 @@ bool modified = false; // If non-relaxed instruction has changed operands, need to convert // them back to float32 - InstructionBuilder builder( - context(), inst, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - inst->ForEachInId([&builder, &modified, this](uint32_t* idp) { - Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); - if (!IsFloat(op_inst, 16)) return; - if (!IsRelaxed(op_inst)) return; + inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + if (converted_ids_.count(*idp) == 0) return; uint32_t old_id = *idp; - GenConvert(idp, 32, &builder); + GenConvert(idp, 32, inst); if (*idp != old_id) modified = true; }); if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); return modified; } -bool ConvertToHalfPass::GenHalfCode(Instruction* inst) { +bool ConvertToHalfPass::GenHalfInst(Instruction* inst) { bool modified = false; // Remember id for later deletion of RelaxedPrecision decoration - bool inst_relaxed = IsRelaxed(inst); - if (inst_relaxed) relaxed_ids_.push_back(inst->result_id()); + bool inst_relaxed = IsRelaxed(inst->result_id()); if (IsArithmetic(inst) && inst_relaxed) modified = GenHalfArith(inst); - else if (inst->opcode() == SpvOpPhi) + else if (inst->opcode() == SpvOpPhi && inst_relaxed) modified = ProcessPhi(inst); - else if (inst->opcode() == SpvOpCompositeExtract) - modified = ProcessExtract(inst); else if (inst->opcode() == SpvOpFConvert) modified = ProcessConvert(inst); else if (image_ops_.count(inst->opcode()) != 0) @@ -323,13 +272,62 @@ return modified; } +bool ConvertToHalfPass::CloseRelaxInst(Instruction* inst) { + if (inst->result_id() == 0) return false; + if (IsRelaxed(inst->result_id())) return false; + if (!IsFloat(inst, 32)) return false; + if (IsDecoratedRelaxed(inst)) { + AddRelaxed(inst->result_id()); + return true; + } + if (closure_ops_.count(inst->opcode()) == 0) return false; + // Can relax if all float operands are relaxed + bool relax = true; + inst->ForEachInId([&relax, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (!IsFloat(op_inst, 32)) return; + if (!IsRelaxed(*idp)) relax = false; + }); + if (relax) { + AddRelaxed(inst->result_id()); + return true; + } + // Can relax if all uses are relaxed + relax = true; + get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) { + if (uinst->result_id() == 0 || !IsFloat(uinst, 32) || + (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) { + relax = false; + return; + } + }); + if (relax) { + AddRelaxed(inst->result_id()); + return true; + } + return false; +} + bool ConvertToHalfPass::ProcessFunction(Function* func) { + // Do a closure of Relaxed on composite and phi instructions + bool changed = true; + while (changed) { + changed = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&changed, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + changed |= CloseRelaxInst(&*ii); + }); + } + // Do convert of relaxed instructions to half precision bool modified = false; cfg()->ForEachBlockInReversePostOrder( func->entry().get(), [&modified, this](BasicBlock* bb) { for (auto ii = bb->begin(); ii != bb->end(); ++ii) - modified |= GenHalfCode(&*ii); + modified |= GenHalfInst(&*ii); }); + // Replace invalid converts of matrix into equivalent vector extracts, + // converts and finally a composite construct cfg()->ForEachBlockInReversePostOrder( func->entry().get(), [&modified, this](BasicBlock* bb) { for (auto ii = bb->begin(); ii != bb->end(); ++ii) @@ -346,7 +344,7 @@ // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(SpvCapabilityFloat16); // Remove all RelaxedPrecision decorations from instructions and globals - for (auto c_id : relaxed_ids_) RemoveRelaxedDecoration(c_id); + for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id); for (auto& val : get_module()->types_values()) { uint32_t v_id = val.result_id(); if (v_id != 0) RemoveRelaxedDecoration(v_id); @@ -366,6 +364,7 @@ SpvOpVectorShuffle, SpvOpCompositeConstruct, SpvOpCompositeInsert, + SpvOpCompositeExtract, SpvOpCopyObject, SpvOpTranspose, SpvOpConvertSToF, @@ -453,7 +452,19 @@ SpvOpImageSparseSampleProjDrefExplicitLod, SpvOpImageSparseDrefGather, }; - relaxed_ids_.clear(); + closure_ops_ = { + SpvOpVectorExtractDynamic, + SpvOpVectorInsertDynamic, + SpvOpVectorShuffle, + SpvOpCompositeConstruct, + SpvOpCompositeInsert, + SpvOpCompositeExtract, + SpvOpCopyObject, + SpvOpTranspose, + SpvOpPhi, + }; + relaxed_ids_set_.clear(); + converted_ids_.clear(); } } // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.h b/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.h index 5225848..143aebf 100644 --- a/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.h +++ b/third_party/SPIRV-Tools/source/opt/convert_to_half_pass.h
@@ -38,7 +38,8 @@ const char* name() const override { return "convert-to-half-pass"; } private: - // Return true if |inst| is an arithmetic op that can be of type float16 + // Return true if |inst| is an arithmetic, composite or phi op that can be + // of type float16 bool IsArithmetic(Instruction* inst); // Return true if |inst| returns scalar, vector or matrix type with base @@ -46,7 +47,13 @@ bool IsFloat(Instruction* inst, uint32_t width); // Return true if |inst| is decorated with RelaxedPrecision - bool IsRelaxed(Instruction* inst); + bool IsDecoratedRelaxed(Instruction* inst); + + // Return true if |id| has been added to the relaxed id set + bool IsRelaxed(uint32_t id); + + // Add |id| to the relaxed id set + void AddRelaxed(uint32_t id); // Return type id for float with |width| analysis::Type* FloatScalarType(uint32_t width); @@ -64,19 +71,23 @@ // Append instructions to builder to convert value |*val_idp| to type // |ty_id| but with |width|. Set |*val_idp| to the new id. - void GenConvert(uint32_t* val_idp, uint32_t width, - InstructionBuilder* builder); + void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst); // Remove RelaxedPrecision decoration of |id|. void RemoveRelaxedDecoration(uint32_t id); + // Add |inst| to relaxed instruction set if warranted. Specifically, if + // it is float32 and either decorated relaxed or a composite or phi + // instruction where all operands are relaxed or all uses are relaxed. + bool CloseRelaxInst(Instruction* inst); + // If |inst| is an arithmetic, phi, extract or convert instruction of float32 // base type and decorated with RelaxedPrecision, change it to the equivalent // float16 based type instruction. Specifically, insert instructions to // convert all operands to float16 (if needed) and change its type to the // equivalent float16 type. Otherwise, insert instructions to convert its // operands back to their original types, if needed. - bool GenHalfCode(Instruction* inst); + bool GenHalfInst(Instruction* inst); // Gen code for relaxed arithmetic |inst| bool GenHalfArith(Instruction* inst); @@ -84,9 +95,6 @@ // Gen code for relaxed phi |inst| bool ProcessPhi(Instruction* inst); - // Gen code for relaxed extract |inst| - bool ProcessExtract(Instruction* inst); - // Gen code for relaxed convert |inst| bool ProcessConvert(Instruction* inst); @@ -98,11 +106,11 @@ // If |inst| is an FConvert of a matrix type, decompose it to a series // of vector extracts, converts and inserts into an Undef. These are - // generated by GenHalfCode because they are easier to manipulate, but are + // generated by GenHalfInst because they are easier to manipulate, but are // invalid so we need to clean them up. bool MatConvertCleanup(Instruction* inst); - // Call GenHalfCode on every instruction in |func|. + // Call GenHalfInst on every instruction in |func|. // If code is generated for an instruction, replace the instruction // with the new instructions that are generated. bool ProcessFunction(Function* func); @@ -124,8 +132,14 @@ // Set of dref sample operations std::unordered_set<uint32_t> dref_image_ops_; + // Set of dref sample operations + std::unordered_set<uint32_t> closure_ops_; + + // Set of ids of all relaxed instructions + std::unordered_set<uint32_t> relaxed_ids_set_; + // Ids of all converted instructions - std::vector<uint32_t> relaxed_ids_; + std::unordered_set<uint32_t> converted_ids_; }; } // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/folding_rules.cpp b/third_party/SPIRV-Tools/source/opt/folding_rules.cpp index de740ca..1c8cdc8 100644 --- a/third_party/SPIRV-Tools/source/opt/folding_rules.cpp +++ b/third_party/SPIRV-Tools/source/opt/folding_rules.cpp
@@ -553,7 +553,6 @@ const analysis::Constant* input1, const analysis::Constant* input2) { assert(input1 && input2); - assert(input1->type() == input2->type()); const analysis::Type* type = input1->type(); std::vector<uint32_t> words; if (const analysis::Vector* vector_type = type->AsVector()) { @@ -1502,60 +1501,64 @@ }; } -FoldingRule CompositeExtractFeedingConstruct() { - // If the OpCompositeConstruct is simply putting back together elements that - // where extracted from the same souce, we can simlpy reuse the source. - // - // This is a common code pattern because of the way that scalar replacement - // works. - return [](IRContext* context, Instruction* inst, - const std::vector<const analysis::Constant*>&) { - assert(inst->opcode() == SpvOpCompositeConstruct && - "Wrong opcode. Should be OpCompositeConstruct."); - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - uint32_t original_id = 0; +// If the OpCompositeConstruct is simply putting back together elements that +// where extracted from the same source, we can simply reuse the source. +// +// This is a common code pattern because of the way that scalar replacement +// works. +bool CompositeExtractFeedingConstruct( + IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == SpvOpCompositeConstruct && + "Wrong opcode. Should be OpCompositeConstruct."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t original_id = 0; - // Check each element to make sure they are: - // - extractions - // - extracting the same position they are inserting - // - all extract from the same id. - for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { - uint32_t element_id = inst->GetSingleWordInOperand(i); - Instruction* element_inst = def_use_mgr->GetDef(element_id); + if (inst->NumInOperands() == 0) { + // The struct being constructed has no members. + return false; + } - if (element_inst->opcode() != SpvOpCompositeExtract) { - return false; - } + // Check each element to make sure they are: + // - extractions + // - extracting the same position they are inserting + // - all extract from the same id. + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + const uint32_t element_id = inst->GetSingleWordInOperand(i); + Instruction* element_inst = def_use_mgr->GetDef(element_id); - if (element_inst->NumInOperands() != 2) { - return false; - } - - if (element_inst->GetSingleWordInOperand(1) != i) { - return false; - } - - if (i == 0) { - original_id = - element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); - } else if (original_id != element_inst->GetSingleWordInOperand( - kExtractCompositeIdInIdx)) { - return false; - } - } - - // The last check it to see that the object being extracted from is the - // correct type. - Instruction* original_inst = def_use_mgr->GetDef(original_id); - if (original_inst->type_id() != inst->type_id()) { + if (element_inst->opcode() != SpvOpCompositeExtract) { return false; } - // Simplify by using the original object. - inst->SetOpcode(SpvOpCopyObject); - inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); - return true; - }; + if (element_inst->NumInOperands() != 2) { + return false; + } + + if (element_inst->GetSingleWordInOperand(1) != i) { + return false; + } + + if (i == 0) { + original_id = + element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + } else if (original_id != + element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)) { + return false; + } + } + + // The last check it to see that the object being extracted from is the + // correct type. + Instruction* original_inst = def_use_mgr->GetDef(original_id); + if (original_inst->type_id() != inst->type_id()) { + return false; + } + + // Simplify by using the original object. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + return true; } FoldingRule InsertFeedingExtract() { @@ -2420,7 +2423,7 @@ // 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_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct()); + rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct); rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract()); rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());
diff --git a/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp b/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp index e309d3a..22c979c 100644 --- a/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp +++ b/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.cpp
@@ -121,6 +121,10 @@ // the result of an OpArrayLength instruction acting on the pointer of // the containing structure as noted above. // +// - Access chain indices are always treated as signed, so: +// - Clamp the upper bound at the signed integer maximum. +// - Use SClamp for all clamping. +// // - TODO(dneto): OpImageTexelPointer: // - Clamp coordinate to the image size returned by OpImageQuerySize // - If multi-sampled, clamp the sample index to the count returned by @@ -141,6 +145,7 @@ #include <cstring> #include <functional> #include <initializer_list> +#include <limits> #include <utility> #include "constants.h" @@ -246,6 +251,7 @@ } for (auto* inst : access_chains) { ClampIndicesForAccessChain(inst); + if (module_status_.failed) return module_status_.modified; } for (auto* inst : image_texel_pointers) { @@ -261,6 +267,8 @@ auto* constant_mgr = context()->get_constant_mgr(); auto* def_use_mgr = context()->get_def_use_mgr(); auto* type_mgr = context()->get_type_mgr(); + const bool have_int64_cap = + context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64); // Replaces one of the OpAccessChain index operands with a new value. // Updates def-use analysis. @@ -268,37 +276,66 @@ Instruction* new_value) { inst.SetOperand(operand_index, {new_value->result_id()}); def_use_mgr->AnalyzeInstUse(&inst); + return SPV_SUCCESS; }; // Replaces one of the OpAccesssChain index operands with a clamped value. // Replace the operand at |operand_index| with the value computed from - // unsigned_clamp(%old_value, %min_value, %max_value). It also analyzes + // signed_clamp(%old_value, %min_value, %max_value). It also analyzes // the new instruction and records that them module is modified. - auto clamp_index = [&inst, this, &replace_index]( + // Assumes %min_value is signed-less-or-equal than %max_value. (All callees + // use 0 for %min_value). + auto clamp_index = [&inst, type_mgr, this, &replace_index]( uint32_t operand_index, Instruction* old_value, Instruction* min_value, Instruction* max_value) { - auto* clamp_inst = MakeClampInst(old_value, min_value, max_value, &inst); - replace_index(operand_index, clamp_inst); + auto* clamp_inst = + MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst); + return replace_index(operand_index, clamp_inst); }; // Ensures the specified index of access chain |inst| has a value that is // at most |count| - 1. If the index is already a constant value less than // |count| then no change is made. - auto clamp_to_literal_count = [&inst, this, &constant_mgr, &type_mgr, - &replace_index, &clamp_index]( - uint32_t operand_index, uint64_t count) { + auto clamp_to_literal_count = + [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index, + &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t { Instruction* index_inst = this->GetDef(inst.GetSingleWordOperand(operand_index)); const auto* index_type = type_mgr->GetType(index_inst->type_id())->AsInteger(); assert(index_type); + const auto index_width = index_type->width(); + if (count <= 1) { // Replace the index with 0. - replace_index(operand_index, GetValueForType(0, index_type)); - return; + return replace_index(operand_index, GetValueForType(0, index_type)); } - const auto index_width = index_type->width(); + uint64_t maxval = count - 1; + + // Compute the bit width of a viable type to hold |maxval|. + // Look for a bit width, up to 64 bits wide, to fit maxval. + uint32_t maxval_width = index_width; + while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) { + maxval_width *= 2; + } + // Determine the type for |maxval|. + analysis::Integer signed_type_for_query(maxval_width, true); + auto* maxval_type = + type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger(); + // Access chain indices are treated as signed, so limit the maximum value + // of the index so it will always be positive for a signed clamp operation. + maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1)); + + if (index_width > 64) { + return this->Fail() << "Can't handle indices wider than 64 bits, found " + "constant index with " + << index_width << " bits as index number " + << operand_index << " of access chain " + << inst.PrettyPrint(); + } + + // Split into two cases: the current index is a constant, or not. // If the index is a constant then |index_constant| will not be a null // pointer. (If index is an |OpConstantNull| then it |index_constant| will @@ -313,55 +350,62 @@ value = int64_t(int_index_constant->GetS32BitValue()); } else if (index_width <= 64) { value = int_index_constant->GetS64BitValue(); - } else { - this->Fail() << "Can't handle indices wider than 64 bits, found " - "constant index with " - << index_type->width() << "bits"; - return; } if (value < 0) { - replace_index(operand_index, GetValueForType(0, index_type)); - } else if (uint64_t(value) < count) { + return replace_index(operand_index, GetValueForType(0, index_type)); + } else if (uint64_t(value) <= maxval) { // Nothing to do. - return; + return SPV_SUCCESS; } else { - // Replace with count - 1. + // Replace with maxval. assert(count > 0); // Already took care of this case above. - replace_index(operand_index, GetValueForType(count - 1, index_type)); + return replace_index(operand_index, + GetValueForType(maxval, maxval_type)); } } else { // Generate a clamp instruction. - - // Compute the bit width of a viable type to hold (count-1). - const auto maxval = count - 1; - const auto* maxval_type = index_type; - // Look for a bit width, up to 64 bits wide, to fit maxval. - uint32_t maxval_width = index_width; - while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) { - maxval_width *= 2; + assert(maxval >= 1); + assert(index_width <= 64); // Otherwise, already returned above. + if (index_width >= 64 && !have_int64_cap) { + // An inconsistent module. + return Fail() << "Access chain index is wider than 64 bits, but Int64 " + "is not declared: " + << index_inst->PrettyPrint(); } // Widen the index value if necessary if (maxval_width > index_width) { - // Find the wider type. We only need this case if a constant (array) - // bound is too big. This never requires us to *add* a capability - // declaration for Int64 because the existence of the array bound would - // already have required that declaration. + // Find the wider type. We only need this case if a constant array + // bound is too big. + + // From how we calculated maxval_width, widening won't require adding + // the Int64 capability. + assert(have_int64_cap || maxval_width <= 32); + if (!have_int64_cap && maxval_width >= 64) { + // Be defensive, but this shouldn't happen. + return this->Fail() + << "Clamping index would require adding Int64 capability. " + << "Can't clamp 32-bit index " << operand_index + << " of access chain " << inst.PrettyPrint(); + } index_inst = WidenInteger(index_type->IsSigned(), maxval_width, index_inst, &inst); - maxval_type = type_mgr->GetType(index_inst->type_id())->AsInteger(); } + // Finally, clamp the index. - clamp_index(operand_index, index_inst, GetValueForType(0, maxval_type), - GetValueForType(maxval, maxval_type)); + return clamp_index(operand_index, index_inst, + GetValueForType(0, maxval_type), + GetValueForType(maxval, maxval_type)); } + return SPV_SUCCESS; }; // Ensures the specified index of access chain |inst| has a value that is at // most the value of |count_inst| minus 1, where |count_inst| is treated as an - // unsigned integer. + // unsigned integer. This can log a failure. auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count, - &clamp_index, &type_mgr](uint32_t operand_index, - Instruction* count_inst) { + &clamp_index, + &type_mgr](uint32_t operand_index, + Instruction* count_inst) -> spv_result_t { Instruction* index_inst = this->GetDef(inst.GetSingleWordOperand(operand_index)); const auto* index_type = @@ -378,12 +422,11 @@ } else if (width <= 64) { value = count_constant->AsIntConstant()->GetU64BitValue(); } else { - this->Fail() << "Can't handle indices wider than 64 bits, found " - "constant index with " - << index_type->width() << "bits"; - return; + return this->Fail() << "Can't handle indices wider than 64 bits, found " + "constant index with " + << index_type->width() << "bits"; } - clamp_to_literal_count(operand_index, value); + return clamp_to_literal_count(operand_index, value); } else { // Widen them to the same width. const auto index_width = index_type->width(); @@ -406,9 +449,21 @@ &inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(), {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}}, {SPV_OPERAND_TYPE_ID, {one->result_id()}}}); - clamp_index(operand_index, index_inst, GetValueForType(0, wider_type), - count_minus_1); + auto* zero = GetValueForType(0, wider_type); + // Make sure we clamp to an upper bound that is at most the signed max + // for the target type. + const uint64_t max_signed_value = + ((uint64_t(1) << (target_width - 1)) - 1); + // Use unsigned-min to ensure that the result is always non-negative. + // That ensures we satisfy the invariant for SClamp, where the "min" + // argument we give it (zero), is no larger than the third argument. + auto* upper_bound = + MakeUMinInst(*type_mgr, count_minus_1, + GetValueForType(max_signed_value, wider_type), &inst); + // Now clamp the index to this upper bound. + return clamp_index(operand_index, index_inst, zero, upper_bound); } + return SPV_SUCCESS; }; const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0)); @@ -483,6 +538,7 @@ return; } clamp_to_count(idx, array_len); + if (module_status_.failed) return; pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); } break; @@ -557,22 +613,51 @@ return conversion; } -Instruction* GraphicsRobustAccessPass::MakeClampInst(Instruction* x, - Instruction* min, - Instruction* max, - Instruction* where) { +Instruction* GraphicsRobustAccessPass::MakeUMinInst( + const analysis::TypeManager& tm, Instruction* x, Instruction* y, + Instruction* where) { + // Get IDs of instructions we'll be referencing. Evaluate them before calling + // the function so we force a deterministic ordering in case both of them need + // to take a new ID. + const uint32_t glsl_insts_id = GetGlslInsts(); + uint32_t smin_id = TakeNextId(); + const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); + const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width(); + assert(xwidth == ywidth); + (void)xwidth; + (void)ywidth; + auto* smin_inst = InsertInst( + where, SpvOpExtInst, x->type_id(), smin_id, + { + {SPV_OPERAND_TYPE_ID, {glsl_insts_id}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}}, + {SPV_OPERAND_TYPE_ID, {x->result_id()}}, + {SPV_OPERAND_TYPE_ID, {y->result_id()}}, + }); + return smin_inst; +} + +Instruction* GraphicsRobustAccessPass::MakeSClampInst( + const analysis::TypeManager& tm, Instruction* x, Instruction* min, + Instruction* max, Instruction* where) { // Get IDs of instructions we'll be referencing. Evaluate them before calling // the function so we force a deterministic ordering in case both of them need // to take a new ID. const uint32_t glsl_insts_id = GetGlslInsts(); uint32_t clamp_id = TakeNextId(); - assert(x->type_id() == min->type_id()); - assert(x->type_id() == max->type_id()); + const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); + const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width(); + const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width(); + assert(xwidth == minwidth); + assert(xwidth == maxwidth); + (void)xwidth; + (void)minwidth; + (void)maxwidth; auto* clamp_inst = InsertInst( where, SpvOpExtInst, x->type_id(), clamp_id, { {SPV_OPERAND_TYPE_ID, {glsl_insts_id}}, - {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UClamp}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}}, {SPV_OPERAND_TYPE_ID, {x->result_id()}}, {SPV_OPERAND_TYPE_ID, {min->result_id()}}, {SPV_OPERAND_TYPE_ID, {max->result_id()}}, @@ -716,6 +801,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( opt::Instruction* image_texel_pointer) { // TODO(dneto): Write tests for this code. + // TODO(dneto): Use signed-clamp return SPV_SUCCESS; // Example: @@ -916,9 +1002,9 @@ {constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}}); // Clamp the coordinate - auto* clamp_coord = - MakeClampInst(coord, constant_mgr->GetDefiningInstruction(coordinate_0), - query_max_including_faces, image_texel_pointer); + auto* clamp_coord = MakeSClampInst( + *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0), + query_max_including_faces, image_texel_pointer); image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()}); // Clamp the sample index @@ -936,8 +1022,8 @@ {{SPV_OPERAND_TYPE_ID, {query_samples_id}}, {SPV_OPERAND_TYPE_ID, {component_1_id}}}); - auto* clamp_samples = MakeClampInst( - samples, constant_mgr->GetDefiningInstruction(coordinate_0), + auto* clamp_samples = MakeSClampInst( + *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0), max_samples, image_texel_pointer); image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()});
diff --git a/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.h b/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.h index b21154e..6fc692c 100644 --- a/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.h +++ b/third_party/SPIRV-Tools/source/opt/graphics_robust_access_pass.h
@@ -18,13 +18,13 @@ #include <map> #include <unordered_map> -#include "source/diagnostic.h" - #include "constants.h" #include "def_use_manager.h" #include "instruction.h" #include "module.h" #include "pass.h" +#include "source/diagnostic.h" +#include "type_manager.h" namespace spvtools { namespace opt { @@ -49,7 +49,8 @@ // consumer. spvtools::DiagnosticStream Fail(); - // Returns SPV_SUCCESS if this pass can correctly process the module. + // Returns SPV_SUCCESS if this pass can correctly process the module, + // as far as we can tell from capabilities and the memory model. // Otherwise logs a message and returns a failure code. spv_result_t IsCompatibleModule(); @@ -59,12 +60,12 @@ spv_result_t ProcessCurrentModule(); // Process the given function. Updates the state value |_|. Returns true - // if the module was modified. + // if the module was modified. This can log a failure. bool ProcessAFunction(opt::Function*); // Clamps indices in the OpAccessChain or OpInBoundsAccessChain instruction // |access_chain|. Inserts instructions before the given instruction. Updates - // analyses and records that the module is modified. + // analyses and records that the module is modified. This can log a failure. void ClampIndicesForAccessChain(Instruction* access_chain); // Returns the id of the instruction importing the "GLSL.std.450" extended @@ -85,17 +86,29 @@ Instruction* WidenInteger(bool sign_extend, uint32_t bit_width, Instruction* value, Instruction* before_inst); - // Returns a new instruction that invokes the UClamp GLSL.std.450 extended + // Returns a new instruction that invokes the UMin GLSL.std.450 extended + // instruction with the two given operands. That is, the result of the + // instruction is: + // - |x| if |x| is unsigned-less than |y| + // - |y| otherwise + // We assume that |x| and |y| are scalar integer types with the same + // width. The instruction is inserted before |where|. + opt::Instruction* MakeUMinInst(const analysis::TypeManager& tm, + Instruction* x, Instruction* y, + Instruction* where); + + // Returns a new instruction that invokes the SClamp GLSL.std.450 extended // instruction with the three given operands. That is, the result of the // instruction is: - // - |min| if |x| is unsigned-less than |min| - // - |max| if |x| is unsigned-more than |max| + // - |min| if |x| is signed-less than |min| + // - |max| if |x| is signed-more than |max| // - |x| otherwise. - // We assume that |min| is unsigned-less-or-equal to |max|, and that the + // We assume that |min| is signed-less-or-equal to |max|, and that the // operands all have the same scalar integer type. The instruction is // inserted before |where|. - opt::Instruction* MakeClampInst(Instruction* x, Instruction* min, - Instruction* max, Instruction* where); + opt::Instruction* MakeSClampInst(const analysis::TypeManager& tm, + Instruction* x, Instruction* min, + Instruction* max, Instruction* where); // Returns a new instruction which evaluates to the length the runtime array // referenced by the access chain at the specfied index. The instruction is
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp index 36991ec..3c874a7 100644 --- a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp +++ b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
@@ -27,7 +27,6 @@ static const int kSpvFunctionCallFunctionId = 2; static const int kSpvFunctionCallArgumentId = 3; static const int kSpvReturnValueId = 0; -static const int kSpvLoopMergeContinueTargetIdInIdx = 1; namespace spvtools { namespace opt { @@ -285,19 +284,14 @@ if (rid != 0) callee_result_ids.insert(rid); }); - // If the caller is in a single-block loop, and the callee has multiple - // blocks, then the normal inlining logic will place the OpLoopMerge in - // the last of several blocks in the loop. Instead, it should be placed - // at the end of the first block. First determine if the caller is in a - // single block loop. We'll wait to move the OpLoopMerge until the end - // of the regular inlining logic, and only if necessary. - bool caller_is_single_block_loop = false; + // If the caller is a loop header and the callee has multiple blocks, then the + // normal inlining logic will place the OpLoopMerge in the last of several + // blocks in the loop. Instead, it should be placed at the end of the first + // block. We'll wait to move the OpLoopMerge until the end of the regular + // inlining logic, and only if necessary. bool caller_is_loop_header = false; - if (auto* loop_merge = call_block_itr->GetLoopMergeInst()) { + if (call_block_itr->GetLoopMergeInst()) { caller_is_loop_header = true; - caller_is_single_block_loop = - call_block_itr->id() == - loop_merge->GetSingleWordInOperand(kSpvLoopMergeContinueTargetIdInIdx); } bool callee_begins_with_structured_header = @@ -611,10 +605,6 @@ --loop_merge_itr; assert(loop_merge_itr->opcode() == SpvOpLoopMerge); std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context())); - if (caller_is_single_block_loop) { - // Also, update its continue target to point to the last block. - cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()}); - } first->tail().InsertBefore(std::move(cp_inst)); // Remove the loop merge from the last block.
diff --git a/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h b/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h index 51d7712..447871b 100644 --- a/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h +++ b/third_party/SPIRV-Tools/source/opt/inst_bindless_check_pass.h
@@ -28,18 +28,19 @@ // external design may change as the layer evolves. class InstBindlessCheckPass : public InstrumentPass { public: - // For test harness only - InstBindlessCheckPass() - : InstrumentPass(7, 23, kInstValidationIdBindless, 1), - input_length_enabled_(true), - input_init_enabled_(true) {} - // For all other interfaces + // Deprecated interface InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, bool input_length_enable, bool input_init_enable, uint32_t version) : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, version), input_length_enabled_(input_length_enable), input_init_enabled_(input_init_enable) {} + // Preferred Interface + InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, + bool input_length_enable, bool input_init_enable) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless), + input_length_enabled_(input_length_enable), + input_init_enabled_(input_init_enable) {} ~InstBindlessCheckPass() override = default;
diff --git a/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h b/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h index 9ad3528..67ffcc3 100644 --- a/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h +++ b/third_party/SPIRV-Tools/source/opt/inst_buff_addr_check_pass.h
@@ -28,13 +28,13 @@ // external design of this class may change as the layer evolves. class InstBuffAddrCheckPass : public InstrumentPass { public: - // For test harness only - InstBuffAddrCheckPass() - : InstrumentPass(7, 23, kInstValidationIdBuffAddr, 1) {} - // For all other interfaces + // Deprecated interface InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id, uint32_t version) : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr, version) {} + // Preferred interface + InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {} ~InstBuffAddrCheckPass() override = default;
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp index 2a3e7f0..dfcd164 100644 --- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp +++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -185,32 +185,12 @@ GetUintId(), SpvOpCompositeExtract, load_id, 1); Instruction* z_inst = builder->AddIdLiteralOp( GetUintId(), SpvOpCompositeExtract, load_id, 2); - if (version_ == 1) { - // For version 1 format, as a stopgap, pack uvec3 into first word: - // x << 21 | y << 10 | z. Second word is unused. (DEPRECATED) - Instruction* x_shft_inst = builder->AddBinaryOp( - GetUintId(), SpvOpShiftLeftLogical, x_inst->result_id(), - builder->GetUintConstantId(21)); - Instruction* y_shft_inst = builder->AddBinaryOp( - GetUintId(), SpvOpShiftLeftLogical, y_inst->result_id(), - builder->GetUintConstantId(10)); - Instruction* x_or_y_inst = builder->AddBinaryOp( - GetUintId(), SpvOpBitwiseOr, x_shft_inst->result_id(), - y_shft_inst->result_id()); - Instruction* x_or_y_or_z_inst = - builder->AddBinaryOp(GetUintId(), SpvOpBitwiseOr, - x_or_y_inst->result_id(), z_inst->result_id()); - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationId, - x_or_y_or_z_inst->result_id(), builder); - } else { - // For version 2 format, write all three words - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, - x_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, - y_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, - z_inst->result_id(), builder); - } + GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, + x_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, + y_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, + z_inst->result_id(), builder); } break; case SpvExecutionModelGeometry: { // Load and store PrimitiveId and InvocationId. @@ -231,30 +211,23 @@ kInstTessCtlOutPrimitiveId, base_offset_id, builder); } break; case SpvExecutionModelTessellationEvaluation: { - if (version_ == 1) { - // For format version 1, load and store InvocationId. - GenBuiltinOutputCode( - context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), - kInstTessOutInvocationId, base_offset_id, builder); - } else { - // For format version 2, load and store PrimitiveId and TessCoord.uv - GenBuiltinOutputCode( - context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), - kInstTessEvalOutPrimitiveId, base_offset_id, builder); - uint32_t load_id = GenVarLoad( - context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder); - Instruction* uvec3_cast_inst = - builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id); - uint32_t uvec3_cast_id = uvec3_cast_inst->result_id(); - Instruction* u_inst = builder->AddIdLiteralOp( - GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0); - Instruction* v_inst = builder->AddIdLiteralOp( - GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1); - GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, - u_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, - v_inst->result_id(), builder); - } + // Load and store PrimitiveId and TessCoord.uv + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), + kInstTessEvalOutPrimitiveId, base_offset_id, builder); + uint32_t load_id = GenVarLoad( + context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder); + Instruction* uvec3_cast_inst = + builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id); + uint32_t uvec3_cast_id = uvec3_cast_inst->result_id(); + Instruction* u_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0); + Instruction* v_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1); + GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, + u_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, + v_inst->result_id(), builder); } break; case SpvExecutionModelFragment: { // Load FragCoord and convert to Uint @@ -671,8 +644,7 @@ context(), &*new_blk_ptr, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); // Gen test if debug output buffer size will not be exceeded. - uint32_t val_spec_offset = - (version_ == 1) ? kInstStageOutCnt : kInst2StageOutCnt; + uint32_t val_spec_offset = kInstStageOutCnt; uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; uint32_t buf_id = GetOutputBufferId(); uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); @@ -892,6 +864,14 @@ } bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { + // Check that format version 2 requested + if (version_ != 2u) { + if (consumer()) { + std::string message = "Unsupported instrumentation format requested"; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return false; + } // Make sure all entry points have the same execution model. Do not // instrument if they do not. // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module @@ -905,12 +885,17 @@ for (auto& e : get_module()->entry_points()) { if (ecnt == 0) stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx); - else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != stage) + else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != + stage) { + if (consumer()) { + std::string message = "Mixed stage shader module not supported"; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } return false; + } ++ecnt; } - // Only supporting vertex, fragment and compute shaders at the moment. - // TODO(greg-lunarg): Handle all stages. + // Check for supported stages if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment && stage != SpvExecutionModelGeometry && stage != SpvExecutionModelGLCompute && @@ -920,8 +905,14 @@ stage != SpvExecutionModelIntersectionNV && stage != SpvExecutionModelAnyHitNV && stage != SpvExecutionModelClosestHitNV && - stage != SpvExecutionModelMissNV && stage != SpvExecutionModelCallableNV) + stage != SpvExecutionModelMissNV && + stage != SpvExecutionModelCallableNV) { + if (consumer()) { + std::string message = "Stage not supported by instrumentation"; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } return false; + } // Add together the roots of all entry points std::queue<uint32_t> roots; for (auto& e : get_module()->entry_points()) {
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.h b/third_party/SPIRV-Tools/source/opt/instrument_pass.h index ead3b73..02568fb 100644 --- a/third_party/SPIRV-Tools/source/opt/instrument_pass.h +++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
@@ -81,7 +81,16 @@ protected: // Create instrumentation pass for |validation_id| which utilizes descriptor // set |desc_set| for debug input and output buffers and writes |shader_id| - // into debug output records with format |version|. + // into debug output records. + InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id) + : Pass(), + desc_set_(desc_set), + shader_id_(shader_id), + validation_id_(validation_id), + version_(2u) {} + // Create instrumentation pass for |validation_id| which utilizes descriptor + // set |desc_set| for debug input and output buffers and writes |shader_id| + // into debug output records with format |version|. Deprecated. InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id, uint32_t version) : Pass(),
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.cpp b/third_party/SPIRV-Tools/source/opt/ir_context.cpp index d940180..7bca29b 100644 --- a/third_party/SPIRV-Tools/source/opt/ir_context.cpp +++ b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
@@ -273,6 +273,14 @@ } } + if (AreAnalysesValid(kAnalysisIdToFuncMapping)) { + for (auto& fn : *module_) { + if (id_to_func_[fn.result_id()] != &fn) { + return false; + } + } + } + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { for (auto& func : *module()) { for (auto& block : func) { @@ -818,6 +826,7 @@ roots->pop(); if (done.insert(fi).second) { Function* fn = GetFunction(fi); + assert(fn && "Trying to process a function that does not exist."); modified = pfn(fn) || modified; AddCalls(fn, roots); }
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp index c68b3e2..e21e680 100644 --- a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp +++ b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
@@ -16,6 +16,7 @@ #include <utility> +#include "source/ext_inst.h" #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" @@ -113,11 +114,14 @@ } else if (IsTypeInst(opcode)) { module_->AddType(std::move(spv_inst)); } else if (IsConstantInst(opcode) || opcode == SpvOpVariable || - opcode == SpvOpUndef) { + opcode == SpvOpUndef || + (opcode == SpvOpExtInst && + spvExtInstIsNonSemantic(inst->ext_inst_type))) { module_->AddGlobalValue(std::move(spv_inst)); } else { Errorf(consumer_, src, loc, - "Unhandled inst type (opcode: %d) found outside function definition.", + "Unhandled inst type (opcode: %d) found outside function " + "definition.", opcode); return false; }
diff --git a/third_party/SPIRV-Tools/source/opt/optimizer.cpp b/third_party/SPIRV-Tools/source/opt/optimizer.cpp index ece1abe..241aa75 100644 --- a/third_party/SPIRV-Tools/source/opt/optimizer.cpp +++ b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
@@ -198,33 +198,39 @@ .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateEliminateDeadFunctionsPass()) .RegisterPass(CreatePrivateToLocalPass()) - .RegisterPass(CreateScalarReplacementPass()) - .RegisterPass(CreateLocalAccessChainConvertPass()) - .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) - .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) - .RegisterPass(CreateSimplificationPass()) - .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateScalarReplacementPass(0)) .RegisterPass(CreateLocalMultiStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateIfConversionPass()) + .RegisterPass(CreateSimplificationPass()) .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateDeadBranchElimPass()) - .RegisterPass(CreateIfConversionPass()) - .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateBlockMergePass()) - .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCopyPropagateArraysPass()) + .RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateEliminateDeadMembersPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) .RegisterPass(CreateRedundancyEliminationPass()) - .RegisterPass(CreateCFGCleanupPass()) - .RegisterPass(CreateAggressiveDCEPass()); + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCFGCleanupPass()); } Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() { - return RegisterPass(CreateStripDebugInfoPass()) - .RegisterPass(CreateStripAtomicCounterMemoryPass()) + return RegisterPass(CreateStripAtomicCounterMemoryPass()) .RegisterPass(CreateGenerateWebGPUInitializersPass()) .RegisterPass(CreateLegalizeVectorShufflePass()) .RegisterPass(CreateSplitInvalidUnreachablePass())
diff --git a/third_party/SPIRV-Tools/source/opt/register_pressure.cpp b/third_party/SPIRV-Tools/source/opt/register_pressure.cpp index 34dac1d..cb24674 100644 --- a/third_party/SPIRV-Tools/source/opt/register_pressure.cpp +++ b/third_party/SPIRV-Tools/source/opt/register_pressure.cpp
@@ -78,9 +78,16 @@ // - Second, walk loop forest to propagate registers crossing back-edges // (add iterative values into the liveness set). void Compute() { - cfg_.ForEachBlockInPostOrder(&*function_->begin(), [this](BasicBlock* bb) { - ComputePartialLiveness(bb); - }); + for (BasicBlock& start_bb : *function_) { + if (reg_pressure_->Get(start_bb.id()) != nullptr) { + continue; + } + cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) { + if (reg_pressure_->Get(bb->id()) == nullptr) { + ComputePartialLiveness(bb); + } + }); + } DoLoopLivenessUnification(); EvaluateRegisterRequirements(); }
diff --git a/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp b/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp index 9e7fad0..936c966 100644 --- a/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp +++ b/third_party/SPIRV-Tools/source/opt/strip_debug_info_pass.cpp
@@ -19,12 +19,60 @@ namespace opt { Pass::Status StripDebugInfoPass::Process() { - bool modified = !context()->debugs1().empty() || - !context()->debugs2().empty() || - !context()->debugs3().empty(); + bool uses_non_semantic_info = false; + for (auto& inst : context()->module()->extensions()) { + const char* ext_name = + reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); + if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + uses_non_semantic_info = true; + } + } std::vector<Instruction*> to_kill; - for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg); + + // if we use non-semantic info, it may reference OpString. Do a more + // expensive pass checking the uses of the OpString to see if any are + // OpExtInst on a non-semantic instruction set. If we're not using the + // extension then we can do a simpler pass and kill all debug1 instructions + if (uses_non_semantic_info) { + for (auto& inst : context()->module()->debugs1()) { + switch (inst.opcode()) { + case SpvOpString: { + analysis::DefUseManager* def_use = context()->get_def_use_mgr(); + + // see if this string is used anywhere by a non-semantic instruction + bool no_nonsemantic_use = + def_use->WhileEachUser(&inst, [def_use](Instruction* use) { + if (use->opcode() == SpvOpExtInst) { + auto ext_inst_set = + def_use->GetDef(use->GetSingleWordInOperand(0u)); + const char* extension_name = reinterpret_cast<const char*>( + &ext_inst_set->GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + // found a non-semantic use, return false as we cannot + // remove this OpString + return false; + } + } + + // other instructions can't be a non-semantic use + return true; + }); + + if (no_nonsemantic_use) to_kill.push_back(&inst); + + break; + } + + default: + to_kill.push_back(&inst); + break; + } + } + } else { + for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg); + } + for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg); for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg); @@ -38,8 +86,11 @@ return false; }); + bool modified = !to_kill.empty(); + for (auto* inst : to_kill) context()->KillInst(inst); + // clear OpLine information context()->module()->ForEachInst([&modified](Instruction* inst) { modified |= !inst->dbg_line_insts().empty(); inst->dbg_line_insts().clear();
diff --git a/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp b/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp index 984073f..c231ead 100644 --- a/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp +++ b/third_party/SPIRV-Tools/source/opt/strip_reflect_info_pass.cpp
@@ -67,9 +67,54 @@ } else if (!other_uses_for_decorate_string && 0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) { to_remove.push_back(&inst); + } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + to_remove.push_back(&inst); } } + // clear all debug data now if it hasn't been cleared already, to remove any + // remaining OpString that may have been referenced by non-semantic extinsts + for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg); + for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg); + for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg); + + // remove any extended inst imports that are non semantic + std::unordered_set<uint32_t> non_semantic_sets; + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + non_semantic_sets.insert(inst.result_id()); + to_remove.push_back(&inst); + } + } + + // if we removed some non-semantic sets, then iterate over the instructions in + // the module to remove any OpExtInst that referenced those sets + if (!non_semantic_sets.empty()) { + context()->module()->ForEachInst( + [&non_semantic_sets, &to_remove](Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) != + non_semantic_sets.end()) { + to_remove.push_back(inst); + } + } + }); + } + + // OpName must come first, since they may refer to other debug instructions. + // If they are after the instructions that refer to, then they will be killed + // when that instruction is killed, which will lead to a double kill. + std::sort(to_remove.begin(), to_remove.end(), + [](Instruction* lhs, Instruction* rhs) -> bool { + if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) + return true; + return false; + }); + for (auto* inst : to_remove) { modified = true; context()->KillInst(inst);
diff --git a/third_party/SPIRV-Tools/source/opt/value_number_table.cpp b/third_party/SPIRV-Tools/source/opt/value_number_table.cpp index 8df34ef..82549a6 100644 --- a/third_party/SPIRV-Tools/source/opt/value_number_table.cpp +++ b/third_party/SPIRV-Tools/source/opt/value_number_table.cpp
@@ -93,7 +93,7 @@ // Phi nodes are a type of copy. If all of the inputs have the same value // number, then we can assign the result of the phi the same value number. - if (inst->opcode() == SpvOpPhi && + if (inst->opcode() == SpvOpPhi && inst->NumInOperands() > 0 && dec_mgr->HaveTheSameDecorations(inst->result_id(), inst->GetSingleWordInOperand(0))) { value = GetValueNumber(inst->GetSingleWordInOperand(0));
diff --git a/third_party/SPIRV-Tools/source/opt/wrap_opkill.h b/third_party/SPIRV-Tools/source/opt/wrap_opkill.h index 87a5d69..09f2dfa 100644 --- a/third_party/SPIRV-Tools/source/opt/wrap_opkill.h +++ b/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
@@ -34,8 +34,7 @@ IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId | - IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants | - IRContext::kAnalysisTypes; + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; } private:
diff --git a/third_party/SPIRV-Tools/source/print.cpp b/third_party/SPIRV-Tools/source/print.cpp index 1d9829d..128587a 100644 --- a/third_party/SPIRV-Tools/source/print.cpp +++ b/third_party/SPIRV-Tools/source/print.cpp
@@ -15,7 +15,8 @@ #include "source/print.h" #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ - defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN) + defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \ + defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA) namespace spvtools { clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp index ab8903e..b407f14 100644 --- a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp +++ b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
@@ -23,7 +23,8 @@ : has_random_seed(false), random_seed(0), replay_validation_enabled(false), - shrinker_step_limit(kDefaultStepLimit) {} + shrinker_step_limit(kDefaultStepLimit), + fuzzer_pass_validation_enabled(false) {} SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() { return new spv_fuzzer_options_t(); @@ -48,3 +49,8 @@ spv_fuzzer_options options, uint32_t shrinker_step_limit) { options->shrinker_step_limit = shrinker_step_limit; } + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options) { + options->fuzzer_pass_validation_enabled = true; +}
diff --git a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h index 7bb16c7..143f77f 100644 --- a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h +++ b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
@@ -34,6 +34,9 @@ // See spvFuzzerOptionsSetShrinkerStepLimit. uint32_t shrinker_step_limit; + + // See spvFuzzerOptionsValidateAfterEveryPass. + bool fuzzer_pass_validation_enabled; }; #endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/text.cpp b/third_party/SPIRV-Tools/source/text.cpp index d88d4f7..88a8e8f 100644 --- a/third_party/SPIRV-Tools/source/text.cpp +++ b/third_party/SPIRV-Tools/source/text.cpp
@@ -242,14 +242,37 @@ // The assembler accepts the symbolic name for an extended instruction, // and emits its corresponding number. spv_ext_inst_desc extInst; - if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst)) { - return context->diagnostic() - << "Invalid extended instruction name '" << textValue << "'."; - } - spvInstructionAddWord(pInst, extInst->ext_inst); + if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) == + SPV_SUCCESS) { + // if we know about this extended instruction, push the numeric value + spvInstructionAddWord(pInst, extInst->ext_inst); - // Prepare to parse the operands for the extended instructions. - spvPushOperandTypes(extInst->operandTypes, pExpectedOperands); + // Prepare to parse the operands for the extended instructions. + spvPushOperandTypes(extInst->operandTypes, pExpectedOperands); + } else { + // if we don't know this extended instruction and the set isn't + // non-semantic, we cannot process further + if (!spvExtInstIsNonSemantic(pInst->extInstType)) { + return context->diagnostic() + << "Invalid extended instruction name '" << textValue << "'."; + } else { + // for non-semantic instruction sets, as long as the text name is an + // integer value we can encode it since we know the form of all such + // extended instructions + spv_literal_t extInstValue; + if (spvTextToLiteral(textValue, &extInstValue) || + extInstValue.type != SPV_LITERAL_TYPE_UINT_32) { + return context->diagnostic() + << "Couldn't translate unknown extended instruction name '" + << textValue << "' to unsigned integer."; + } + + spvInstructionAddWord(pInst, extInstValue.value.u32); + + // opcode contains an unknown number of IDs. + pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } + } } break; case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { @@ -377,7 +400,8 @@ case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: - case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: { + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: { uint32_t value; if (grammar.parseMaskOperand(type, textValue, &value)) { return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.h b/third_party/SPIRV-Tools/source/val/basic_block.h index efbd243..876105c 100644 --- a/third_party/SPIRV-Tools/source/val/basic_block.h +++ b/third_party/SPIRV-Tools/source/val/basic_block.h
@@ -15,8 +15,8 @@ #ifndef SOURCE_VAL_BASIC_BLOCK_H_ #define SOURCE_VAL_BASIC_BLOCK_H_ -#include <cstdint> #include <bitset> +#include <cstdint> #include <functional> #include <memory> #include <vector> @@ -28,7 +28,7 @@ enum BlockType : uint32_t { kBlockTypeUndefined, - kBlockTypeHeader, + kBlockTypeSelection, kBlockTypeLoop, kBlockTypeMerge, kBlockTypeBreak,
diff --git a/third_party/SPIRV-Tools/source/val/function.cpp b/third_party/SPIRV-Tools/source/val/function.cpp index 876a608..0281770 100644 --- a/third_party/SPIRV-Tools/source/val/function.cpp +++ b/third_party/SPIRV-Tools/source/val/function.cpp
@@ -14,9 +14,8 @@ #include "source/val/function.h" -#include <cassert> - #include <algorithm> +#include <cassert> #include <sstream> #include <unordered_map> #include <unordered_set> @@ -99,7 +98,7 @@ spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { RegisterBlock(merge_id, false); BasicBlock& merge_block = blocks_.at(merge_id); - current_block_->set_type(kBlockTypeHeader); + current_block_->set_type(kBlockTypeSelection); merge_block.set_type(kBlockTypeMerge); merge_block_header_[&merge_block] = current_block_; @@ -344,7 +343,7 @@ BasicBlock* header = merge_block_header_[bb]; assert(header); block_depth_[bb] = GetBlockDepth(header); - } else if (bb_dom->is_type(kBlockTypeHeader) || + } else if (bb_dom->is_type(kBlockTypeSelection) || bb_dom->is_type(kBlockTypeLoop)) { // The dominator of the given block is a header block. So, the nesting // depth of this block is: 1 + nesting depth of the header.
diff --git a/third_party/SPIRV-Tools/source/val/instruction.h b/third_party/SPIRV-Tools/source/val/instruction.h index 1fa855f..e30bfbc 100644 --- a/third_party/SPIRV-Tools/source/val/instruction.h +++ b/third_party/SPIRV-Tools/source/val/instruction.h
@@ -21,6 +21,7 @@ #include <utility> #include <vector> +#include "source/ext_inst.h" #include "source/table.h" #include "spirv-tools/libspirv.h" @@ -85,6 +86,11 @@ return inst_.ext_inst_type; } + bool IsNonSemantic() const { + return opcode() == SpvOp::SpvOpExtInst && + spvExtInstIsNonSemantic(inst_.ext_inst_type); + } + // Casts the words belonging to the operand under |index| to |T| and returns. template <typename T> T GetOperandAs(size_t index) const {
diff --git a/third_party/SPIRV-Tools/source/val/validate_annotation.cpp b/third_party/SPIRV-Tools/source/val/validate_annotation.cpp index 2695280..df38f1b 100644 --- a/third_party/SPIRV-Tools/source/val/validate_annotation.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_annotation.cpp
@@ -286,7 +286,8 @@ auto use = pair.first; if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate && use->opcode() != SpvOpGroupMemberDecorate && - use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId) { + use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId && + !use->IsNonSemantic()) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Result id of OpDecorationGroup can only " << "be targeted by OpName, OpGroupDecorate, "
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp index 4801fc5..43a6af7 100644 --- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/val/validate.h" - #include <algorithm> #include <cassert> #include <functional> @@ -34,6 +32,7 @@ #include "source/val/basic_block.h" #include "source/val/construct.h" #include "source/val/function.h" +#include "source/val/validate.h" #include "source/val/validation_state.h" namespace spvtools { @@ -755,6 +754,26 @@ << header_name << " <ID> " << header->id(); } } + + if (block->is_type(BlockType::kBlockTypeSelection) || + block->is_type(BlockType::kBlockTypeLoop)) { + size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1; + const auto& merge_inst = _.ordered_instructions()[index]; + if (merge_inst.opcode() == SpvOpSelectionMerge || + merge_inst.opcode() == SpvOpLoopMerge) { + uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0); + auto merge_block = function->GetBlock(merge_id).first; + if (merge_block->reachable() && + !construct_blocks.count(merge_block)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "Header block " << _.getIdName(block->id()) + << " is contained in the " << construct_name + << " construct headed by " << _.getIdName(header->id()) + << ", but its merge block " << _.getIdName(merge_id) + << " is not"; + } + } + } } // Checks rules for case constructs.
diff --git a/third_party/SPIRV-Tools/source/val/validate_debug.cpp b/third_party/SPIRV-Tools/source/val/validate_debug.cpp index b49890f..0a25d8a 100644 --- a/third_party/SPIRV-Tools/source/val/validate_debug.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_debug.cpp
@@ -56,13 +56,6 @@ } // namespace spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) { - if (spvIsWebGPUEnv(_.context()->target_env) && - spvOpcodeIsDebug(inst->opcode())) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "Debugging instructions are not allowed in the WebGPU execution " - << "environment."; - } - switch (inst->opcode()) { case SpvOpMemberName: if (auto error = ValidateMemberName(_, inst)) return error;
diff --git a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp index d513a25..3b44833 100644 --- a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
@@ -1275,6 +1275,7 @@ const auto store = use.first; if (store->opcode() == SpvOpFConvert) continue; if (spvOpcodeIsDebug(store->opcode())) continue; + if (store->IsNonSemantic()) continue; if (spvOpcodeIsDecoration(store->opcode())) continue; if (store->opcode() != SpvOpStore) { return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
diff --git a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp index 1a64605..070cc4c 100644 --- a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
@@ -61,8 +61,8 @@ spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { + const auto name_id = 1; if (spvIsWebGPUEnv(_.context()->target_env)) { - const auto name_id = 1; const std::string name(reinterpret_cast<const char*>( inst->words().data() + inst->operands()[name_id].offset)); if (name != "GLSL.std.450") { @@ -72,6 +72,16 @@ } } + if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { + const std::string name(reinterpret_cast<const char*>( + inst->words().data() + inst->operands()[name_id].offset)); + if (name.find("NonSemantic.") == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NonSemantic extended instruction sets cannot be declared " + "without SPV_KHR_non_semantic_info."; + } + } + return SPV_SUCCESS; }
diff --git a/third_party/SPIRV-Tools/source/val/validate_function.cpp b/third_party/SPIRV-Tools/source/val/validate_function.cpp index b983194..f995ab3 100644 --- a/third_party/SPIRV-Tools/source/val/validate_function.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_function.cpp
@@ -87,7 +87,8 @@ for (auto& pair : inst->uses()) { const auto* use = pair.first; if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) == - acceptable.end()) { + acceptable.end() && + !use->IsNonSemantic()) { return _.diag(SPV_ERROR_INVALID_ID, use) << "Invalid use of function result id " << _.getIdName(inst->id()) << ".";
diff --git a/third_party/SPIRV-Tools/source/val/validate_id.cpp b/third_party/SPIRV-Tools/source/val/validate_id.cpp index d80e1b8..7406330 100644 --- a/third_party/SPIRV-Tools/source/val/validate_id.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_id.cpp
@@ -167,7 +167,8 @@ const auto opcode = inst->opcode(); if (spvOpcodeGeneratesType(def->opcode()) && !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && - !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction && + !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && + opcode != SpvOpFunction && opcode != SpvOpCooperativeMatrixLengthNV && !(opcode == SpvOpSpecConstantOp && inst->word(3) == SpvOpCooperativeMatrixLengthNV)) { @@ -175,7 +176,7 @@ << "Operand " << _.getIdName(operand_word) << " cannot be a type"; } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && - !spvOpcodeIsDebug(opcode) && + !spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi && opcode != SpvOpExtInst && opcode != SpvOpExtInstImport && @@ -187,6 +188,11 @@ return _.diag(SPV_ERROR_INVALID_ID, inst) << "Operand " << _.getIdName(operand_word) << " requires a type"; + } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " in semantic instruction cannot be a non-semantic " + "instruction"; } else { ret = SPV_SUCCESS; }
diff --git a/third_party/SPIRV-Tools/source/val/validate_layout.cpp b/third_party/SPIRV-Tools/source/val/validate_layout.cpp index 53c2835..259befe 100644 --- a/third_party/SPIRV-Tools/source/val/validate_layout.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_layout.cpp
@@ -34,6 +34,30 @@ // checked. spv_result_t ModuleScopedInstructions(ValidationState_t& _, const Instruction* inst, SpvOp opcode) { + switch (opcode) { + case SpvOpExtInst: + if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but since they must name a return type they cannot be the + // first instruction in the types section. Therefore check that we are + // already in it. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } + } else { + // otherwise they must be used in a block + if (_.current_layout_section() < kLayoutFunctionDefinitions) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + default: + break; + } + while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) { _.ProgressToNextLayoutSectionOrder(); @@ -144,6 +168,29 @@ } break; + case SpvOpExtInst: + if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but must either be placed outside a function declaration, + // or inside a block. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } else if (_.in_function_body() && _.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst within function definition must " + "appear in a block"; + } + } else { + // otherwise they must be used in a block + if (_.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + default: if (_.current_layout_section() == kLayoutFunctionDeclarations && _.in_function_body()) {
diff --git a/third_party/SPIRV-Tools/source/val/validate_type.cpp b/third_party/SPIRV-Tools/source/val/validate_type.cpp index 4d673b4..5924c69 100644 --- a/third_party/SPIRV-Tools/source/val/validate_type.cpp +++ b/third_party/SPIRV-Tools/source/val/validate_type.cpp
@@ -19,29 +19,40 @@ #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" +#include "spirv/unified1/spirv.h" namespace spvtools { namespace val { namespace { -// True if the integer constant is > 0. |const_words| are words of the -// constant-defining instruction (either OpConstant or -// OpSpecConstant). typeWords are the words of the constant's-type-defining -// OpTypeInt. -bool AboveZero(const std::vector<uint32_t>& const_words, - const std::vector<uint32_t>& type_words) { - const uint32_t width = type_words[2]; - const bool is_signed = type_words[3] > 0; +// Returns, as an int64_t, the literal value from an OpConstant or the +// default value of an OpSpecConstant, assuming it is an integral type. +// For signed integers, relies the rule that literal value is sign extended +// to fill out to word granularity. Assumes that the constant value +// has +int64_t ConstantLiteralAsInt64(uint32_t width, + const std::vector<uint32_t>& const_words) { const uint32_t lo_word = const_words[3]; - if (width > 32) { - // The spec currently doesn't allow integers wider than 64 bits. - const uint32_t hi_word = const_words[4]; // Must exist, per spec. - if (is_signed && (hi_word >> 31)) return false; - return (lo_word | hi_word) > 0; - } else { - if (is_signed && (lo_word >> 31)) return false; - return lo_word > 0; - } + if (width <= 32) return int32_t(lo_word); + assert(width <= 64); + assert(const_words.size() > 4); + const uint32_t hi_word = const_words[4]; // Must exist, per spec. + return static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32); +} + +// Returns, as an uint64_t, the literal value from an OpConstant or the +// default value of an OpSpecConstant, assuming it is an integral type. +// For signed integers, relies the rule that literal value is sign extended +// to fill out to word granularity. Assumes that the constant value +// has +int64_t ConstantLiteralAsUint64(uint32_t width, + const std::vector<uint32_t>& const_words) { + const uint32_t lo_word = const_words[3]; + if (width <= 32) return lo_word; + assert(width <= 64); + assert(const_words.size() > 4); + const uint32_t hi_word = const_words[4]; // Must exist, per spec. + return (uint64_t(lo_word) | uint64_t(hi_word) << 32); } // Validates that type declarations are unique, unless multiple declarations @@ -258,14 +269,33 @@ switch (length->opcode()) { case SpvOpSpecConstant: - case SpvOpConstant: - if (AboveZero(length->words(), const_result_type->words())) break; - // Else fall through! - case SpvOpConstantNull: { + case SpvOpConstant: { + auto& type_words = const_result_type->words(); + const bool is_signed = type_words[3] > 0; + const uint32_t width = type_words[2]; + const int64_t ivalue = ConstantLiteralAsInt64(width, length->words()); + if (ivalue == 0 || (ivalue < 0 && is_signed)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length <id> '" << _.getIdName(length_id) + << "' default value must be at least 1: found " << ivalue; + } + if (spvIsWebGPUEnv(_.context()->target_env)) { + // WebGPU has maximum integer width of 32 bits, and max array size + // is one more than the max signed integer representation. + const uint64_t max_permitted = (uint64_t(1) << 31); + const uint64_t uvalue = ConstantLiteralAsUint64(width, length->words()); + if (uvalue > max_permitted) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length <id> '" << _.getIdName(length_id) + << "' size exceeds max value " << max_permitted + << " permitted by WebGPU: got " << uvalue; + } + } + } break; + case SpvOpConstantNull: return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypeArray Length <id> '" << _.getIdName(length_id) << "' default value must be at least 1."; - } case SpvOpSpecConstantOp: // Assume it's OK, rather than try to evaluate the operation. break; @@ -506,7 +536,7 @@ for (auto& pair : inst->uses()) { const auto* use = pair.first; if (use->opcode() != SpvOpFunction && !spvOpcodeIsDebug(use->opcode()) && - !spvOpcodeIsDecoration(use->opcode())) { + !use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, use) << "Invalid use of function type result id " << _.getIdName(inst->id()) << ".";
diff --git a/third_party/SPIRV-Tools/source/val/validation_state.cpp b/third_party/SPIRV-Tools/source/val/validation_state.cpp index 20eaf88..51aebbe 100644 --- a/third_party/SPIRV-Tools/source/val/validation_state.cpp +++ b/third_party/SPIRV-Tools/source/val/validation_state.cpp
@@ -93,6 +93,9 @@ case SpvOpLine: case SpvOpNoLine: case SpvOpUndef: + // SpvOpExtInst is only allowed here for certain extended instruction + // sets. This will be checked separately + case SpvOpExtInst: out = true; break; default: break;
diff --git a/third_party/SPIRV-Tools/test/CMakeLists.txt b/third_party/SPIRV-Tools/test/CMakeLists.txt index 3dca430..70999f9 100644 --- a/third_party/SPIRV-Tools/test/CMakeLists.txt +++ b/third_party/SPIRV-Tools/test/CMakeLists.txt
@@ -100,8 +100,10 @@ diagnostic_test.cpp enum_string_mapping_test.cpp enum_set_test.cpp + ext_inst.cldebug100_test.cpp ext_inst.debuginfo_test.cpp ext_inst.glsl_test.cpp + ext_inst.non_semantic_test.cpp ext_inst.opencl_test.cpp fix_word_test.cpp generator_magic_number_test.cpp
diff --git a/third_party/SPIRV-Tools/test/ext_inst.cldebug100_test.cpp b/third_party/SPIRV-Tools/test/ext_inst.cldebug100_test.cpp new file mode 100644 index 0000000..4f1e106 --- /dev/null +++ b/third_party/SPIRV-Tools/test/ext_inst.cldebug100_test.cpp
@@ -0,0 +1,1070 @@ +// Copyright (c) 2017-2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> +#include <vector> + +#include "OpenCLDebugInfo100.h" +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "spirv/unified1/spirv.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +// This file tests the correctness of encoding and decoding of instructions +// involving the OpenCL.DebugInfo.100 extended instruction set. +// Validation is not checked here. + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using testing::Eq; +using utils::MakeVector; + +// Test values of enums vs. what is written in the spec. + +TEST(ExtInstCLDebugInfo, InstructionValues) { + EXPECT_EQ(0, OpenCLDebugInfo100DebugInfoNone); + EXPECT_EQ(1, OpenCLDebugInfo100DebugCompilationUnit); + EXPECT_EQ(2, OpenCLDebugInfo100DebugTypeBasic); + EXPECT_EQ(3, OpenCLDebugInfo100DebugTypePointer); + EXPECT_EQ(4, OpenCLDebugInfo100DebugTypeQualifier); + EXPECT_EQ(5, OpenCLDebugInfo100DebugTypeArray); + EXPECT_EQ(6, OpenCLDebugInfo100DebugTypeVector); + EXPECT_EQ(7, OpenCLDebugInfo100DebugTypedef); + EXPECT_EQ(8, OpenCLDebugInfo100DebugTypeFunction); + EXPECT_EQ(9, OpenCLDebugInfo100DebugTypeEnum); + EXPECT_EQ(10, OpenCLDebugInfo100DebugTypeComposite); + EXPECT_EQ(11, OpenCLDebugInfo100DebugTypeMember); + EXPECT_EQ(12, OpenCLDebugInfo100DebugTypeInheritance); + EXPECT_EQ(13, OpenCLDebugInfo100DebugTypePtrToMember); + EXPECT_EQ(14, OpenCLDebugInfo100DebugTypeTemplate); + EXPECT_EQ(15, OpenCLDebugInfo100DebugTypeTemplateParameter); + EXPECT_EQ(16, OpenCLDebugInfo100DebugTypeTemplateTemplateParameter); + EXPECT_EQ(17, OpenCLDebugInfo100DebugTypeTemplateParameterPack); + EXPECT_EQ(18, OpenCLDebugInfo100DebugGlobalVariable); + EXPECT_EQ(19, OpenCLDebugInfo100DebugFunctionDeclaration); + EXPECT_EQ(20, OpenCLDebugInfo100DebugFunction); + EXPECT_EQ(21, OpenCLDebugInfo100DebugLexicalBlock); + EXPECT_EQ(22, OpenCLDebugInfo100DebugLexicalBlockDiscriminator); + EXPECT_EQ(23, OpenCLDebugInfo100DebugScope); + EXPECT_EQ(24, OpenCLDebugInfo100DebugNoScope); + EXPECT_EQ(25, OpenCLDebugInfo100DebugInlinedAt); + EXPECT_EQ(26, OpenCLDebugInfo100DebugLocalVariable); + EXPECT_EQ(27, OpenCLDebugInfo100DebugInlinedVariable); + EXPECT_EQ(28, OpenCLDebugInfo100DebugDeclare); + EXPECT_EQ(29, OpenCLDebugInfo100DebugValue); + EXPECT_EQ(30, OpenCLDebugInfo100DebugOperation); + EXPECT_EQ(31, OpenCLDebugInfo100DebugExpression); + EXPECT_EQ(32, OpenCLDebugInfo100DebugMacroDef); + EXPECT_EQ(33, OpenCLDebugInfo100DebugMacroUndef); + EXPECT_EQ(34, OpenCLDebugInfo100DebugImportedEntity); + EXPECT_EQ(35, OpenCLDebugInfo100DebugSource); +} + +TEST(ExtInstCLDebugInfo, InfoFlagValues) { + EXPECT_EQ(1 << 0, OpenCLDebugInfo100FlagIsProtected); + EXPECT_EQ(1 << 1, OpenCLDebugInfo100FlagIsPrivate); + EXPECT_EQ(((1 << 0) | (1 << 1)), OpenCLDebugInfo100FlagIsPublic); + EXPECT_EQ(1 << 2, OpenCLDebugInfo100FlagIsLocal); + EXPECT_EQ(1 << 3, OpenCLDebugInfo100FlagIsDefinition); + EXPECT_EQ(1 << 4, OpenCLDebugInfo100FlagFwdDecl); + EXPECT_EQ(1 << 5, OpenCLDebugInfo100FlagArtificial); + EXPECT_EQ(1 << 6, OpenCLDebugInfo100FlagExplicit); + EXPECT_EQ(1 << 7, OpenCLDebugInfo100FlagPrototyped); + EXPECT_EQ(1 << 8, OpenCLDebugInfo100FlagObjectPointer); + EXPECT_EQ(1 << 9, OpenCLDebugInfo100FlagStaticMember); + EXPECT_EQ(1 << 10, OpenCLDebugInfo100FlagIndirectVariable); + EXPECT_EQ(1 << 11, OpenCLDebugInfo100FlagLValueReference); + EXPECT_EQ(1 << 12, OpenCLDebugInfo100FlagRValueReference); + EXPECT_EQ(1 << 13, OpenCLDebugInfo100FlagIsOptimized); + EXPECT_EQ(1 << 14, OpenCLDebugInfo100FlagIsEnumClass); + EXPECT_EQ(1 << 15, OpenCLDebugInfo100FlagTypePassByValue); + EXPECT_EQ(1 << 16, OpenCLDebugInfo100FlagTypePassByReference); +} + +TEST(ExtInstCLDebugInfo, BaseTypeAttributeEndodingValues) { + EXPECT_EQ(0, OpenCLDebugInfo100Unspecified); + EXPECT_EQ(1, OpenCLDebugInfo100Address); + EXPECT_EQ(2, OpenCLDebugInfo100Boolean); + EXPECT_EQ(3, OpenCLDebugInfo100Float); + EXPECT_EQ(4, OpenCLDebugInfo100Signed); + EXPECT_EQ(5, OpenCLDebugInfo100SignedChar); + EXPECT_EQ(6, OpenCLDebugInfo100Unsigned); + EXPECT_EQ(7, OpenCLDebugInfo100UnsignedChar); +} + +TEST(ExtInstCLDebugInfo, CompositeTypeValues) { + EXPECT_EQ(0, OpenCLDebugInfo100Class); + EXPECT_EQ(1, OpenCLDebugInfo100Structure); + EXPECT_EQ(2, OpenCLDebugInfo100Union); +} + +TEST(ExtInstCLDebugInfo, TypeQualifierValues) { + EXPECT_EQ(0, OpenCLDebugInfo100ConstType); + EXPECT_EQ(1, OpenCLDebugInfo100VolatileType); + EXPECT_EQ(2, OpenCLDebugInfo100RestrictType); + EXPECT_EQ(3, OpenCLDebugInfo100AtomicType); +} + +TEST(ExtInstCLDebugInfo, DebugOperationValues) { + EXPECT_EQ(0, OpenCLDebugInfo100Deref); + EXPECT_EQ(1, OpenCLDebugInfo100Plus); + EXPECT_EQ(2, OpenCLDebugInfo100Minus); + EXPECT_EQ(3, OpenCLDebugInfo100PlusUconst); + EXPECT_EQ(4, OpenCLDebugInfo100BitPiece); + EXPECT_EQ(5, OpenCLDebugInfo100Swap); + EXPECT_EQ(6, OpenCLDebugInfo100Xderef); + EXPECT_EQ(7, OpenCLDebugInfo100StackValue); + EXPECT_EQ(8, OpenCLDebugInfo100Constu); + EXPECT_EQ(9, OpenCLDebugInfo100Fragment); +} + +TEST(ExtInstCLDebugInfo, ImportedEntityValues) { + EXPECT_EQ(0, OpenCLDebugInfo100ImportedModule); + EXPECT_EQ(1, OpenCLDebugInfo100ImportedDeclaration); +} + +// Test round trip through assembler and disassembler. + +struct InstructionCase { + uint32_t opcode; + std::string name; + std::string operands; + std::vector<uint32_t> expected_operands; +}; + +using ExtInstCLDebugInfo100RoundTripTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>; +using ExtInstCLDebugInfo100RoundTripTestExplicit = spvtest::TextToBinaryTest; + +TEST_P(ExtInstCLDebugInfo100RoundTripTest, ParameterizedExtInst) { + const std::string input = + "%1 = OpExtInstImport \"OpenCL.DebugInfo.100\"\n" + "%3 = OpExtInst %2 %1 " + + GetParam().name + GetParam().operands + "\n"; + // First make sure it assembles correctly. + std::cout << input << std::endl; + EXPECT_THAT(CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("OpenCL.DebugInfo.100")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode}, + GetParam().expected_operands)}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input; +} + +#define EPREFIX "Debug" + +#define CASE_0(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, "", {} \ + } + +#define CASE_ILL(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #L0 " " #L1, { \ + 4, L0, L1 \ + } \ + } + +#define CASE_IL(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0, { \ + 4, L0 \ + } \ + } + +#define CASE_I(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4", { 4 } \ + } + +#define CASE_II(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5", { 4, 5 } \ + } + +#define CASE_III(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6", { \ + 4, 5, 6 \ + } \ + } + +#define CASE_IIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7", { \ + 4, 5, 6, 7 \ + } \ + } + +#define CASE_IIIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7 %8", \ + { \ + 4, 5, 6, 7, 8 \ + } \ + } + +#define CASE_IIIIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 %8 %9", { \ + 4, 5, 6, 7, 8, 9 \ + } \ + } + +#define CASE_IIIIIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 %8 %9 %10", { \ + 4, 5, 6, 7, 8, 9, 10 \ + } \ + } + +#define CASE_IIILLI(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7", { \ + 4, 5, 6, L0, L1, 7 \ + } \ + } + +#define CASE_IIILLIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr, { \ + 4, 5, 6, L0, L1, 7, Fnum \ + } \ + } + +#define CASE_IIILLIFL(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr " " #L2, { \ + 4, 5, 6, L0, L1, 7, Fnum, L2 \ + } \ + } + +#define CASE_IIILLIL(Enum, L0, L1, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " #L2, { \ + 4, 5, 6, L0, L1, 7, L2 \ + } \ + } + +#define CASE_IE(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #E0, { \ + 4, uint32_t(OpenCLDebugInfo100##E0) \ + } \ + } + +#define CASE_IEIILLI(Enum, E0, L1, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 %6 " #L1 " " #L2 " %7", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, 6, L1, L2, 7 \ + } \ + } + +#define CASE_IIE(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 " #E0, { \ + 4, 5, uint32_t(OpenCLDebugInfo100##E0) \ + } \ + } + +#define CASE_ISF(Enum, S0, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #S0 " " Fstr, { \ + 4, uint32_t(SpvStorageClass##S0), Fnum \ + } \ + } + +#define CASE_LII(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #L0 " %4 %5", \ + { \ + L0, 4, 5 \ + } \ + } + +#define CASE_LLIe(Enum, L0, L1, RawEnumName, RawEnumValue) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #L0 " " #L1 " %4 " RawEnumName, { \ + L0, L1, 4, RawEnumValue \ + } \ + } + +#define CASE_ILI(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0 " %5", \ + { \ + 4, L0, 5 \ + } \ + } + +#define CASE_ILII(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #L0 " %5 %6", { \ + 4, L0, 5, 6 \ + } \ + } + +#define CASE_ILLII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #L0 " " #L1 " %5 %6", { \ + 4, L0, L1, 5, 6 \ + } \ + } + +#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, { \ + 4, 5, 6, L0, L1, 7, 8, Fnum \ + } \ + } + +#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10 \ + } \ + } + +#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12 \ + } \ + } + +#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14 \ + } \ + } + +#define CASE_IEILLIIIF(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr, { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum \ + } \ + } + +#define CASE_IEILLIIIFI(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9 \ + } \ + } + +#define CASE_IEILLIIIFII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10 \ + } \ + } + +#define CASE_IEILLIIIFIII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, 11 \ + } \ + } + +#define CASE_IEILLIIIFIIII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11 %12", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, \ + 11, 12 \ + } \ + } + +#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, { \ + 4, 5, 6, L0, L1, 7, 8, 9, Fnum \ + } \ + } + +#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \ + 4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10 \ + } \ + } + +#define CASE_IIIIF(Enum, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 " Fstr, { \ + 4, 5, 6, 7, Fnum \ + } \ + } + +#define CASE_IIILL(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1, { \ + 4, 5, 6, L0, L1 \ + } \ + } + +#define CASE_IIIILL(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 " #L0 " " #L1, { \ + 4, 5, 6, 7, L0, L1 \ + } \ + } + +#define CASE_IILLI(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6", { \ + 4, 5, L0, L1, 6 \ + } \ + } + +#define CASE_IILLII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7", { \ + 4, 5, L0, L1, 6, 7 \ + } \ + } + +#define CASE_IILLIII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7 %8", { \ + 4, 5, L0, L1, 6, 7, 8 \ + } \ + } + +#define CASE_IILLIIII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", { \ + 4, 5, L0, L1, 6, 7, 8, 9 \ + } \ + } + +#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9 \ + } \ + } + +#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10 \ + } \ + } + +#define CASE_E(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0, { \ + uint32_t(OpenCLDebugInfo100##E0) \ + } \ + } + +#define CASE_EI(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4", { \ + uint32_t(OpenCLDebugInfo100##E0), 4 \ + } \ + } + +#define CASE_EII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4 %5", \ + { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5 \ + } \ + } + +#define CASE_EIII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " %4 %5 %6", { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6 \ + } \ + } + +#define CASE_EIIII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " %4 %5 %6 %7", { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7 \ + } \ + } + +#define CASE_EIIIII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " %4 %5 %6 %7 %8", { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7, 8 \ + } \ + } + +#define CASE_EL(Enum, E0, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " " #L0, { \ + uint32_t(OpenCLDebugInfo100##E0), L0 \ + } \ + } + +#define CASE_ELL(Enum, E0, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " " #L0 " " #L1, { \ + uint32_t(OpenCLDebugInfo100##E0), L0, L1 \ + } \ + } + +// OpenCL.DebugInfo.100 4.1 Missing Debugging Information +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInfoNone, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_0(InfoNone), // enum value 0 + }))); + +// OpenCL.DebugInfo.100 4.2 Compilation Unit +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugCompilationUnit, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_LLIe(CompilationUnit, 100, 42, "HLSL", SpvSourceLanguageHLSL), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + // TODO(dneto): Should this be a list of sourc texts, + // to accomodate length limits? + CASE_I(Source), + CASE_II(Source), + }))); + +// OpenCL.DebugInfo.100 4.3 Type instructions +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeBasic, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIE(TypeBasic, Unspecified), + CASE_IIE(TypeBasic, Address), + CASE_IIE(TypeBasic, Boolean), + CASE_IIE(TypeBasic, Float), + CASE_IIE(TypeBasic, Signed), + CASE_IIE(TypeBasic, SignedChar), + CASE_IIE(TypeBasic, Unsigned), + CASE_IIE(TypeBasic, UnsignedChar), + }))); + +// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same +// as the bitwise-OR of FlagIsProtected and FlagIsPrivate. +// The disassembler will emit the compound expression instead. +// There is no simple fix for this. This enum is not really a mask +// for the bottom two bits. +TEST_F(ExtInstCLDebugInfo100RoundTripTestExplicit, FlagIsPublic) { + const std::string prefix = + "%1 = OpExtInstImport \"DebugInfo\"\n" + "%3 = OpExtInst %2 %1 DebugTypePointer %4 Private "; + const std::string input = prefix + "FlagIsPublic\n"; + const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n"; + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")), + MakeInstruction(SpvOpExtInst, + {2, 3, 1, OpenCLDebugInfo100DebugTypePointer, 4, + uint32_t(SpvStorageClassPrivate), + OpenCLDebugInfo100FlagIsPublic})}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input; +} + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypePointer, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + + //// Use each flag independently. + CASE_ISF(TypePointer, Private, "FlagIsProtected", + uint32_t(OpenCLDebugInfo100FlagIsProtected)), + CASE_ISF(TypePointer, Private, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + + // FlagIsPublic is tested above. + + CASE_ISF(TypePointer, Private, "FlagIsLocal", + uint32_t(OpenCLDebugInfo100FlagIsLocal)), + CASE_ISF(TypePointer, Private, "FlagIsDefinition", + uint32_t(OpenCLDebugInfo100FlagIsDefinition)), + CASE_ISF(TypePointer, Private, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + CASE_ISF(TypePointer, Private, "FlagArtificial", + uint32_t(OpenCLDebugInfo100FlagArtificial)), + CASE_ISF(TypePointer, Private, "FlagExplicit", + uint32_t(OpenCLDebugInfo100FlagExplicit)), + CASE_ISF(TypePointer, Private, "FlagPrototyped", + uint32_t(OpenCLDebugInfo100FlagPrototyped)), + CASE_ISF(TypePointer, Private, "FlagObjectPointer", + uint32_t(OpenCLDebugInfo100FlagObjectPointer)), + CASE_ISF(TypePointer, Private, "FlagStaticMember", + uint32_t(OpenCLDebugInfo100FlagStaticMember)), + CASE_ISF(TypePointer, Private, "FlagIndirectVariable", + uint32_t(OpenCLDebugInfo100FlagIndirectVariable)), + CASE_ISF(TypePointer, Private, "FlagLValueReference", + uint32_t(OpenCLDebugInfo100FlagLValueReference)), + CASE_ISF(TypePointer, Private, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_ISF(TypePointer, Private, "FlagIsEnumClass", + uint32_t(OpenCLDebugInfo100FlagIsEnumClass)), + CASE_ISF(TypePointer, Private, "FlagTypePassByValue", + uint32_t(OpenCLDebugInfo100FlagTypePassByValue)), + CASE_ISF(TypePointer, Private, "FlagTypePassByReference", + uint32_t(OpenCLDebugInfo100FlagTypePassByReference)), + + //// Use flags in combination, and try different storage classes. + CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsProtected) | + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_ISF( + TypePointer, Workgroup, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl) | + uint32_t(OpenCLDebugInfo100FlagIndirectVariable) | + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeQualifier, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IE(TypeQualifier, ConstType), + CASE_IE(TypeQualifier, VolatileType), + CASE_IE(TypeQualifier, RestrictType), + CASE_IE(TypeQualifier, AtomicType), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeArray, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_II(TypeArray), + CASE_III(TypeArray), + CASE_IIII(TypeArray), + CASE_IIIII(TypeArray), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeVector, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IL(TypeVector, 2), + CASE_IL(TypeVector, 3), + CASE_IL(TypeVector, 4), + CASE_IL(TypeVector, 16), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypedef, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLI(Typedef, 12, 13), + CASE_IIILLI(Typedef, 14, 99), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeFunction, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_EI(TypeFunction, FlagIsProtected), + CASE_EII(TypeFunction, FlagIsDefinition), + CASE_EIII(TypeFunction, FlagArtificial), + CASE_EIIII(TypeFunction, FlagExplicit), + CASE_EIIIII(TypeFunction, FlagIsPrivate), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeEnum, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLIIFII( + TypeEnum, 12, 13, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl) | + uint32_t(OpenCLDebugInfo100FlagIndirectVariable) | + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember", + uint32_t(OpenCLDebugInfo100FlagStaticMember)), + CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember", + uint32_t(OpenCLDebugInfo100FlagStaticMember)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeComposite, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IEILLIIIF( + TypeComposite, Class, 12, 13, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl) | + uint32_t(OpenCLDebugInfo100FlagIndirectVariable) | + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + // Cover all tag values: Class, Structure, Union + CASE_IEILLIIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + // Now add members + CASE_IEILLIIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeMember, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IIILLIIIF(TypeMember, 99, 100, "FlagIsPrivate|FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + // Add the optional Id argument. + CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeInheritance, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIIIF(TypeInheritance, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypePtrToMember, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_II(TypePtrToMember), + }))); + +// OpenCL.DebugInfo.100 4.4 Templates + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplate, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_II(TypeTemplate), + CASE_III(TypeTemplate), + CASE_IIII(TypeTemplate), + CASE_IIIII(TypeTemplate), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameter, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIIILL(TypeTemplateParameter, 1, 2), + CASE_IIIILL(TypeTemplateParameter, 99, 102), + CASE_IIIILL(TypeTemplateParameter, 10, 7), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateTemplateParameter, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILL(TypeTemplateTemplateParameter, 1, 2), + CASE_IIILL(TypeTemplateTemplateParameter, 99, 102), + CASE_IIILL(TypeTemplateTemplateParameter, 10, 7), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameterPack, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IILLI(TypeTemplateParameterPack, 1, 2), + CASE_IILLII(TypeTemplateParameterPack, 99, 102), + CASE_IILLIII(TypeTemplateParameterPack, 10, 7), + CASE_IILLIIII(TypeTemplateParameterPack, 10, 7), + }))); + +// OpenCL.DebugInfo.100 4.5 Global Variables + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugGlobalVariable, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + }))); + +// OpenCL.DebugInfo.100 4.6 Functions + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugFunctionDeclaration, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugFunction, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3), + CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44), + // Add the optional declaration Id. + CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3), + CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44), + }))); + +// OpenCL.DebugInfo.100 4.7 Local Information + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlock, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_ILLII(LexicalBlock, 1, 2), + CASE_ILLII(LexicalBlock, 42, 43), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlockDiscriminator, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_ILI(LexicalBlockDiscriminator, 1), + CASE_ILI(LexicalBlockDiscriminator, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugScope, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_I(Scope), + CASE_II(Scope), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugNoScope, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_0(NoScope), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedAt, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_LII(InlinedAt, 1), + CASE_LII(InlinedAt, 42), + }))); + +// OpenCL.DebugInfo.100 4.8 Local Variables + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugLocalVariable, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIILLIF(LocalVariable, 1, 2, "FlagIsPrivate", + OpenCLDebugInfo100FlagIsPrivate), + CASE_IIILLIF(LocalVariable, 4, 5, "FlagIsProtected", + OpenCLDebugInfo100FlagIsProtected), + CASE_IIILLIFL(LocalVariable, 9, 99, "FlagIsProtected", + OpenCLDebugInfo100FlagIsProtected, 195), + CASE_IIILLIFL(LocalVariable, 19, 199, "FlagIsPrivate", + OpenCLDebugInfo100FlagIsPrivate, 195), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedVariable, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_II(InlinedVariable), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugDeclare, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_III(Declare), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugDebugValue, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_IIII(Value), + CASE_IIIII(Value), + CASE_IIIIII(Value), + // Test up to 3 id parameters. We can always try more. + CASE_IIIIIII(Value), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugOperation, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_E(Operation, Deref), + CASE_E(Operation, Plus), + CASE_E(Operation, Minus), + CASE_EL(Operation, PlusUconst, 1), + CASE_EL(Operation, PlusUconst, 42), + CASE_ELL(Operation, BitPiece, 1, 2), + CASE_ELL(Operation, BitPiece, 4, 5), + CASE_E(Operation, Swap), + CASE_E(Operation, Xderef), + CASE_E(Operation, StackValue), + CASE_EL(Operation, Constu, 1), + CASE_EL(Operation, Constu, 42), + CASE_ELL(Operation, Fragment, 100, 200), + CASE_ELL(Operation, Fragment, 8, 9), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugExpression, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_0(Expression), + CASE_I(Expression), + CASE_II(Expression), + CASE_III(Expression), + CASE_IIII(Expression), + CASE_IIIII(Expression), + CASE_IIIIII(Expression), + CASE_IIIIIII(Expression), + }))); + +// OpenCL.DebugInfo.100 4.9 Macros + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroDef, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_ILI(MacroDef, 1), + CASE_ILI(MacroDef, 42), + CASE_ILII(MacroDef, 1), + CASE_ILII(MacroDef, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroUndef, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + CASE_ILI(MacroUndef, 1), + CASE_ILI(MacroUndef, 42), + }))); + +// OpenCL.DebugInfo.100 4.10 Imported Entities + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugImportedEntity, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector<InstructionCase>({ + // ID Name + // Literal Tag + // ID Source + // ID Entity + // Literal Number Line + // Literal Number Column + // ID Parent + CASE_IEIILLI(ImportedEntity, ImportedModule, 67, 68), + CASE_IEIILLI(ImportedEntity, ImportedDeclaration, 42, 43), + }))); + +#undef EPREFIX +#undef CASE_0 +#undef CASE_ILL +#undef CASE_IL +#undef CASE_I +#undef CASE_II +#undef CASE_III +#undef CASE_IIII +#undef CASE_IIIII +#undef CASE_IIIIII +#undef CASE_IIIIIII +#undef CASE_IIILLI +#undef CASE_IIILLIL +#undef CASE_IE +#undef CASE_IEIILLI +#undef CASE_IIE +#undef CASE_ISF +#undef CASE_LII +#undef CASE_LLIe +#undef CASE_ILI +#undef CASE_ILII +#undef CASE_ILLII +#undef CASE_IIILLIF +#undef CASE_IIILLIFL +#undef CASE_IIILLIIF +#undef CASE_IIILLIIFII +#undef CASE_IIILLIIFIIII +#undef CASE_IIILLIIFIIIIII +#undef CASE_IEILLIIIF +#undef CASE_IEILLIIIFI +#undef CASE_IEILLIIIFII +#undef CASE_IEILLIIIFIII +#undef CASE_IEILLIIIFIIII +#undef CASE_IIILLIIIF +#undef CASE_IIILLIIIFI +#undef CASE_IIIIF +#undef CASE_IIILL +#undef CASE_IIIILL +#undef CASE_IILLI +#undef CASE_IILLII +#undef CASE_IILLIII +#undef CASE_IILLIIII +#undef CASE_IIILLIIFLI +#undef CASE_IIILLIIFLII +#undef CASE_E +#undef CASE_EI +#undef CASE_EII +#undef CASE_EIII +#undef CASE_EIIII +#undef CASE_EIIIII +#undef CASE_EL +#undef CASE_ELL + +} // namespace +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/ext_inst.non_semantic_test.cpp b/third_party/SPIRV-Tools/test/ext_inst.non_semantic_test.cpp new file mode 100644 index 0000000..870684e --- /dev/null +++ b/third_party/SPIRV-Tools/test/ext_inst.non_semantic_test.cpp
@@ -0,0 +1,90 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for non-semantic extended instructions + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +using ::testing::Eq; + +namespace spvtools { +namespace { + +using NonSemanticRoundTripTest = RoundTripTest; +using NonSemanticTextToBinaryTest = spvtest::TextToBinaryTest; + +TEST_F(NonSemanticRoundTripTest, NonSemanticInsts) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 132384681 %2 +%4 = OpTypeInt 32 0 +%5 = OpConstant %4 123 +%6 = OpString "Test string" +%7 = OpExtInst %4 %1 82198732 %5 %6 +%8 = OpExtInstImport "NonSemantic.Testing.AnotherUnknownExtInstSet" +%9 = OpExtInst %4 %8 613874321 %7 %5 %6 +)"; + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_0); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(NonSemanticTextToBinaryTest, InvalidExtInstSetName) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic_Testing_ExtInst" +)"; + + EXPECT_THAT( + CompileFailure(spirv), + Eq("Invalid extended instruction import 'NonSemantic_Testing_ExtInst'")); +} + +TEST_F(NonSemanticTextToBinaryTest, NonSemanticIntParameter) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 1 99999 +)"; + + EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %.")); +} + +TEST_F(NonSemanticTextToBinaryTest, NonSemanticFloatParameter) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 1 3.141592 +)"; + + EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %.")); +} + +TEST_F(NonSemanticTextToBinaryTest, NonSemanticStringParameter) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 1 "foobar" +)"; + + EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %.")); +} + +} // namespace +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt index b38f35e..fb9e964 100644 --- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt +++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -24,18 +24,28 @@ fuzzer_pass_add_useful_constructs_test.cpp instruction_descriptor_test.cpp transformation_add_constant_boolean_test.cpp + transformation_add_constant_composite_test.cpp transformation_add_constant_scalar_test.cpp transformation_add_dead_break_test.cpp transformation_add_dead_continue_test.cpp + transformation_add_global_undef_test.cpp + transformation_add_global_variable_test.cpp transformation_add_no_contraction_decoration_test.cpp + transformation_add_type_array_test.cpp transformation_add_type_boolean_test.cpp transformation_add_type_float_test.cpp + transformation_add_type_function_test.cpp transformation_add_type_int_test.cpp + transformation_add_type_matrix_test.cpp transformation_add_type_pointer_test.cpp + transformation_add_type_struct_test.cpp + transformation_add_type_vector_test.cpp transformation_composite_construct_test.cpp transformation_composite_extract_test.cpp transformation_copy_object_test.cpp + transformation_merge_blocks_test.cpp transformation_move_block_down_test.cpp + transformation_outline_function_test.cpp transformation_replace_boolean_constant_with_constant_binary_test.cpp transformation_replace_constant_with_uniform_test.cpp transformation_replace_id_with_synonym_test.cpp
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp index bc6d4ee..1d15ad6 100644 --- a/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp +++ b/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.cpp
@@ -14,6 +14,7 @@ #include "test/fuzz/fuzz_test_util.h" +#include <fstream> #include <iostream> #include "tools/io.h" @@ -105,5 +106,20 @@ } } +void DumpTransformationsJson( + const protobufs::TransformationSequence& transformations, + const char* filename) { + std::string json_string; + auto json_options = google::protobuf::util::JsonOptions(); + json_options.add_whitespace = true; + auto json_generation_status = google::protobuf::util::MessageToJsonString( + transformations, &json_string, json_options); + if (json_generation_status == google::protobuf::util::Status::OK) { + std::ofstream transformations_json_file(filename); + transformations_json_file << json_string; + transformations_json_file.close(); + } +} + } // namespace fuzz } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.h b/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.h index 93f37e5..9e08bf6 100644 --- a/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.h +++ b/third_party/SPIRV-Tools/test/fuzz/fuzz_test_util.h
@@ -19,6 +19,7 @@ #include <vector> +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/opt/build_module.h" #include "source/opt/ir_context.h" #include "spirv-tools/libspirv.h" @@ -100,6 +101,12 @@ // Dumps |binary| to file |filename|. Useful for interactive debugging. void DumpShader(const std::vector<uint32_t>& binary, const char* filename); +// Dumps |transformations| to file |filename| in JSON format. Useful for +// interactive debugging. +void DumpTransformationsJson( + const protobufs::TransformationSequence& transformations, + const char* filename); + } // namespace fuzz } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp index 22a26fc..fa1f096 100644 --- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp +++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -62,13 +62,11 @@ for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) { std::vector<uint32_t> fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; - spvtools::FuzzerOptions fuzzer_options; - spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed); - Fuzzer fuzzer(env); + Fuzzer fuzzer(env, seed, true); fuzzer.SetMessageConsumer(kSilentConsumer); auto fuzzer_result_status = - fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out, + fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out, &fuzzer_transformation_sequence_out); ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status); ASSERT_TRUE(t.Validate(fuzzer_binary_out)); @@ -421,8 +419,8 @@ %86 = OpLabel %184 = OpPhi %26 %27 %78 %126 %89 %92 = OpSLessThan %8 %184 %56 - OpLoopMerge %88 %89 None - OpBranchConditional %92 %87 %88 + OpLoopMerge %1000 %89 None + OpBranchConditional %92 %87 %1000 %87 = OpLabel %95 = OpIAdd %26 %183 %74 %96 = OpSLessThan %8 %184 %95 @@ -464,6 +462,8 @@ %89 = OpLabel %126 = OpIAdd %26 %184 %74 OpBranch %86 + %1000 = OpLabel + OpBranch %88 %88 = OpLabel %128 = OpIAdd %26 %183 %74 OpBranch %77 @@ -724,7 +724,6 @@ %37 = OpSDiv %6 %302 %35 %38 = OpIMul %6 %35 %37 %40 = OpIEqual %17 %38 %302 - OpSelectionMerge %42 None OpBranchConditional %40 %41 %42 %41 = OpLabel %50 = OpConvertSToF %20 %302 @@ -752,7 +751,6 @@ OpBranch %59 %75 = OpLabel %78 = OpSGreaterThan %17 %304 %9 - OpSelectionMerge %80 None OpBranchConditional %78 %79 %80 %79 = OpLabel %83 = OpISub %6 %304 %54
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp index 6485070..9af863e 100644 --- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp +++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
@@ -165,12 +165,10 @@ // Run the fuzzer and check that it successfully yields a valid binary. std::vector<uint32_t> fuzzer_binary_out; protobufs::TransformationSequence fuzzer_transformation_sequence_out; - spvtools::FuzzerOptions fuzzer_options; - spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed); - Fuzzer fuzzer(env); + Fuzzer fuzzer(env, seed, true); fuzzer.SetMessageConsumer(kSilentConsumer); auto fuzzer_result_status = - fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out, + fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out, &fuzzer_transformation_sequence_out); ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status); ASSERT_TRUE(t.Validate(fuzzer_binary_out)); @@ -530,8 +528,8 @@ %86 = OpLabel %184 = OpPhi %26 %27 %78 %126 %89 %92 = OpSLessThan %8 %184 %56 - OpLoopMerge %88 %89 None - OpBranchConditional %92 %87 %88 + OpLoopMerge %1000 %89 None + OpBranchConditional %92 %87 %1000 %87 = OpLabel %95 = OpIAdd %26 %183 %74 %96 = OpSLessThan %8 %184 %95 @@ -573,6 +571,8 @@ %89 = OpLabel %126 = OpIAdd %26 %184 %74 OpBranch %86 + %1000 = OpLabel + OpBranch %88 %88 = OpLabel %128 = OpIAdd %26 %183 %74 OpBranch %77 @@ -831,7 +831,6 @@ %37 = OpSDiv %6 %302 %35 %38 = OpIMul %6 %35 %37 %40 = OpIEqual %17 %38 %302 - OpSelectionMerge %42 None OpBranchConditional %40 %41 %42 %41 = OpLabel %50 = OpConvertSToF %20 %302 @@ -859,7 +858,6 @@ OpBranch %59 %75 = OpLabel %78 = OpSGreaterThan %17 %304 %9 - OpSelectionMerge %80 None OpBranchConditional %78 %79 %80 %79 = OpLabel %83 = OpISub %6 %304 %54
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp new file mode 100644 index 0000000..5ce171b --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -0,0 +1,158 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_composite.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddConstantCompositeTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeMatrix %7 3 + %11 = OpConstant %6 0 + %12 = OpConstant %6 1 + %14 = OpConstant %6 2 + %15 = OpConstant %6 3 + %17 = OpConstant %6 4 + %18 = OpConstant %6 5 + %21 = OpTypeInt 32 1 + %22 = OpTypeInt 32 0 + %23 = OpConstant %22 3 + %24 = OpTypeArray %21 %23 + %25 = OpTypeBool + %26 = OpTypeStruct %24 %25 + %29 = OpConstant %21 1 + %30 = OpConstant %21 2 + %31 = OpConstant %21 3 + %33 = OpConstantFalse %25 + %35 = OpTypeVector %6 3 + %38 = OpConstant %6 6 + %39 = OpConstant %6 7 + %40 = OpConstant %6 8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Too few ids + ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101}) + .IsApplicable(context.get(), fact_manager)); + // Too many ids + ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14}) + .IsApplicable(context.get(), fact_manager)); + // Id already in use + ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12}) + .IsApplicable(context.get(), fact_manager)); + // %39 is not a type + ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}) + .IsApplicable(context.get(), fact_manager)); + + TransformationAddConstantComposite transformations[] = { + // %100 = OpConstantComposite %7 %11 %12 + TransformationAddConstantComposite(100, 7, {11, 12}), + + // %101 = OpConstantComposite %7 %14 %15 + TransformationAddConstantComposite(101, 7, {14, 15}), + + // %102 = OpConstantComposite %7 %17 %18 + TransformationAddConstantComposite(102, 7, {17, 18}), + + // %103 = OpConstantComposite %8 %100 %101 %102 + TransformationAddConstantComposite(103, 8, {100, 101, 102}), + + // %104 = OpConstantComposite %24 %29 %30 %31 + TransformationAddConstantComposite(104, 24, {29, 30, 31}), + + // %105 = OpConstantComposite %26 %104 %33 + TransformationAddConstantComposite(105, 26, {104, 33}), + + // %106 = OpConstantComposite %35 %38 %39 %40 + TransformationAddConstantComposite(106, 35, {38, 39, 40})}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeMatrix %7 3 + %11 = OpConstant %6 0 + %12 = OpConstant %6 1 + %14 = OpConstant %6 2 + %15 = OpConstant %6 3 + %17 = OpConstant %6 4 + %18 = OpConstant %6 5 + %21 = OpTypeInt 32 1 + %22 = OpTypeInt 32 0 + %23 = OpConstant %22 3 + %24 = OpTypeArray %21 %23 + %25 = OpTypeBool + %26 = OpTypeStruct %24 %25 + %29 = OpConstant %21 1 + %30 = OpConstant %21 2 + %31 = OpConstant %21 3 + %33 = OpConstantFalse %25 + %35 = OpTypeVector %6 3 + %38 = OpConstant %6 6 + %39 = OpConstant %6 7 + %40 = OpConstant %6 8 + %100 = OpConstantComposite %7 %11 %12 + %101 = OpConstantComposite %7 %14 %15 + %102 = OpConstantComposite %7 %17 %18 + %103 = OpConstantComposite %8 %100 %101 %102 + %104 = OpConstantComposite %24 %29 %30 %31 + %105 = OpConstantComposite %26 %104 %33 + %106 = OpConstantComposite %35 %38 %39 %40 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp index 8173e72..ff93da8 100644 --- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_continue_test.cpp
@@ -209,117 +209,6 @@ ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -TEST(TransformationAddDeadContinueTest, - DoNotAllowContinueToMergeBlockOfAnotherLoop) { - // A loop header must dominate its merge block if that merge block is - // reachable. We are thus not allowed to add a dead continue that would result - // in violation of this property. This test checks for such a scenario. - - std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %16 %139 - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %8 = OpTypeBool - %14 = OpTypeVector %6 4 - %15 = OpTypePointer Input %14 - %16 = OpVariable %15 Input - %138 = OpTypePointer Output %14 - %139 = OpVariable %138 Output - %400 = OpConstantTrue %8 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpBranch %500 - %500 = OpLabel - OpLoopMerge %501 %502 None - OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502 - %503 = OpLabel - OpLoopMerge %502 %504 None - OpBranchConditional %400 %505 %504 - %505 = OpLabel - OpBranch %502 - %504 = OpLabel - OpBranch %503 - %502 = OpLabel - OpBranchConditional %400 %501 %500 - %501 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - ASSERT_TRUE(IsValid(env, context.get())); - FactManager fact_manager; - - ASSERT_FALSE(TransformationAddDeadContinue(500, true, {}) - .IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(TransformationAddDeadContinue(500, false, {}) - .IsApplicable(context.get(), fact_manager)); -} - -TEST(TransformationAddDeadContinueTest, DoNotAllowContinueToSelectionMerge) { - // A selection header must dominate its merge block if that merge block is - // reachable. We are thus not allowed to add a dead continue that would result - // in violation of this property. This test checks for such a scenario. - - std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %16 %139 - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %8 = OpTypeBool - %14 = OpTypeVector %6 4 - %15 = OpTypePointer Input %14 - %16 = OpVariable %15 Input - %138 = OpTypePointer Output %14 - %139 = OpVariable %138 Output - %400 = OpConstantTrue %8 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpBranch %500 - %500 = OpLabel - OpLoopMerge %501 %502 None - OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502 - %503 = OpLabel - OpSelectionMerge %502 None - OpBranchConditional %400 %505 %504 - %505 = OpLabel - OpBranch %502 - %504 = OpLabel - OpBranch %502 - %502 = OpLabel - OpBranchConditional %400 %501 %500 - %501 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - ASSERT_TRUE(IsValid(env, context.get())); - FactManager fact_manager; - - ASSERT_FALSE(TransformationAddDeadContinue(500, true, {}) - .IsApplicable(context.get(), fact_manager)); - ASSERT_FALSE(TransformationAddDeadContinue(500, false, {}) - .IsApplicable(context.get(), fact_manager)); -} - TEST(TransformationAddDeadContinueTest, LoopNest) { // Checks some allowed and disallowed scenarios for a nest of loops, including // continuing a loop from an if or switch. @@ -1420,7 +1309,6 @@ OpLoopMerge %1557 %1570 None OpBranchConditional %395 %1562 %1557 %1562 = OpLabel - OpSelectionMerge %1570 None OpBranchConditional %395 %1571 %1570 %1571 = OpLabel OpBranch %1557 @@ -1626,11 +1514,8 @@ ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); } -TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) { - // A miscellaneous test that exposing a known bug in spirv-fuzz. - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable - // this test as an when the known issue is fixed. +TEST(TransformationAddDeadContinueTest, Miscellaneous6) { + // A miscellaneous test that exposed a bug in spirv-fuzz. std::string shader = R"( OpCapability Shader @@ -1648,7 +1533,7 @@ OpBranch %10 %10 = OpLabel OpLoopMerge %13 %12 None - OpBranchConditional %9 %13 %11 + OpBranch %11 %11 = OpLabel %20 = OpCopyObject %6 %9 OpBranch %12
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp new file mode 100644 index 0000000..66130be --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_function_test.cpp
@@ -0,0 +1,447 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_function.h" +#include "source/fuzz/instruction_message.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddFunctionTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationAddFunction transformation1(std::vector<protobufs::Instruction>( + {MakeInstructionMessage( + SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}}), + MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), + MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), + MakeInstructionMessage(SpvOpLabel, 0, 14, {}), + MakeInstructionMessage( + SpvOpVariable, 9, 17, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 7, 19, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}), + MakeInstructionMessage(SpvOpLabel, 0, 21, {}), + MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {23}}, + {SPV_OPERAND_TYPE_ID, {24}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}), + MakeInstructionMessage(SpvOpLabel, 0, 25, {}), + MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}), + MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}), + MakeInstructionMessage( + SpvOpSLessThan, 28, 29, + {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}), + MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {29}}, + {SPV_OPERAND_TYPE_ID, {22}}, + {SPV_OPERAND_TYPE_ID, {23}}}), + MakeInstructionMessage(SpvOpLabel, 0, 22, {}), + MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}), + MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}), + MakeInstructionMessage(SpvOpConvertSToF, 8, 32, + {{SPV_OPERAND_TYPE_ID, {31}}}), + MakeInstructionMessage( + SpvOpFMul, 8, 33, + {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}), + MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}), + MakeInstructionMessage( + SpvOpFAdd, 8, 35, + {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}), + MakeInstructionMessage(SpvOpLabel, 0, 24, {}), + MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}), + MakeInstructionMessage( + SpvOpIAdd, 6, 38, + {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}), + MakeInstructionMessage(SpvOpLabel, 0, 23, {}), + MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}), + MakeInstructionMessage(SpvOpReturnValue, 0, 0, + {{SPV_OPERAND_TYPE_ID, {39}}}), + MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); + + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %8 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %17 = OpVariable %9 Function + %19 = OpVariable %7 Function + OpStore %17 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %6 %19 + %27 = OpLoad %6 %11 + %29 = OpSLessThan %28 %26 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %30 = OpLoad %8 %12 + %31 = OpLoad %6 %19 + %32 = OpConvertSToF %8 %31 + %33 = OpFMul %8 %30 %32 + %34 = OpLoad %8 %17 + %35 = OpFAdd %8 %34 %33 + OpStore %17 %35 + OpBranch %24 + %24 = OpLabel + %36 = OpLoad %6 %19 + %38 = OpIAdd %6 %36 %37 + OpStore %19 %38 + OpBranch %21 + %23 = OpLabel + %39 = OpLoad %8 %17 + OpReturnValue %39 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation1, context.get())); + + TransformationAddFunction transformation2(std::vector<protobufs::Instruction>( + {MakeInstructionMessage( + SpvOpFunction, 2, 15, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {3}}}), + MakeInstructionMessage(SpvOpLabel, 0, 16, {}), + MakeInstructionMessage( + SpvOpVariable, 7, 44, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 9, 45, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 7, 48, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 9, 49, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}), + MakeInstructionMessage(SpvOpFunctionCall, 8, 46, + {{SPV_OPERAND_TYPE_ID, {13}}, + {SPV_OPERAND_TYPE_ID, {44}}, + {SPV_OPERAND_TYPE_ID, {45}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}), + MakeInstructionMessage(SpvOpFunctionCall, 8, 50, + {{SPV_OPERAND_TYPE_ID, {13}}, + {SPV_OPERAND_TYPE_ID, {48}}, + {SPV_OPERAND_TYPE_ID, {49}}}), + MakeInstructionMessage( + SpvOpFAdd, 8, 51, + {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}), + MakeInstructionMessage(SpvOpReturn, 0, 0, {}), + MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); + + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %8 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %17 = OpVariable %9 Function + %19 = OpVariable %7 Function + OpStore %17 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %6 %19 + %27 = OpLoad %6 %11 + %29 = OpSLessThan %28 %26 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %30 = OpLoad %8 %12 + %31 = OpLoad %6 %19 + %32 = OpConvertSToF %8 %31 + %33 = OpFMul %8 %30 %32 + %34 = OpLoad %8 %17 + %35 = OpFAdd %8 %34 %33 + OpStore %17 %35 + OpBranch %24 + %24 = OpLabel + %36 = OpLoad %6 %19 + %38 = OpIAdd %6 %36 %37 + OpStore %19 %38 + OpBranch %21 + %23 = OpLabel + %39 = OpLoad %8 %17 + OpReturnValue %39 + OpFunctionEnd + %15 = OpFunction %2 None %3 + %16 = OpLabel + %44 = OpVariable %7 Function + %45 = OpVariable %9 Function + %48 = OpVariable %7 Function + %49 = OpVariable %9 Function + OpStore %44 %20 + OpStore %45 %18 + %46 = OpFunctionCall %8 %13 %44 %45 + OpStore %48 %37 + OpStore %49 %47 + %50 = OpFunctionCall %8 %13 %48 %49 + %51 = OpFAdd %8 %46 %50 + OpStore %43 %51 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation2, context.get())); +} + +TEST(TransformationAddFunctionTest, InapplicableTransformations) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %8 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %17 = OpVariable %9 Function + %19 = OpVariable %7 Function + OpStore %17 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %6 %19 + %27 = OpLoad %6 %11 + %29 = OpSLessThan %28 %26 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %30 = OpLoad %8 %12 + %31 = OpLoad %6 %19 + %32 = OpConvertSToF %8 %31 + %33 = OpFMul %8 %30 %32 + %34 = OpLoad %8 %17 + %35 = OpFAdd %8 %34 %33 + OpStore %17 %35 + OpBranch %24 + %24 = OpLabel + %36 = OpLoad %6 %19 + %38 = OpIAdd %6 %36 %37 + OpStore %19 %38 + OpBranch %21 + %23 = OpLabel + %39 = OpLoad %8 %17 + OpReturnValue %39 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // No instructions + ASSERT_FALSE( + TransformationAddFunction(std::vector<protobufs::Instruction>({})) + .IsApplicable(context.get(), fact_manager)); + + // No function begin + ASSERT_FALSE( + TransformationAddFunction( + std::vector<protobufs::Instruction>( + {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), + MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), + MakeInstructionMessage(SpvOpLabel, 0, 14, {})})) + .IsApplicable(context.get(), fact_manager)); + + // No OpLabel + ASSERT_FALSE( + TransformationAddFunction( + std::vector<protobufs::Instruction>( + {MakeInstructionMessage(SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}}), + MakeInstructionMessage(SpvOpReturnValue, 0, 0, + {{SPV_OPERAND_TYPE_ID, {39}}}), + MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})) + .IsApplicable(context.get(), fact_manager)); + + // Abrupt end of instructions + ASSERT_FALSE(TransformationAddFunction( + std::vector<protobufs::Instruction>({MakeInstructionMessage( + SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}})})) + .IsApplicable(context.get(), fact_manager)); + + // No function end + ASSERT_FALSE( + TransformationAddFunction( + std::vector<protobufs::Instruction>( + {MakeInstructionMessage(SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}}), + MakeInstructionMessage(SpvOpLabel, 0, 14, {}), + MakeInstructionMessage(SpvOpReturnValue, 0, 0, + {{SPV_OPERAND_TYPE_ID, {39}}})})) + .IsApplicable(context.get(), fact_manager)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp new file mode 100644 index 0000000..c14f7e9 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_undef_test.cpp
@@ -0,0 +1,118 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_undef.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddGlobalUndefTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable(context.get(), + fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable(context.get(), + fact_manager)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable(context.get(), + fact_manager)); + + TransformationAddGlobalUndef transformations[] = { + // %100 = OpUndef %6 + TransformationAddGlobalUndef(100, 6), + + // %101 = OpUndef %7 + TransformationAddGlobalUndef(101, 7), + + // %102 = OpUndef %8 + TransformationAddGlobalUndef(102, 8), + + // %103 = OpUndef %9 + TransformationAddGlobalUndef(103, 9), + + // %104 = OpUndef %10 + TransformationAddGlobalUndef(104, 10), + + // %105 = OpUndef %11 + TransformationAddGlobalUndef(105, 11)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %100 = OpUndef %6 + %101 = OpUndef %7 + %102 = OpUndef %8 + %103 = OpUndef %9 + %104 = OpUndef %10 + %105 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp new file mode 100644 index 0000000..eda6828 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_global_variable_test.cpp
@@ -0,0 +1,275 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_variable.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddGlobalVariableTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %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 + %22 = OpConstantFalse %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddGlobalVariable(4, 10, 0).IsApplicable( + context.get(), fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddGlobalVariable(100, 1, 0).IsApplicable( + context.get(), fact_manager)); + + // %7 is not a pointer type + ASSERT_FALSE(TransformationAddGlobalVariable(100, 7, 0).IsApplicable( + context.get(), fact_manager)); + + // %9 does not have Private storage class + ASSERT_FALSE(TransformationAddGlobalVariable(100, 9, 0).IsApplicable( + context.get(), fact_manager)); + + // %15 does not have Private storage class + ASSERT_FALSE(TransformationAddGlobalVariable(100, 15, 0) + .IsApplicable(context.get(), fact_manager)); + + // %10 is a pointer to float, while %16 is an int constant + ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 16) + .IsApplicable(context.get(), fact_manager)); + + // %10 is a Private pointer to float, while %15 is a variable with type + // Uniform float pointer + ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, 15) + .IsApplicable(context.get(), fact_manager)); + + // %12 is a Private pointer to int, while %10 is a variable with type + // Private float pointer + ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, 10) + .IsApplicable(context.get(), fact_manager)); + + // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK + // since the initializer's type should be the *pointee* type. + ASSERT_FALSE(TransformationAddGlobalVariable(104, 10, 14) + .IsApplicable(context.get(), fact_manager)); + + // This would work in principle, but logical addressing does not allow + // a pointer to a pointer. + ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, 14) + .IsApplicable(context.get(), fact_manager)); + + TransformationAddGlobalVariable transformations[] = { + // %100 = OpVariable %12 Private + TransformationAddGlobalVariable(100, 12, 0), + + // %101 = OpVariable %10 Private + TransformationAddGlobalVariable(101, 10, 0), + + // %102 = OpVariable %13 Private + TransformationAddGlobalVariable(102, 13, 0), + + // %103 = OpVariable %12 Private %16 + TransformationAddGlobalVariable(103, 12, 16), + + // %104 = OpVariable %19 Private %21 + TransformationAddGlobalVariable(104, 19, 21), + + // %105 = OpVariable %19 Private %22 + TransformationAddGlobalVariable(105, 19, 22)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + 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 + %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 + %22 = OpConstantFalse %18 + %100 = OpVariable %12 Private + %101 = OpVariable %10 Private + %102 = OpVariable %13 Private + %103 = OpVariable %12 Private %16 + %104 = OpVariable %19 Private %21 + %105 = OpVariable %19 Private %22 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { + // This checks that when global variables are added to a SPIR-V 1.4+ module, + // they are also 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" + 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 + %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 + %4 = OpFunction %2 None %3 + %30 = OpLabel + OpReturn + OpFunctionEnd + %5 = OpFunction %2 None %3 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationAddGlobalVariable transformations[] = { + // %100 = OpVariable %12 Private + TransformationAddGlobalVariable(100, 12, 0), + + // %101 = OpVariable %12 Private %16 + TransformationAddGlobalVariable(101, 12, 16), + + // %102 = OpVariable %19 Private %21 + TransformationAddGlobalVariable(102, 19, 21)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "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 + %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 + %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, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp new file mode 100644 index 0000000..2bcbe73 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_array_test.cpp
@@ -0,0 +1,136 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_array.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeArrayTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %12 = OpConstant %7 3 + %13 = OpConstant %7 0 + %14 = OpConstant %7 -1 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 5 + %17 = OpConstant %15 0 + %18 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddTypeArray(4, 10, 16).IsApplicable( + context.get(), fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeArray(100, 1, 16) + .IsApplicable(context.get(), fact_manager)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddTypeArray(100, 3, 16) + .IsApplicable(context.get(), fact_manager)); + + // %2 is not a constant + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 2) + .IsApplicable(context.get(), fact_manager)); + + // %18 is not an integer + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 18) + .IsApplicable(context.get(), fact_manager)); + + // %13 is signed 0 + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 13) + .IsApplicable(context.get(), fact_manager)); + + // %14 is negative + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 14) + .IsApplicable(context.get(), fact_manager)); + + // %17 is unsigned 0 + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17) + .IsApplicable(context.get(), fact_manager)); + + TransformationAddTypeArray transformations[] = { + // %100 = OpTypeArray %10 %16 + TransformationAddTypeArray(100, 10, 16), + + // %101 = OpTypeArray %7 %12 + TransformationAddTypeArray(101, 7, 12)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %12 = OpConstant %7 3 + %13 = OpConstant %7 0 + %14 = OpConstant %7 -1 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 5 + %17 = OpConstant %15 0 + %18 = OpConstant %6 1 + %100 = OpTypeArray %10 %16 + %101 = OpTypeArray %7 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp new file mode 100644 index 0000000..46bd436 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_function_test.cpp
@@ -0,0 +1,134 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_function.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeFunctionTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %12 = OpTypeFloat 32 + %13 = OpTypeStruct %6 %12 + %14 = OpTypePointer Function %13 + %200 = OpTypePointer Function %13 + %15 = OpTypeVector %12 3 + %16 = OpTypePointer Function %15 + %17 = OpTypeVector %12 2 + %18 = OpTypeFunction %17 %14 %16 + %23 = OpConstant %12 1 + %24 = OpConstantComposite %17 %23 %23 + %27 = OpConstant %6 3 + %30 = OpConstant %6 1 + %31 = OpConstant %12 2 + %32 = OpConstantComposite %13 %30 %31 + %33 = OpConstantComposite %15 %23 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddTypeFunction(4, 12, {12, 16, 14}) + .IsApplicable(context.get(), fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeFunction(100, 1, {12, 16, 14}) + .IsApplicable(context.get(), fact_manager)); + + // %18 is a function type + ASSERT_FALSE(TransformationAddTypeFunction(100, 12, {18}) + .IsApplicable(context.get(), fact_manager)); + + // A function of this signature already exists + ASSERT_FALSE(TransformationAddTypeFunction(100, 17, {14, 16}) + .IsApplicable(context.get(), fact_manager)); + + TransformationAddTypeFunction transformations[] = { + // %100 = OpTypeFunction %12 %12 %16 %14 + TransformationAddTypeFunction(100, 12, {12, 16, 14}), + + // %101 = OpTypeFunction %12 + TransformationAddTypeFunction(101, 12, {}), + + // %102 = OpTypeFunction %17 %200 %16 + TransformationAddTypeFunction(102, 17, {200, 16})}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %12 = OpTypeFloat 32 + %13 = OpTypeStruct %6 %12 + %14 = OpTypePointer Function %13 + %200 = OpTypePointer Function %13 + %15 = OpTypeVector %12 3 + %16 = OpTypePointer Function %15 + %17 = OpTypeVector %12 2 + %18 = OpTypeFunction %17 %14 %16 + %23 = OpConstant %12 1 + %24 = OpConstantComposite %17 %23 %23 + %27 = OpConstant %6 3 + %30 = OpConstant %6 1 + %31 = OpConstant %12 2 + %32 = OpConstantComposite %13 %30 %31 + %33 = OpConstantComposite %15 %23 %23 %23 + %100 = OpTypeFunction %12 %12 %16 %14 + %101 = OpTypeFunction %12 + %102 = OpTypeFunction %17 %200 %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp new file mode 100644 index 0000000..84f27e9 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_matrix_test.cpp
@@ -0,0 +1,130 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_matrix.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeMatrixTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable(context.get(), + fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeMatrix(100, 1, 2).IsApplicable( + context.get(), fact_manager)); + + // %11 is not a floating-point vector + ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2) + .IsApplicable(context.get(), fact_manager)); + + TransformationAddTypeMatrix transformations[] = { + // %100 = OpTypeMatrix %8 2 + TransformationAddTypeMatrix(100, 8, 2), + + // %101 = OpTypeMatrix %8 3 + TransformationAddTypeMatrix(101, 8, 3), + + // %102 = OpTypeMatrix %8 4 + TransformationAddTypeMatrix(102, 8, 4), + + // %103 = OpTypeMatrix %9 2 + TransformationAddTypeMatrix(103, 9, 2), + + // %104 = OpTypeMatrix %9 3 + TransformationAddTypeMatrix(104, 9, 3), + + // %105 = OpTypeMatrix %9 4 + TransformationAddTypeMatrix(105, 9, 4), + + // %106 = OpTypeMatrix %10 2 + TransformationAddTypeMatrix(106, 10, 2), + + // %107 = OpTypeMatrix %10 3 + TransformationAddTypeMatrix(107, 10, 3), + + // %108 = OpTypeMatrix %10 4 + TransformationAddTypeMatrix(108, 10, 4)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %100 = OpTypeMatrix %8 2 + %101 = OpTypeMatrix %8 3 + %102 = OpTypeMatrix %8 4 + %103 = OpTypeMatrix %9 2 + %104 = OpTypeMatrix %9 3 + %105 = OpTypeMatrix %9 4 + %106 = OpTypeMatrix %10 2 + %107 = OpTypeMatrix %10 3 + %108 = OpTypeMatrix %10 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp new file mode 100644 index 0000000..ae68c9a --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_struct_test.cpp
@@ -0,0 +1,110 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_struct.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeStructTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable(context.get(), + fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeStruct(100, {1}).IsApplicable( + context.get(), fact_manager)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable( + context.get(), fact_manager)); + + TransformationAddTypeStruct transformations[] = { + // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 + TransformationAddTypeStruct(100, {6, 7, 8, 9, 10, 11}), + + // %101 = OpTypeStruct + TransformationAddTypeStruct(101, {}), + + // %102 = OpTypeStruct %6 + TransformationAddTypeStruct(102, {6}), + + // %103 = OpTypeStruct %6 %6 + TransformationAddTypeStruct(103, {6, 6})}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 + %101 = OpTypeStruct + %102 = OpTypeStruct %6 + %103 = OpTypeStruct %6 %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp new file mode 100644 index 0000000..6ac4498 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_type_vector_test.cpp
@@ -0,0 +1,102 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_vector.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeVectorTest, 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 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Id already in use + ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable(context.get(), + fact_manager)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable( + context.get(), fact_manager)); + + TransformationAddTypeVector transformations[] = { + // %100 = OpTypeVector %6 2 + TransformationAddTypeVector(100, 6, 2), + + // %101 = OpTypeVector %7 3 + TransformationAddTypeVector(101, 7, 3), + + // %102 = OpTypeVector %8 4 + TransformationAddTypeVector(102, 8, 4), + + // %103 = OpTypeVector %9 2 + TransformationAddTypeVector(103, 9, 2)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + } + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeBool + %100 = OpTypeVector %6 2 + %101 = OpTypeVector %7 3 + %102 = OpTypeVector %8 4 + %103 = OpTypeVector %9 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp index 9d3fde0..b489f71 100644 --- a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
@@ -291,7 +291,7 @@ %31 = OpLabel %42 = OpAccessChain %36 %18 %41 %43 = OpLoad %11 %42 - OpSelectionMerge %47 None + OpSelectionMerge %45 None OpSwitch %43 %46 0 %44 1 %45 %46 = OpLabel %69 = OpIAdd %11 %96 %27
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp new file mode 100644 index 0000000..e2b4aa6 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_merge_blocks_test.cpp
@@ -0,0 +1,675 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_blocks.h" + +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationMergeBlocksTest, BlockDoesNotExist) { + 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 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + ASSERT_FALSE( + TransformationMergeBlocks(3).IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationMergeBlocks(7).IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) { + 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 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + ASSERT_FALSE( + TransformationMergeBlocks(6).IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationMergeBlocksTest, + DoNotMergeSecondBlockHasMultiplePredecessors) { + 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 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + ASSERT_FALSE( + TransformationMergeBlocks(10).IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) { + 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 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %11 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationMergeBlocks transformation(10); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %11 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopMerge) { + 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 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %6 + %6 = OpLabel + OpBranchConditional %8 %9 %11 + %9 = OpLabel + OpBranch %10 + %11 = OpLabel + OpBranch %5 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationMergeBlocks transformation(10); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %9 %11 None + OpBranch %6 + %6 = OpLabel + OpBranchConditional %8 %9 %11 + %9 = OpLabel + OpReturn + %11 = OpLabel + OpBranch %5 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopContinue) { + 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 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %13 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %8 %9 %12 + %12 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %5 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationMergeBlocks transformation(11); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %13 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %10 %12 None + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %8 %9 %12 + %12 = OpLabel + OpBranch %5 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockStartsWithOpPhi) { + 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 + %7 = OpTypeBool + %8 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %9 = OpPhi %7 %8 %5 + %10 = OpCopyObject %7 %9 + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %7 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationMergeBlocks transformation(6); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %7 = OpTypeBool + %8 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpCopyObject %7 %8 + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %7 %8 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, BasicMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + %15 = OpLoad %6 %8 + OpBranch %102 + %102 = OpLabel + %16 = OpLoad %6 %10 + %17 = OpIMul %6 %16 %15 + OpBranch %103 + %103 = OpLabel + OpStore %10 %17 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + for (auto& transformation : + {TransformationMergeBlocks(100), TransformationMergeBlocks(101), + TransformationMergeBlocks(102), TransformationMergeBlocks(103)}) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpIMul %6 %16 %15 + OpStore %10 %17 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionHeader) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + OpSelectionMerge %103 None + OpBranchConditional %51 %102 %103 + %102 = OpLabel + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + OpBranch %103 + %103 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + for (auto& transformation : + {TransformationMergeBlocks(101), TransformationMergeBlocks(100)}) { + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + OpSelectionMerge %103 None + OpBranchConditional %51 %102 %103 + %102 = OpLabel + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + OpBranch %103 + %103 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, + MergeWhenFirstBlockIsLoopMergeFollowedByUnconditionalBranch) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + OpLoopMerge %102 %103 None + OpBranch %101 + %101 = OpLabel + %200 = OpCopyObject %6 %9 + OpBranchConditional %51 %102 %103 + %103 = OpLabel + OpBranch %100 + %102 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationMergeBlocks transformation(101); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + %200 = OpCopyObject %6 %9 + OpLoopMerge %102 %103 None + OpBranchConditional %51 %102 %103 + %103 = OpLabel + OpBranch %100 + %102 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp new file mode 100644 index 0000000..4f828b6 --- /dev/null +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_outline_function_test.cpp
@@ -0,0 +1,2045 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_outline_function.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationOutlineFunctionTest, TrivialOutline) { + // This tests outlining of a single, empty basic block. + + 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 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %103 = OpFunctionCall %2 %101 + OpReturn + OpFunctionEnd + %101 = OpFunction %2 None %3 + %102 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfRegionStartsWithOpVariable) { + // This checks that we do not outline the first block of a function if it + // contains OpVariable. + + 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 + %7 = OpTypeBool + %8 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = 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); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) { + // This tests outlining of some non-trivial control flow, but such that the + // basic blocks in the control flow do not actually do anything. + + 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 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %12 %11 None + OpBranch %10 + %10 = OpLabel + OpBranchConditional %21 %11 %12 + %11 = OpLabel + OpBranch %9 + %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, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 13, /* not relevant */ + 200, 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %103 = OpFunctionCall %2 %101 + OpReturn + OpFunctionEnd + %101 = OpFunction %2 None %3 + %102 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %12 %11 None + OpBranch %10 + %10 = OpLabel + OpBranchConditional %21 %11 %12 + %11 = OpLabel + OpBranch %9 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesUnusedIds) { + // This tests outlining of a single basic block that does some computation, + // but that does not use nor generate ids required outside of the outlined + // region. + + 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 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %103 = OpFunctionCall %2 %101 + OpReturn + OpFunctionEnd + %101 = OpFunction %2 None %3 + %102 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesSingleUsedId) { + // This tests outlining of a block that generates an id that is used in a + // later block. + + 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 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpIAdd %20 %7 %8 + OpBranch %10 + %10 = OpLabel + %11 = OpCopyObject %20 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103, + 105, {}, {{9, 104}}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %99 = OpTypeStruct %20 + %100 = OpTypeFunction %99 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %103 = OpFunctionCall %99 %101 + %9 = OpCompositeExtract %20 %103 0 + OpBranch %10 + %10 = OpLabel + %11 = OpCopyObject %20 %9 + OpReturn + OpFunctionEnd + %101 = OpFunction %99 None %100 + %102 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %104 = OpIAdd %20 %7 %8 + %105 = OpCompositeConstruct %99 %104 + OpReturnValue %105 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineDiamondThatGeneratesSeveralIds) { + // This tests outlining of several blocks that generate a number of ids that + // are used in later blocks. + + 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 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %22 = OpTypeBool + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpSLessThan %22 %7 %8 + OpSelectionMerge %12 None + OpBranchConditional %9 %10 %11 + %10 = OpLabel + %13 = OpIAdd %20 %7 %8 + OpBranch %12 + %11 = OpLabel + %14 = OpIAdd %20 %7 %7 + OpBranch %12 + %12 = OpLabel + %15 = OpPhi %20 %13 %10 %14 %11 + OpBranch %80 + %80 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %20 %15 + %18 = OpCopyObject %22 %9 + %19 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + 6, 80, 100, 101, 102, 103, 104, 105, {}, + {{15, 106}, {9, 107}, {7, 108}, {8, 109}}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %22 = OpTypeBool + %3 = OpTypeFunction %2 + %100 = OpTypeStruct %20 %20 %22 %20 + %101 = OpTypeFunction %100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %104 = OpFunctionCall %100 %102 + %7 = OpCompositeExtract %20 %104 0 + %8 = OpCompositeExtract %20 %104 1 + %9 = OpCompositeExtract %22 %104 2 + %15 = OpCompositeExtract %20 %104 3 + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %20 %15 + %18 = OpCopyObject %22 %9 + %19 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + %102 = OpFunction %100 None %101 + %103 = OpLabel + %108 = OpCopyObject %20 %21 + %109 = OpCopyObject %20 %21 + %107 = OpSLessThan %22 %108 %109 + OpSelectionMerge %12 None + OpBranchConditional %107 %10 %11 + %10 = OpLabel + %13 = OpIAdd %20 %108 %109 + OpBranch %12 + %11 = OpLabel + %14 = OpIAdd %20 %108 %108 + OpBranch %12 + %12 = OpLabel + %106 = OpPhi %20 %13 %10 %14 %11 + OpBranch %80 + %80 = OpLabel + %105 = OpCompositeConstruct %100 %108 %109 %107 %106 + OpReturnValue %105 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesASingleId) { + // This tests outlining of a block that uses an id defined earlier. + + 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 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %7 = OpCopyObject %20 %21 + OpBranch %6 + %6 = OpLabel + %8 = OpCopyObject %20 %7 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104, + 105, {{7, 106}}, {}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %101 = OpTypeFunction %2 %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %7 = OpCopyObject %20 %21 + OpBranch %6 + %6 = OpLabel + %104 = OpFunctionCall %2 %102 %7 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + %102 = OpFunction %2 None %101 + %106 = OpFunctionParameter %20 + %103 = OpLabel + %8 = OpCopyObject %20 %106 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAVariable) { + // This tests outlining of a block that uses a variable. + + 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 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %12 = OpTypePointer Function %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpVariable %12 Function + OpBranch %6 + %6 = OpLabel + %8 = OpLoad %20 %13 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104, + 105, {{13, 106}}, {}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %12 = OpTypePointer Function %20 + %101 = OpTypeFunction %2 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpVariable %12 Function + OpBranch %6 + %6 = OpLabel + %104 = OpFunctionCall %2 %102 %13 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + %102 = OpFunction %2 None %101 + %106 = OpFunctionParameter %12 + %103 = OpLabel + %8 = OpLoad %20 %106 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAParameter) { + // This tests outlining of a block that uses a function parameter. + + 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" + OpName %10 "foo(i1;" + OpName %9 "x" + OpName %18 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %13 = OpConstant %6 1 + %17 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %7 Function + OpStore %18 %17 + %19 = OpFunctionCall %6 %10 %18 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %14 = OpIAdd %6 %12 %13 + OpReturnValue %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104, + 105, {{9, 106}}, {{14, 107}}); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "x" + OpName %18 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %13 = OpConstant %6 1 + %17 = OpConstant %6 3 + %100 = OpTypeStruct %6 + %101 = OpTypeFunction %100 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %7 Function + OpStore %18 %17 + %19 = OpFunctionCall %6 %10 %18 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %104 = OpFunctionCall %100 %102 %9 + %14 = OpCompositeExtract %6 %104 0 + OpReturnValue %14 + OpFunctionEnd + %102 = OpFunction %100 None %101 + %106 = OpFunctionParameter %7 + %103 = OpLabel + %12 = OpLoad %6 %106 + %107 = OpIAdd %6 %12 %13 + %105 = OpCompositeConstruct %100 %107 + OpReturnValue %105 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfLoopMergeIsOutsideRegion) { + 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 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %8 + %8 = OpLabel + OpBranchConditional %10 %6 %7 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) { + 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 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpReturn + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) { + 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 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpKill + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfRegionInvolvesUnreachable) { + 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 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpUnreachable + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfSelectionMergeIsOutsideRegion) { + 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 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %7 None + OpBranchConditional %10 %8 %7 + %8 = OpLabel + OpBranch %7 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) { + 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 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %11 None + OpBranch %7 + %7 = OpLabel + OpBranchConditional %10 %11 %8 + %11 = OpLabel + OpBranch %6 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfLoopContinueIsOutsideRegion) { + 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 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %7 + %8 = OpLabel + OpBranch %6 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineWithLoopCarriedPhiDependence) { + 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 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %12 = OpPhi %9 %10 %5 %13 %8 + OpLoopMerge %7 %8 None + OpBranch %8 + %8 = OpLabel + %13 = OpCopyObject %9 %10 + OpBranchConditional %10 %6 %7 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineSelectionHeaderNotInRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %8 %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + OpBranch %57 + %57 = OpLabel + %23 = OpCopyObject %20 %22 + OpBranch %58 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 54, + /*exit_block*/ 58, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{22, 206}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %201 = OpTypeFunction %2 %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + %204 = OpFunctionCall %2 %202 %22 + OpReturn + OpFunctionEnd + %202 = OpFunction %2 None %201 + %206 = OpFunctionParameter %20 + %203 = OpLabel + OpBranch %57 + %57 = OpLabel + %23 = OpCopyObject %20 %206 + OpBranch %58 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %30 = OpTypeFunction %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpFunctionCall %20 %100 + OpReturn + OpFunctionEnd + %100 = OpFunction %20 None %30 + %8 = OpLabel + %31 = OpCopyObject %20 %21 + OpBranch %9 + %9 = OpLabel + %32 = OpCopyObject %20 %31 + OpBranch %10 + %10 = OpLabel + OpReturnValue %32 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 9, + /*exit_block*/ 10, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{31, 206}}, + /*output_id_to_fresh_id*/ {{32, 207}}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %30 = OpTypeFunction %20 + %200 = OpTypeStruct %20 + %201 = OpTypeFunction %200 %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpFunctionCall %20 %100 + OpReturn + OpFunctionEnd + %100 = OpFunction %20 None %30 + %8 = OpLabel + %31 = OpCopyObject %20 %21 + OpBranch %9 + %9 = OpLabel + %204 = OpFunctionCall %200 %202 %31 + %32 = OpCompositeExtract %20 %204 0 + OpReturnValue %32 + OpFunctionEnd + %202 = OpFunction %200 None %201 + %206 = OpFunctionParameter %20 + %203 = OpLabel + %207 = OpCopyObject %20 %206 + OpBranch %10 + %10 = OpLabel + %205 = OpCompositeConstruct %200 %207 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + OutlineRegionEndingWithConditionalBranch) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %54 + %54 = OpLabel + %6 = OpCopyObject %20 %21 + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 54, + /*exit_block*/ 54, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{}}, + /*output_id_to_fresh_id*/ {{6, 206}}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %200 = OpTypeStruct %20 + %201 = OpTypeFunction %200 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %54 + %54 = OpLabel + %204 = OpFunctionCall %200 %202 + %6 = OpCompositeExtract %20 %204 0 + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %203 = OpLabel + %206 = OpCopyObject %20 %21 + %205 = OpCompositeConstruct %200 %206 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + OutlineRegionEndingWithConditionalBranch2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 54, + /*exit_block*/ 54, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + %204 = OpFunctionCall %2 %202 + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + %202 = OpFunction %2 None %3 + %203 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %6 %7 %5 + %23 = OpCopyObject %6 %22 + OpBranch %24 + %24 = OpLabel + %25 = OpCopyObject %6 %23 + %26 = OpCopyObject %6 %22 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 21, + /*exit_block*/ 21, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{22, 207}}, + /*output_id_to_fresh_id*/ {{23, 208}}); + + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineRegionThatStartsWithLoopHeader) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + OpBranchConditional %7 %22 %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 21, + /*exit_block*/ 24, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineRegionThatEndsWithLoopMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + OpBranchConditional %7 %22 %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 5, + /*exit_block*/ 22, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) { + // An access chain result is a pointer, but it cannot be passed as a function + // parameter, as it is not a memory object. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %9 = OpTypePointer Function %6 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %8 Function + OpBranch %11 + %11 = OpLabel + %12 = OpAccessChain %9 %10 %19 + OpBranch %13 + %13 = OpLabel + %14 = OpLoad %6 %12 + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 13, + /*exit_block*/ 15, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{12, 207}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous1) { + // This tests outlining of some non-trivial code. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %85 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %28 "buf" + OpMemberName %28 0 "u1" + OpMemberName %28 1 "u2" + OpName %30 "" + OpName %85 "color" + OpMemberDecorate %28 0 Offset 0 + OpMemberDecorate %28 1 Offset 4 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %85 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstant %6 3 + %13 = OpConstant %6 4 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %28 = OpTypeStruct %6 %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %84 = OpTypePointer Output %7 + %85 = OpVariable %84 Output + %114 = OpConstant %15 8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + %103 = OpPhi %15 %18 %5 %106 %43 + %102 = OpPhi %7 %14 %5 %107 %43 + %101 = OpPhi %15 %18 %5 %40 %43 + %32 = OpAccessChain %31 %30 %18 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %15 %33 + %36 = OpSLessThan %35 %101 %34 + OpLoopMerge %24 %43 None + OpBranchConditional %36 %23 %24 + %23 = OpLabel + %40 = OpIAdd %15 %101 %39 + OpBranch %150 + %150 = OpLabel + OpBranch %41 + %41 = OpLabel + %107 = OpPhi %7 %102 %150 %111 %65 + %106 = OpPhi %15 %103 %150 %110 %65 + %104 = OpPhi %15 %40 %150 %81 %65 + %47 = OpAccessChain %31 %30 %39 + %48 = OpLoad %6 %47 + %49 = OpConvertFToS %15 %48 + %50 = OpSLessThan %35 %104 %49 + OpLoopMerge %1000 %65 None + OpBranchConditional %50 %42 %1000 + %42 = OpLabel + %60 = OpIAdd %15 %106 %114 + %63 = OpSGreaterThan %35 %104 %60 + OpBranchConditional %63 %64 %65 + %64 = OpLabel + %71 = OpCompositeExtract %6 %107 0 + %72 = OpFAdd %6 %71 %11 + %97 = OpCompositeInsert %7 %72 %107 0 + %76 = OpCompositeExtract %6 %107 3 + %77 = OpConvertFToS %15 %76 + %79 = OpIAdd %15 %60 %77 + OpBranch %65 + %65 = OpLabel + %111 = OpPhi %7 %107 %42 %97 %64 + %110 = OpPhi %15 %60 %42 %79 %64 + %81 = OpIAdd %15 %104 %39 + OpBranch %41 + %1000 = OpLabel + OpBranch %1001 + %1001 = OpLabel + OpBranch %43 + %43 = OpLabel + OpBranch %22 + %24 = OpLabel + %87 = OpCompositeExtract %6 %102 0 + %91 = OpConvertSToF %6 %103 + %92 = OpCompositeConstruct %7 %87 %11 %91 %10 + OpStore %85 %92 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 150, + /*exit_block*/ 1001, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}}, + /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %85 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %28 "buf" + OpMemberName %28 0 "u1" + OpMemberName %28 1 "u2" + OpName %30 "" + OpName %85 "color" + OpMemberDecorate %28 0 Offset 0 + OpMemberDecorate %28 1 Offset 4 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %85 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstant %6 3 + %13 = OpConstant %6 4 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %28 = OpTypeStruct %6 %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %84 = OpTypePointer Output %7 + %85 = OpVariable %84 Output + %114 = OpConstant %15 8 + %200 = OpTypeStruct %7 %15 + %201 = OpTypeFunction %200 %15 %7 %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + %103 = OpPhi %15 %18 %5 %106 %43 + %102 = OpPhi %7 %14 %5 %107 %43 + %101 = OpPhi %15 %18 %5 %40 %43 + %32 = OpAccessChain %31 %30 %18 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %15 %33 + %36 = OpSLessThan %35 %101 %34 + OpLoopMerge %24 %43 None + OpBranchConditional %36 %23 %24 + %23 = OpLabel + %40 = OpIAdd %15 %101 %39 + OpBranch %150 + %150 = OpLabel + %204 = OpFunctionCall %200 %202 %103 %102 %40 + %107 = OpCompositeExtract %7 %204 0 + %106 = OpCompositeExtract %15 %204 1 + OpBranch %43 + %43 = OpLabel + OpBranch %22 + %24 = OpLabel + %87 = OpCompositeExtract %6 %102 0 + %91 = OpConvertSToF %6 %103 + %92 = OpCompositeConstruct %7 %87 %11 %91 %10 + OpStore %85 %92 + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %301 = OpFunctionParameter %15 + %300 = OpFunctionParameter %7 + %302 = OpFunctionParameter %15 + %203 = OpLabel + OpBranch %41 + %41 = OpLabel + %401 = OpPhi %7 %300 %203 %111 %65 + %400 = OpPhi %15 %301 %203 %110 %65 + %104 = OpPhi %15 %302 %203 %81 %65 + %47 = OpAccessChain %31 %30 %39 + %48 = OpLoad %6 %47 + %49 = OpConvertFToS %15 %48 + %50 = OpSLessThan %35 %104 %49 + OpLoopMerge %1000 %65 None + OpBranchConditional %50 %42 %1000 + %42 = OpLabel + %60 = OpIAdd %15 %400 %114 + %63 = OpSGreaterThan %35 %104 %60 + OpBranchConditional %63 %64 %65 + %64 = OpLabel + %71 = OpCompositeExtract %6 %401 0 + %72 = OpFAdd %6 %71 %11 + %97 = OpCompositeInsert %7 %72 %401 0 + %76 = OpCompositeExtract %6 %401 3 + %77 = OpConvertFToS %15 %76 + %79 = OpIAdd %15 %60 %77 + OpBranch %65 + %65 = OpLabel + %111 = OpPhi %7 %401 %42 %97 %64 + %110 = OpPhi %15 %60 %42 %79 %64 + %81 = OpIAdd %15 %104 %39 + OpBranch %41 + %1000 = OpLabel + OpBranch %1001 + %1001 = OpLabel + %205 = OpCompositeConstruct %200 %401 %400 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %167 = OpConstantTrue %21 + %168 = OpConstantFalse %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranchConditional %168 %37 %38 + %38 = OpLabel + OpBranchConditional %168 %37 %36 + %37 = OpLabel + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 38, + /*exit_block*/ 36, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %167 = OpConstantTrue %21 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %80 + %80 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpBranchConditional %167 %15 %16 + %15 = OpLabel + OpBranch %17 + %16 = OpLabel + OpBranch %81 + %81 = OpLabel + OpReturn + %17 = OpLabel + OpBranch %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationOutlineFunction transformation( + /*entry_block*/ 80, + /*exit_block*/ 81, + /*new_function_struct_return_type_id*/ 300, + /*new_function_type_id*/ 301, + /*new_function_id*/ 302, + /*new_function_region_entry_block*/ 304, + /*new_caller_result_id*/ 305, + /*new_callee_result_id*/ 306, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %167 = OpConstantTrue %21 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %80 + %80 = OpLabel + %305 = OpFunctionCall %2 %302 + OpReturn + OpFunctionEnd + %302 = OpFunction %2 None %3 + %304 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpBranchConditional %167 %15 %16 + %15 = OpLabel + OpBranch %17 + %16 = OpLabel + OpBranch %81 + %81 = OpLabel + OpReturn + %17 = OpLabel + OpBranch %14 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 75e117a..41b6116 100644 --- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -159,18 +159,20 @@ %65 = OpAccessChain %13 %11 %64 %66 = OpLoad %6 %65 %67 = OpSGreaterThan %29 %84 %66 - OpSelectionMerge %69 None + OpSelectionMerge %1000 None OpBranchConditional %67 %68 %72 %68 = OpLabel %71 = OpIAdd %6 %84 %26 - OpBranch %69 + OpBranch %1000 %72 = OpLabel %74 = OpIAdd %6 %84 %64 %205 = OpCopyObject %6 %74 - OpBranch %69 - %69 = OpLabel + OpBranch %1000 + %1000 = OpLabel %86 = OpPhi %6 %71 %68 %74 %72 %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %69 + %69 = OpLabel OpBranch %20 %22 = OpLabel %75 = OpAccessChain %46 %42 %50 @@ -421,18 +423,20 @@ %65 = OpAccessChain %13 %11 %64 %66 = OpLoad %6 %65 %67 = OpSGreaterThan %29 %84 %66 - OpSelectionMerge %69 None + OpSelectionMerge %1000 None OpBranchConditional %67 %68 %72 %68 = OpLabel %71 = OpIAdd %6 %84 %26 - OpBranch %69 + OpBranch %1000 %72 = OpLabel %74 = OpIAdd %6 %84 %64 %205 = OpCopyObject %6 %74 - OpBranch %69 - %69 = OpLabel + OpBranch %1000 + %1000 = OpLabel %86 = OpPhi %6 %71 %68 %205 %72 %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %69 + %69 = OpLabel OpBranch %20 %22 = OpLabel %75 = OpAccessChain %46 %42 %50
diff --git a/third_party/SPIRV-Tools/test/fuzzers/spvtools_as_fuzzer.cpp b/third_party/SPIRV-Tools/test/fuzzers/spvtools_as_fuzzer.cpp index 1b1de00..8cecb05 100644 --- a/third_party/SPIRV-Tools/test/fuzzers/spvtools_as_fuzzer.cpp +++ b/third_party/SPIRV-Tools/test/fuzzers/spvtools_as_fuzzer.cpp
@@ -36,7 +36,7 @@ input_str.resize(char_count); memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t)); - spv_binary binary; + spv_binary binary = nullptr; spv_diagnostic diagnostic = nullptr; spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), SPV_TEXT_TO_BINARY_OPTION_NONE, &binary, @@ -66,5 +66,7 @@ binary = nullptr; } + spvContextDestroy(context); + return 0; }
diff --git a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp index 9e5197d..e7303ba 100644 --- a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
@@ -5932,7 +5932,6 @@ %42 = OpLabel %43 = OpLoad %int %i %44 = OpSLessThan %bool %43 %int_1 -OpSelectionMerge %45 None OpBranchConditional %44 %46 %40 %46 = OpLabel %47 = OpLoad %int %i
diff --git a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp index f87fd80..11fba73 100644 --- a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
@@ -447,10 +447,53 @@ OpLoopMerge %merge %continue None OpBranch %inner_header %inner_header = OpLabel -OpSelectionMerge %continue None -OpBranchConditional %true %then %continue +OpSelectionMerge %if_merge None +OpBranchConditional %true %then %if_merge %then = OpLabel OpBranch %continue +%if_merge = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranchConditional %false %merge %header +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<BlockMergePass>(text, true); +} + +TEST_F(BlockMergeTest, CannotMergeContinue) { + const std::string text = R"( +; CHECK: OpBranch [[loop_header:%\w+]] +; CHECK: [[loop_header]] = OpLabel +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]] +; CHECK: [[if_header]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK: [[continue]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %true %inner_header %merge +%inner_header = OpLabel +OpSelectionMerge %if_merge None +OpBranchConditional %true %then %if_merge +%then = OpLabel +OpBranch %continue +%if_merge = OpLabel +OpBranch %continue %continue = OpLabel OpBranchConditional %false %merge %header %merge = OpLabel
diff --git a/third_party/SPIRV-Tools/test/opt/convert_relaxed_to_half_test.cpp b/third_party/SPIRV-Tools/test/opt/convert_relaxed_to_half_test.cpp index 3ac8009..c138154 100644 --- a/third_party/SPIRV-Tools/test/opt/convert_relaxed_to_half_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/convert_relaxed_to_half_test.cpp
@@ -997,7 +997,7 @@ %99 = OpFConvert %v4half %15 OpBranch %65 %65 = OpLabel -%96 = OpPhi %v4half %99 %5 %76 %71 +%96 = OpPhi %v4half %99 %5 %100 %71 %95 = OpPhi %int %int_0 %5 %78 %71 %70 = OpSLessThan %bool %95 %int_10 OpLoopMerge %66 %71 None @@ -1222,6 +1222,115 @@ defs_after + func_after, true, true); } +TEST_F(ConvertToHalfTest, ConvertToHalfWithClosure) { + // Include as many contiguous composite instructions as possible into + // half-precision computations + // + // Compiled with glslang -V -Os + // + // clang-format off + // + // #version 410 core + // + // precision mediump float; + // + // layout(location = 1) in vec3 foo; + // layout(location = 2) in mat2 bar; + // layout(location = 1) out vec3 res; + // + // vec3 func(vec3 tap, mat2 M) { + // return vec3(M * tap.xy, 1.0); + // } + // + // void main() { + // res = func(foo, bar); + // } + // + // clang-format on + + const std::string defs = + R"(OpCapability Shader +; CHECK: OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %res %foo %bar +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 410 +OpName %main "main" +OpName %res "res" +OpName %foo "foo" +OpName %bar "bar" +OpDecorate %res RelaxedPrecision +; CHECK-NOT: OpDecorate %res RelaxedPrecision +OpDecorate %res Location 1 +OpDecorate %foo RelaxedPrecision +; CHECK-NOT: OpDecorate %foo RelaxedPrecision +OpDecorate %foo Location 1 +OpDecorate %bar RelaxedPrecision +; CHECK-NOT: OpDecorate %bar RelaxedPrecision +OpDecorate %bar Location 2 +OpDecorate %34 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +; CHECK-NOT: OpDecorate %34 RelaxedPrecision +; CHECK-NOT: OpDecorate %36 RelaxedPrecision +; CHECK-NOT: OpDecorate %41 RelaxedPrecision +; CHECK-NOT: OpDecorate %42 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 +%float_1 = OpConstant %float 1 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%res = OpVariable %_ptr_Output_v3float Output +%_ptr_Input_v3float = OpTypePointer Input %v3float +%foo = OpVariable %_ptr_Input_v3float Input +%_ptr_Input_mat2v2float = OpTypePointer Input %mat2v2float +%bar = OpVariable %_ptr_Input_mat2v2float Input +)"; + + const std::string func = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%34 = OpLoad %v3float %foo +%36 = OpLoad %mat2v2float %bar +; CHECK: %48 = OpFConvert %v3half %34 +; CHECK: %49 = OpFConvert %v3half %34 +%41 = OpVectorShuffle %v2float %34 %34 0 1 +; CHECK-NOT: %41 = OpVectorShuffle %v2float %34 %34 0 1 +; CHECK: %41 = OpVectorShuffle %v2half %48 %49 0 1 +%42 = OpMatrixTimesVector %v2float %36 %41 +; CHECK-NOT: %42 = OpMatrixTimesVector %v2float %36 %41 +; CHECK: %55 = OpCompositeExtract %v2float %36 0 +; CHECK: %56 = OpFConvert %v2half %55 +; CHECK: %57 = OpCompositeExtract %v2float %36 1 +; CHECK: %58 = OpFConvert %v2half %57 +; CHECK: %59 = OpCompositeConstruct %mat2v2half %56 %58 +; CHECK: %52 = OpCopyObject %mat2v2float %36 +; CHECK: %42 = OpMatrixTimesVector %v2half %59 %41 +%43 = OpCompositeExtract %float %42 0 +%44 = OpCompositeExtract %float %42 1 +; CHECK-NOT: %43 = OpCompositeExtract %float %42 0 +; CHECK-NOT: %44 = OpCompositeExtract %float %42 1 +; CHECK: %43 = OpCompositeExtract %half %42 0 +; CHECK: %44 = OpCompositeExtract %half %42 1 +%45 = OpCompositeConstruct %v3float %43 %44 %float_1 +; CHECK-NOT: %45 = OpCompositeConstruct %v3float %43 %44 %float_1 +; CHECK: %53 = OpFConvert %float %43 +; CHECK: %54 = OpFConvert %float %44 +; CHECK: %45 = OpCompositeConstruct %v3float %53 %54 %float_1 +OpStore %res %45 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<ConvertToHalfPass>(defs + func, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp index 5d41bdc..e612867 100644 --- a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
@@ -2798,7 +2798,9 @@ OpFunctionEnd )"; - SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true); + // The selection merge in the loop naming the continue target as merge is + // invalid, but handled by this pass so validation is disabled. + SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, false); } TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) { @@ -2942,7 +2944,9 @@ OpFunctionEnd )"; - SinglePassRunAndMatch<DeadBranchElimPass>(body, true); + // The selection merge in the loop naming the continue target as merge is + // invalid, but handled by this pass so validation is disabled. + SinglePassRunAndMatch<DeadBranchElimPass>(body, false); } TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {
diff --git a/third_party/SPIRV-Tools/test/opt/fold_test.cpp b/third_party/SPIRV-Tools/test/opt/fold_test.cpp index 68a7d18..26d1220 100644 --- a/third_party/SPIRV-Tools/test/opt/fold_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/fold_test.cpp
@@ -3396,7 +3396,17 @@ "%2 = OpCompositeConstruct %v2int %103 %103\n" + "OpReturn\n" + "OpFunctionEnd", - 2, VEC2_0_ID) + 2, VEC2_0_ID), + // Test case 5: Don't segfault when trying to fold an OpCompositeConstruct + // for an empty struct, and we reached the id limit. + InstructionFoldingCase<uint32_t>( + Header() + "%empty_struct = OpTypeStruct\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%4194303 = OpCompositeConstruct %empty_struct\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4194303, 0) )); INSTANTIATE_TEST_SUITE_P(PhiFoldingTest, GeneralInstructionFoldingTest, @@ -6111,6 +6121,23 @@ "%4 = OpFSub %float %float_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 13: merge subtract of subtract with mixed types. + // 2 - (1 - x) = x + 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpISub %int %uint_1 %2\n" + + "%4 = OpISub %int %int_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) ));
diff --git a/third_party/SPIRV-Tools/test/opt/graphics_robust_access_test.cpp b/third_party/SPIRV-Tools/test/opt/graphics_robust_access_test.cpp index 58bd404..d38571e 100644 --- a/third_party/SPIRV-Tools/test/opt/graphics_robust_access_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/graphics_robust_access_test.cpp
@@ -271,7 +271,7 @@ %uint_4 = OpConstant %uint 4 )" << MainPrefix() << R"( - %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%uint_3") + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%int_3") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); } @@ -329,7 +329,7 @@ ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_3 = OpConstant %int 3 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3 + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -354,7 +354,7 @@ ; CHECK-DAG: %short_3 = OpConstant %short 3 ; CHECK-NOT: = OpTypeInt 32 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] UClamp %i %short_0 %short_3 + ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] SClamp %i %short_0 %short_3 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -375,11 +375,11 @@ << MainPrefix() << R"( ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-NOT: = OpTypeInt 32 - ; CHECK-DAG: %ushort_0 = OpConstant %ushort 0 - ; CHECK-DAG: %ushort_3 = OpConstant %ushort 3 + ; CHECK-DAG: %short_0 = OpConstant %short 0 + ; CHECK-DAG: %short_3 = OpConstant %short 3 ; CHECK-NOT: = OpTypeInt 32 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] UClamp %i %ushort_0 %ushort_3 + ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_3 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -404,7 +404,7 @@ ; CHECK-DAG: %long_3 = OpConstant %long 3 ; CHECK-NOT: = OpTypeInt 32 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_3 + ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_3 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -425,11 +425,11 @@ << MainPrefix() << R"( ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-NOT: = OpTypeInt 32 - ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0 - ; CHECK-DAG: %ulong_3 = OpConstant %ulong 3 + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_3 = OpConstant %long 3 ; CHECK-NOT: = OpTypeInt 32 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_3 + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_3 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -486,10 +486,9 @@ %uint_1 = OpConstant %uint 1 %uint_4 = OpConstant %uint 4 )" << MainPrefix() << R"( - ; CHECK: %uint_3 = OpConstant %uint 3 + ; CHECK: %int_3 = OpConstant %int 3 %var = OpVariable %var_ty Function)" - << ACCheck(ac, "%uint_4 %uint_1", "%uint_3 %uint_1") - << MainSuffix(); + << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); } } @@ -529,7 +528,7 @@ ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_3 = OpConstant %int 3 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3 + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -585,7 +584,7 @@ ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_199 = OpConstant %int 199 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199 + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -606,11 +605,11 @@ %i = OpUndef %short )" << MainPrefix() << R"( ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" - ; CHECK-DAG: %uint_0 = OpConstant %uint 0 - ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999 + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_69999 = OpConstant %int 69999 ; CHECK: OpLabel ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999 + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -631,11 +630,11 @@ %i = OpUndef %ushort )" << MainPrefix() << R"( ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" - ; CHECK-DAG: %uint_0 = OpConstant %uint 0 - ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999 + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_69999 = OpConstant %int 69999 ; CHECK: OpLabel ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999 + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -656,10 +655,10 @@ %i = OpUndef %uint )" << MainPrefix() << R"( ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" - ; CHECK-DAG: %uint_0 = OpConstant %uint 0 - ; CHECK-DAG: %uint_199 = OpConstant %uint 199 + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_199 = OpConstant %int 199 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %uint_199 + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -683,7 +682,7 @@ ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_199 = OpConstant %int 199 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199 + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -707,7 +706,7 @@ ; CHECK-DAG: %long_0 = OpConstant %long 0 ; CHECK-DAG: %long_199 = OpConstant %long 199 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_199 + ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -728,16 +727,91 @@ %i = OpUndef %ulong )" << MainPrefix() << R"( ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" - ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0 - ; CHECK-DAG: %ulong_199 = OpConstant %ulong 199 + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_199 = OpConstant %long 199 ; CHECK: OpLabel - ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_199 + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); } } +TEST_F(GraphicsRobustAccessTest, + ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() + << TypesInt() << TypesFloat() << R"( + %uint_50000 = OpConstant %uint 50000 + %arr = OpTypeArray %float %uint_50000 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %ushort + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %short_0 = OpConstant %short 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]] + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, + ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %uint_3000000000 = OpConstant %uint 3000000000 + %arr = OpTypeArray %float %uint_3000000000 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %uint + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]] + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, + ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesLong() + << TypesFloat() + // 2^63 == 9,223,372,036,854,775,807 + << R"( + %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999 + %arr = OpTypeArray %float %ulong_9223372036854775999 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %ulong + )" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]] + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); + } +} + TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) { for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -753,9 +827,11 @@ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-DAG: %uint_0 = OpConstant %uint 0 ; CHECK-DAG: %uint_1 = OpConstant %uint 1 + ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647 ; CHECK: OpLabel ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1 - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %uint_5 %uint_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]] %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -908,11 +984,13 @@ %int_0 = OpConstant %int 0 %int_2 = OpConstant %int 2 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" - ; CHECK: %int_1 = OpConstant %int 1 + ; CHECK-DAG: %int_1 = OpConstant %int 1 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 - ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %int_0 %int_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]") << MainSuffix(); @@ -937,11 +1015,13 @@ ; CHECK: %uint = OpTypeInt 32 0 ; CHECK-DAG: %uint_1 = OpConstant %uint 1 ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1 ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]") << MainSuffix(); @@ -966,11 +1046,13 @@ ; CHECK: %uint = OpTypeInt 32 0 ; CHECK-DAG: %uint_1 = OpConstant %uint 1 ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1 ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]") << MainSuffix(); @@ -993,10 +1075,12 @@ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-DAG: %int_1 = OpConstant %int 1 ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 - ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix(); @@ -1019,10 +1103,12 @@ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-DAG: %uint_1 = OpConstant %uint 1 ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1 - ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix(); @@ -1046,11 +1132,13 @@ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-DAG: %long_0 = OpConstant %long 0 ; CHECK-DAG: %long_1 = OpConstant %long 1 + ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]] ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1 - ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -1073,11 +1161,13 @@ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0 ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1 + ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]] ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1 - ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix(); SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true); @@ -1109,11 +1199,13 @@ ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 - ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]] - ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %int_3 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]] + ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3 )" << MainPrefix() << ACCheck(ac, "%int_2 %i %int_1 %j", "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]") @@ -1148,6 +1240,7 @@ ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_9 = OpConstant %int 9 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel ; This access chain is manufatured only so we can compute the array length. ; Note that the %int_9 is already clamped @@ -1155,7 +1248,8 @@ << R"( %[[ssbo_p]] %var %int_9 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 - ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]] + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]] )" << MainPrefix() << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2", "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2") @@ -1195,8 +1289,9 @@ ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_9 = OpConstant %int 9 ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel - ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9 + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9 ; CHECK: %ac_ssbo = )" << ac << R"( %ssbo_pty %var %[[clamp_i]] ; CHECK: %ac_rtarr = )" @@ -1207,8 +1302,9 @@ ; definition to find the base pointer %ac_ssbo. ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 - ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]] - ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]] + ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3 ; CHECK: %ac = )" << ac << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]] ; CHECK-NOT: AccessChain @@ -1258,8 +1354,9 @@ ; CHECK-DAG: %int_0 = OpConstant %int 0 ; CHECK-DAG: %int_9 = OpConstant %int 9 ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel - ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9 + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9 ; CHECK: %ac_ssbo = )" << ac << R"( %ssbo_pty %var %[[clamp_i]] ; CHECK: %bb1 = OpLabel @@ -1272,8 +1369,9 @@ ; definition to find the base pointer %ac_ssbo. ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 - ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]] - ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]] + ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3 ; CHECK: %ac = )" << ac << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]] ; CHECK-NOT: AccessChain
diff --git a/third_party/SPIRV-Tools/test/opt/inline_test.cpp b/third_party/SPIRV-Tools/test/opt/inline_test.cpp index fac49ca..f44c04a 100644 --- a/third_party/SPIRV-Tools/test/opt/inline_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
@@ -1819,7 +1819,7 @@ %9 = OpLabel OpBranch %10 %10 = OpLabel -OpLoopMerge %12 %13 None +OpLoopMerge %12 %10 None OpBranch %13 %13 = OpLabel OpBranchConditional %true %10 %12 @@ -1980,7 +1980,7 @@ OpBranch %13 %13 = OpLabel %14 = OpCopyObject %bool %false -OpLoopMerge %16 %19 None +OpLoopMerge %16 %13 None OpBranch %17 %17 = OpLabel %18 = OpCopyObject %bool %true @@ -2145,7 +2145,7 @@ %19 = OpLabel %20 = OpCopyObject %int %int_2 %25 = OpCopyObject %int %int_0 -OpLoopMerge %23 %26 None +OpLoopMerge %23 %19 None OpBranch %26 %27 = OpLabel %28 = OpCopyObject %int %int_1
diff --git a/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp b/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp index b4db2d5..d867b01 100644 --- a/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/inst_bindless_check_test.cpp
@@ -14,7 +14,6 @@ // limitations under the License. // Bindless Check Instrumentation Tests. -// Tests ending with V2 use version 2 record format. #include <string> #include <vector> @@ -29,235 +28,6 @@ using InstBindlessTest = PassTest<::testing::Test>; -TEST_F(InstBindlessTest, Simple) { - // Texture2D g_tColor[128]; - // - // layout(push_constant) cbuffer PerViewConstantBuffer_t - // { - // uint g_nDataIdx; - // }; - // - // SamplerState g_sAniso; - // - // struct PS_INPUT - // { - // float2 vTextureCoords : TEXCOORD2; - // }; - // - // struct PS_OUTPUT - // { - // float4 vColor : SV_Target0; - // }; - // - // PS_OUTPUT MainPs(PS_INPUT i) - // { - // PS_OUTPUT ps_output; - // ps_output.vColor = - // g_tColor[ g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy); - // return ps_output; - // } - - const std::string entry_before = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -)"; - - const std::string entry_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -)"; - - const std::string names_annots = - R"(OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -)"; - - const std::string new_annots = - R"(OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -)"; - - const std::string consts_types_vars = - R"(%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%16 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_16_uint_128 = OpTypeArray %16 %uint_128 -%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 -%24 = OpTypeSampler -%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 -%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant -%26 = OpTypeSampledImage %16 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string new_consts_types_vars = - R"(%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%48 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_56 = OpConstant %uint 56 -%103 = OpConstantNull %v4float -)"; - - const std::string func_pt1 = - R"(%MainPs = OpFunction %void None %10 -%29 = OpLabel -%30 = OpLoad %v2float %i_vTextureCoords -%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%32 = OpLoad %uint %31 -%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 -%34 = OpLoad %16 %33 -%35 = OpLoad %24 %g_sAniso -%36 = OpSampledImage %26 %34 %35 -)"; - - const std::string func_pt2_before = - R"(%37 = OpImageSampleImplicitLod %v4float %36 %30 -OpStore %_entryPointOutput_vColor %37 -OpReturn -OpFunctionEnd -)"; - - const std::string func_pt2_after = - R"(%40 = OpULessThan %bool %32 %uint_128 -OpSelectionMerge %41 None -OpBranchConditional %40 %42 %43 -%42 = OpLabel -%44 = OpLoad %16 %33 -%45 = OpSampledImage %26 %44 %35 -%46 = OpImageSampleImplicitLod %v4float %45 %30 -OpBranch %41 -%43 = OpLabel -%102 = OpFunctionCall %void %47 %uint_56 %uint_0 %32 %uint_128 -OpBranch %41 -%41 = OpLabel -%104 = OpPhi %v4float %46 %42 %103 %43 -OpStore %_entryPointOutput_vColor %104 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%47 = OpFunction %void None %48 -%49 = OpFunctionParameter %uint -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpLabel -%59 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%62 = OpAtomicIAdd %uint %59 %uint_4 %uint_0 %uint_9 -%63 = OpIAdd %uint %62 %uint_9 -%64 = OpArrayLength %uint %57 1 -%65 = OpULessThanEqual %bool %63 %64 -OpSelectionMerge %66 None -OpBranchConditional %65 %67 %66 -%67 = OpLabel -%68 = OpIAdd %uint %62 %uint_0 -%70 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %68 -OpStore %70 %uint_9 -%72 = OpIAdd %uint %62 %uint_1 -%73 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %72 -OpStore %73 %uint_23 -%75 = OpIAdd %uint %62 %uint_2 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %49 -%78 = OpIAdd %uint %62 %uint_3 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %78 -OpStore %79 %uint_4 -%82 = OpLoad %v4float %gl_FragCoord -%84 = OpBitcast %v4uint %82 -%85 = OpCompositeExtract %uint %84 0 -%86 = OpIAdd %uint %62 %uint_4 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %86 -OpStore %87 %85 -%88 = OpCompositeExtract %uint %84 1 -%90 = OpIAdd %uint %62 %uint_5 -%91 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %90 -OpStore %91 %88 -%93 = OpIAdd %uint %62 %uint_6 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93 -OpStore %94 %50 -%96 = OpIAdd %uint %62 %uint_7 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96 -OpStore %97 %51 -%99 = OpIAdd %uint %62 %uint_8 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99 -OpStore %100 %52 -OpBranch %66 -%66 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - entry_before + names_annots + consts_types_vars + func_pt1 + - func_pt2_before, - entry_after + names_annots + new_annots + consts_types_vars + - new_consts_types_vars + func_pt1 + func_pt2_after + output_func, - true, true, 7u, 23u, false, false, 1u); -} - TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) { // Texture2D g_tColor[128]; // @@ -336,1872 +106,7 @@ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>(before, before, true, true, 7u, - 23u, false, false, 1u); -} - -TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { - // Texture2D g_tColor[128]; - // - // layout(push_constant) cbuffer PerViewConstantBuffer_t - // { - // uint g_nDataIdx; - // uint g_nDataIdx2; - // }; - // - // SamplerState g_sAniso; - // - // struct PS_INPUT - // { - // float2 vTextureCoords : TEXCOORD2; - // }; - // - // struct PS_OUTPUT - // { - // float4 vColor : SV_Target0; - // }; - // - // PS_OUTPUT MainPs(PS_INPUT i) - // { - // PS_OUTPUT ps_output; - // - // float t = g_tColor[g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy); - // float t2 = g_tColor[g_nDataIdx2].Sample(g_sAniso, i.vTextureCoords.xy); - // ps_output.vColor = t + t2; - // return ps_output; - // } - - const std::string defs_before = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%int_1 = OpConstant %int 1 -%17 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_17_uint_128 = OpTypeArray %17 %uint_128 -%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 -%25 = OpTypeSampler -%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 -%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant -%27 = OpTypeSampledImage %17 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_63 Block -OpMemberDecorate %_struct_63 0 Offset 0 -OpMemberDecorate %_struct_63 1 Offset 4 -OpDecorate %65 DescriptorSet 7 -OpDecorate %65 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%int_1 = OpConstant %int 1 -%17 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_17_uint_128 = OpTypeArray %17 %uint_128 -%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 -%25 = OpTypeSampler -%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 -%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant -%27 = OpTypeSampledImage %17 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%56 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_63 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 -%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_58 = OpConstant %uint 58 -%111 = OpConstantNull %v4float -%uint_64 = OpConstant %uint 64 -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %10 -%30 = OpLabel -%31 = OpLoad %v2float %i_vTextureCoords -%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%33 = OpLoad %uint %32 -%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 -%35 = OpLoad %17 %34 -%36 = OpLoad %25 %g_sAniso -%37 = OpSampledImage %27 %35 %36 -%38 = OpImageSampleImplicitLod %v4float %37 %31 -%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 -%40 = OpLoad %uint %39 -%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 -%42 = OpLoad %17 %41 -%43 = OpSampledImage %27 %42 %36 -%44 = OpImageSampleImplicitLod %v4float %43 %31 -%45 = OpFAdd %v4float %38 %44 -OpStore %_entryPointOutput_vColor %45 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %10 -%30 = OpLabel -%31 = OpLoad %v2float %i_vTextureCoords -%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%33 = OpLoad %uint %32 -%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 -%35 = OpLoad %17 %34 -%36 = OpLoad %25 %g_sAniso -%37 = OpSampledImage %27 %35 %36 -%48 = OpULessThan %bool %33 %uint_128 -OpSelectionMerge %49 None -OpBranchConditional %48 %50 %51 -%50 = OpLabel -%52 = OpLoad %17 %34 -%53 = OpSampledImage %27 %52 %36 -%54 = OpImageSampleImplicitLod %v4float %53 %31 -OpBranch %49 -%51 = OpLabel -%110 = OpFunctionCall %void %55 %uint_58 %uint_0 %33 %uint_128 -OpBranch %49 -%49 = OpLabel -%112 = OpPhi %v4float %54 %50 %111 %51 -%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 -%40 = OpLoad %uint %39 -%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 -%42 = OpLoad %17 %41 -%43 = OpSampledImage %27 %42 %36 -%113 = OpULessThan %bool %40 %uint_128 -OpSelectionMerge %114 None -OpBranchConditional %113 %115 %116 -%115 = OpLabel -%117 = OpLoad %17 %41 -%118 = OpSampledImage %27 %117 %36 -%119 = OpImageSampleImplicitLod %v4float %118 %31 -OpBranch %114 -%116 = OpLabel -%121 = OpFunctionCall %void %55 %uint_64 %uint_0 %40 %uint_128 -OpBranch %114 -%114 = OpLabel -%122 = OpPhi %v4float %119 %115 %111 %116 -%45 = OpFAdd %v4float %112 %122 -OpStore %_entryPointOutput_vColor %45 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%55 = OpFunction %void None %56 -%57 = OpFunctionParameter %uint -%58 = OpFunctionParameter %uint -%59 = OpFunctionParameter %uint -%60 = OpFunctionParameter %uint -%61 = OpLabel -%67 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 -%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_9 -%71 = OpIAdd %uint %70 %uint_9 -%72 = OpArrayLength %uint %65 1 -%73 = OpULessThanEqual %bool %71 %72 -OpSelectionMerge %74 None -OpBranchConditional %73 %75 %74 -%75 = OpLabel -%76 = OpIAdd %uint %70 %uint_0 -%78 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %76 -OpStore %78 %uint_9 -%80 = OpIAdd %uint %70 %uint_1 -%81 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %80 -OpStore %81 %uint_23 -%83 = OpIAdd %uint %70 %uint_2 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %83 -OpStore %84 %57 -%86 = OpIAdd %uint %70 %uint_3 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %86 -OpStore %87 %uint_4 -%90 = OpLoad %v4float %gl_FragCoord -%92 = OpBitcast %v4uint %90 -%93 = OpCompositeExtract %uint %92 0 -%94 = OpIAdd %uint %70 %uint_4 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 -OpStore %95 %93 -%96 = OpCompositeExtract %uint %92 1 -%98 = OpIAdd %uint %70 %uint_5 -%99 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %98 -OpStore %99 %96 -%101 = OpIAdd %uint %70 %uint_6 -%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101 -OpStore %102 %58 -%104 = OpIAdd %uint %70 %uint_7 -%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104 -OpStore %105 %59 -%107 = OpIAdd %uint %70 %uint_8 -%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107 -OpStore %108 %60 -OpBranch %74 -%74 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 1u); -} - -TEST_F(InstBindlessTest, InstrumentOpImage) { - // This test verifies that the pass will correctly instrument shader - // using OpImage. This test was created by editing the SPIR-V - // from the Simple test. - - const std::string defs_before = - R"(OpCapability Shader -OpCapability StorageImageReadWithoutFormat -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%v2int = OpTypeVector %int 2 -%int_0 = OpConstant %int 0 -%20 = OpTypeImage %float 2D 0 0 0 0 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%39 = OpTypeSampledImage %20 -%_arr_39_uint_128 = OpTypeArray %39 %uint_128 -%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 -%_ptr_Input_v2int = OpTypePointer Input %v2int -%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability StorageImageReadWithoutFormat -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_51 Block -OpMemberDecorate %_struct_51 0 Offset 0 -OpMemberDecorate %_struct_51 1 Offset 4 -OpDecorate %53 DescriptorSet 7 -OpDecorate %53 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%v2int = OpTypeVector %int 2 -%int_0 = OpConstant %int 0 -%15 = OpTypeImage %float 2D 0 0 0 0 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%18 = OpTypeSampledImage %15 -%_arr_18_uint_128 = OpTypeArray %18 %uint_128 -%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 -%_ptr_Input_v2int = OpTypePointer Input %v2int -%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%44 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_51 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_51 = OpTypePointer StorageBuffer %_struct_51 -%53 = OpVariable %_ptr_StorageBuffer__struct_51 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_51 = OpConstant %uint 51 -%99 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 -%5 = OpLabel -%53 = OpLoad %v2int %i_vTextureCoords -%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%64 = OpLoad %uint %63 -%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64 -%66 = OpLoad %39 %65 -%75 = OpImage %20 %66 -%71 = OpImageRead %v4float %75 %53 -OpStore %_entryPointOutput_vColor %71 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %9 -%26 = OpLabel -%27 = OpLoad %v2int %i_vTextureCoords -%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%29 = OpLoad %uint %28 -%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 -%31 = OpLoad %18 %30 -%32 = OpImage %15 %31 -%36 = OpULessThan %bool %29 %uint_128 -OpSelectionMerge %37 None -OpBranchConditional %36 %38 %39 -%38 = OpLabel -%40 = OpLoad %18 %30 -%41 = OpImage %15 %40 -%42 = OpImageRead %v4float %41 %27 -OpBranch %37 -%39 = OpLabel -%98 = OpFunctionCall %void %43 %uint_51 %uint_0 %29 %uint_128 -OpBranch %37 -%37 = OpLabel -%100 = OpPhi %v4float %42 %38 %99 %39 -OpStore %_entryPointOutput_vColor %100 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%43 = OpFunction %void None %44 -%45 = OpFunctionParameter %uint -%46 = OpFunctionParameter %uint -%47 = OpFunctionParameter %uint -%48 = OpFunctionParameter %uint -%49 = OpLabel -%55 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_0 -%58 = OpAtomicIAdd %uint %55 %uint_4 %uint_0 %uint_9 -%59 = OpIAdd %uint %58 %uint_9 -%60 = OpArrayLength %uint %53 1 -%61 = OpULessThanEqual %bool %59 %60 -OpSelectionMerge %62 None -OpBranchConditional %61 %63 %62 -%63 = OpLabel -%64 = OpIAdd %uint %58 %uint_0 -%66 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %64 -OpStore %66 %uint_9 -%68 = OpIAdd %uint %58 %uint_1 -%69 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %68 -OpStore %69 %uint_23 -%71 = OpIAdd %uint %58 %uint_2 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %71 -OpStore %72 %45 -%74 = OpIAdd %uint %58 %uint_3 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %74 -OpStore %75 %uint_4 -%78 = OpLoad %v4float %gl_FragCoord -%80 = OpBitcast %v4uint %78 -%81 = OpCompositeExtract %uint %80 0 -%82 = OpIAdd %uint %58 %uint_4 -%83 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %82 -OpStore %83 %81 -%84 = OpCompositeExtract %uint %80 1 -%86 = OpIAdd %uint %58 %uint_5 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %86 -OpStore %87 %84 -%89 = OpIAdd %uint %58 %uint_6 -%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89 -OpStore %90 %46 -%92 = OpIAdd %uint %58 %uint_7 -%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92 -OpStore %93 %47 -%95 = OpIAdd %uint %58 %uint_8 -%96 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %95 -OpStore %96 %48 -OpBranch %62 -%62 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 1u); -} - -TEST_F(InstBindlessTest, InstrumentSampledImage) { - // This test verifies that the pass will correctly instrument shader - // using sampled image. This test was created by editing the SPIR-V - // from the Simple test. - - const std::string defs_before = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%20 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%39 = OpTypeSampledImage %20 -%_arr_39_uint_128 = OpTypeArray %39 %uint_128 -%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_49 Block -OpMemberDecorate %_struct_49 0 Offset 0 -OpMemberDecorate %_struct_49 1 Offset 4 -OpDecorate %51 DescriptorSet 7 -OpDecorate %51 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%15 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%18 = OpTypeSampledImage %15 -%_arr_18_uint_128 = OpTypeArray %18 %uint_128 -%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%42 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_49 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_49 = OpTypePointer StorageBuffer %_struct_49 -%51 = OpVariable %_ptr_StorageBuffer__struct_49 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_49 = OpConstant %uint 49 -%97 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 -%5 = OpLabel -%53 = OpLoad %v2float %i_vTextureCoords -%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%64 = OpLoad %uint %63 -%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64 -%66 = OpLoad %39 %65 -%71 = OpImageSampleImplicitLod %v4float %66 %53 -OpStore %_entryPointOutput_vColor %71 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %9 -%26 = OpLabel -%27 = OpLoad %v2float %i_vTextureCoords -%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%29 = OpLoad %uint %28 -%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 -%31 = OpLoad %18 %30 -%35 = OpULessThan %bool %29 %uint_128 -OpSelectionMerge %36 None -OpBranchConditional %35 %37 %38 -%37 = OpLabel -%39 = OpLoad %18 %30 -%40 = OpImageSampleImplicitLod %v4float %39 %27 -OpBranch %36 -%38 = OpLabel -%96 = OpFunctionCall %void %41 %uint_49 %uint_0 %29 %uint_128 -OpBranch %36 -%36 = OpLabel -%98 = OpPhi %v4float %40 %37 %97 %38 -OpStore %_entryPointOutput_vColor %98 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%41 = OpFunction %void None %42 -%43 = OpFunctionParameter %uint -%44 = OpFunctionParameter %uint -%45 = OpFunctionParameter %uint -%46 = OpFunctionParameter %uint -%47 = OpLabel -%53 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_0 -%56 = OpAtomicIAdd %uint %53 %uint_4 %uint_0 %uint_9 -%57 = OpIAdd %uint %56 %uint_9 -%58 = OpArrayLength %uint %51 1 -%59 = OpULessThanEqual %bool %57 %58 -OpSelectionMerge %60 None -OpBranchConditional %59 %61 %60 -%61 = OpLabel -%62 = OpIAdd %uint %56 %uint_0 -%64 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %62 -OpStore %64 %uint_9 -%66 = OpIAdd %uint %56 %uint_1 -%67 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %66 -OpStore %67 %uint_23 -%69 = OpIAdd %uint %56 %uint_2 -%70 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %69 -OpStore %70 %43 -%72 = OpIAdd %uint %56 %uint_3 -%73 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %72 -OpStore %73 %uint_4 -%76 = OpLoad %v4float %gl_FragCoord -%78 = OpBitcast %v4uint %76 -%79 = OpCompositeExtract %uint %78 0 -%80 = OpIAdd %uint %56 %uint_4 -%81 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %80 -OpStore %81 %79 -%82 = OpCompositeExtract %uint %78 1 -%84 = OpIAdd %uint %56 %uint_5 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %84 -OpStore %85 %82 -%87 = OpIAdd %uint %56 %uint_6 -%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87 -OpStore %88 %44 -%90 = OpIAdd %uint %56 %uint_7 -%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90 -OpStore %91 %45 -%93 = OpIAdd %uint %56 %uint_8 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %93 -OpStore %94 %46 -OpBranch %60 -%60 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 1u); -} - -TEST_F(InstBindlessTest, InstrumentImageWrite) { - // This test verifies that the pass will correctly instrument shader - // doing bindless image write. This test was created by editing the SPIR-V - // from the Simple test. - - const std::string defs_before = - R"(OpCapability Shader -OpCapability StorageImageWriteWithoutFormat -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%v2int = OpTypeVector %int 2 -%int_0 = OpConstant %int 0 -%20 = OpTypeImage %float 2D 0 0 0 0 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%80 = OpConstantNull %v4float -%_arr_20_uint_128 = OpTypeArray %20 %uint_128 -%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 -%_ptr_Input_v2int = OpTypePointer Input %v2int -%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability StorageImageWriteWithoutFormat -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_48 Block -OpMemberDecorate %_struct_48 0 Offset 0 -OpMemberDecorate %_struct_48 1 Offset 4 -OpDecorate %50 DescriptorSet 7 -OpDecorate %50 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%v2int = OpTypeVector %int 2 -%int_0 = OpConstant %int 0 -%16 = OpTypeImage %float 2D 0 0 0 0 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%19 = OpConstantNull %v4float -%_arr_16_uint_128 = OpTypeArray %16 %uint_128 -%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 -%_ptr_Input_v2int = OpTypePointer Input %v2int -%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%41 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_48 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_48 = OpTypePointer StorageBuffer %_struct_48 -%50 = OpVariable %_ptr_StorageBuffer__struct_48 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_51 = OpConstant %uint 51 -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 -%5 = OpLabel -%53 = OpLoad %v2int %i_vTextureCoords -%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%64 = OpLoad %uint %63 -%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 -%66 = OpLoad %20 %65 -OpImageWrite %66 %53 %80 -OpStore %_entryPointOutput_vColor %80 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %9 -%27 = OpLabel -%28 = OpLoad %v2int %i_vTextureCoords -%29 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%30 = OpLoad %uint %29 -%31 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %30 -%32 = OpLoad %16 %31 -%35 = OpULessThan %bool %30 %uint_128 -OpSelectionMerge %36 None -OpBranchConditional %35 %37 %38 -%37 = OpLabel -%39 = OpLoad %16 %31 -OpImageWrite %39 %28 %19 -OpBranch %36 -%38 = OpLabel -%95 = OpFunctionCall %void %40 %uint_51 %uint_0 %30 %uint_128 -OpBranch %36 -%36 = OpLabel -OpStore %_entryPointOutput_vColor %19 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%40 = OpFunction %void None %41 -%42 = OpFunctionParameter %uint -%43 = OpFunctionParameter %uint -%44 = OpFunctionParameter %uint -%45 = OpFunctionParameter %uint -%46 = OpLabel -%52 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_0 -%55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_9 -%56 = OpIAdd %uint %55 %uint_9 -%57 = OpArrayLength %uint %50 1 -%58 = OpULessThanEqual %bool %56 %57 -OpSelectionMerge %59 None -OpBranchConditional %58 %60 %59 -%60 = OpLabel -%61 = OpIAdd %uint %55 %uint_0 -%63 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %61 -OpStore %63 %uint_9 -%65 = OpIAdd %uint %55 %uint_1 -%66 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %65 -OpStore %66 %uint_23 -%68 = OpIAdd %uint %55 %uint_2 -%69 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %68 -OpStore %69 %42 -%71 = OpIAdd %uint %55 %uint_3 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %71 -OpStore %72 %uint_4 -%75 = OpLoad %v4float %gl_FragCoord -%77 = OpBitcast %v4uint %75 -%78 = OpCompositeExtract %uint %77 0 -%79 = OpIAdd %uint %55 %uint_4 -%80 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %79 -OpStore %80 %78 -%81 = OpCompositeExtract %uint %77 1 -%83 = OpIAdd %uint %55 %uint_5 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %83 -OpStore %84 %81 -%86 = OpIAdd %uint %55 %uint_6 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86 -OpStore %87 %43 -%89 = OpIAdd %uint %55 %uint_7 -%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89 -OpStore %90 %44 -%92 = OpIAdd %uint %55 %uint_8 -%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92 -OpStore %93 %45 -OpBranch %59 -%59 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 1u); -} - -TEST_F(InstBindlessTest, InstrumentVertexSimple) { - // This test verifies that the pass will correctly instrument shader - // doing bindless image write. This test was created by editing the SPIR-V - // from the Simple test. - - const std::string defs_before = - R"(OpCapability Shader -OpCapability Sampled1D -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %main "main" %_ %coords2D -OpSource GLSL 450 -OpName %main "main" -OpName %lod "lod" -OpName %coords1D "coords1D" -OpName %gl_PerVertex "gl_PerVertex" -OpMemberName %gl_PerVertex 0 "gl_Position" -OpMemberName %gl_PerVertex 1 "gl_PointSize" -OpMemberName %gl_PerVertex 2 "gl_ClipDistance" -OpMemberName %gl_PerVertex 3 "gl_CullDistance" -OpName %_ "" -OpName %texSampler1D "texSampler1D" -OpName %foo "foo" -OpMemberName %foo 0 "g_idx" -OpName %__0 "" -OpName %coords2D "coords2D" -OpMemberDecorate %gl_PerVertex 0 BuiltIn Position -OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize -OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance -OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance -OpDecorate %gl_PerVertex Block -OpDecorate %texSampler1D DescriptorSet 0 -OpDecorate %texSampler1D Binding 3 -OpMemberDecorate %foo 0 Offset 0 -OpDecorate %foo Block -OpDecorate %__0 DescriptorSet 0 -OpDecorate %__0 Binding 5 -OpDecorate %coords2D Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Function_float = OpTypePointer Function %float -%float_3 = OpConstant %float 3 -%float_1_78900003 = OpConstant %float 1.78900003 -%v4float = OpTypeVector %float 4 -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_arr_float_uint_1 = OpTypeArray %float %uint_1 -%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 -%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex -%_ = OpVariable %_ptr_Output_gl_PerVertex Output -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%21 = OpTypeImage %float 1D 0 0 0 1 Unknown -%22 = OpTypeSampledImage %21 -%uint_128 = OpConstant %uint 128 -%_arr_22_uint_128 = OpTypeArray %22 %uint_128 -%_ptr_UniformConstant__arr_22_uint_128 = OpTypePointer UniformConstant %_arr_22_uint_128 -%texSampler1D = OpVariable %_ptr_UniformConstant__arr_22_uint_128 UniformConstant -%foo = OpTypeStruct %int -%_ptr_Uniform_foo = OpTypePointer Uniform %foo -%__0 = OpVariable %_ptr_Uniform_foo Uniform -%_ptr_Uniform_int = OpTypePointer Uniform %int -%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 -%_ptr_Output_v4float = OpTypePointer Output %v4float -%v2float = OpTypeVector %float 2 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%coords2D = OpVariable %_ptr_Input_v2float Input -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability Sampled1D -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexIndex %gl_InstanceIndex -OpSource GLSL 450 -OpName %main "main" -OpName %lod "lod" -OpName %coords1D "coords1D" -OpName %gl_PerVertex "gl_PerVertex" -OpMemberName %gl_PerVertex 0 "gl_Position" -OpMemberName %gl_PerVertex 1 "gl_PointSize" -OpMemberName %gl_PerVertex 2 "gl_ClipDistance" -OpMemberName %gl_PerVertex 3 "gl_CullDistance" -OpName %_ "" -OpName %texSampler1D "texSampler1D" -OpName %foo "foo" -OpMemberName %foo 0 "g_idx" -OpName %__0 "" -OpName %coords2D "coords2D" -OpMemberDecorate %gl_PerVertex 0 BuiltIn Position -OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize -OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance -OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance -OpDecorate %gl_PerVertex Block -OpDecorate %texSampler1D DescriptorSet 0 -OpDecorate %texSampler1D Binding 3 -OpMemberDecorate %foo 0 Offset 0 -OpDecorate %foo Block -OpDecorate %__0 DescriptorSet 0 -OpDecorate %__0 Binding 5 -OpDecorate %coords2D Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_61 Block -OpMemberDecorate %_struct_61 0 Offset 0 -OpMemberDecorate %_struct_61 1 Offset 4 -OpDecorate %63 DescriptorSet 7 -OpDecorate %63 Binding 0 -OpDecorate %gl_VertexIndex BuiltIn VertexIndex -OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex -%void = OpTypeVoid -%12 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Function_float = OpTypePointer Function %float -%float_3 = OpConstant %float 3 -%float_1_78900003 = OpConstant %float 1.78900003 -%v4float = OpTypeVector %float 4 -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_arr_float_uint_1 = OpTypeArray %float %uint_1 -%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 -%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex -%_ = OpVariable %_ptr_Output_gl_PerVertex Output -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%24 = OpTypeImage %float 1D 0 0 0 1 Unknown -%25 = OpTypeSampledImage %24 -%uint_128 = OpConstant %uint 128 -%_arr_25_uint_128 = OpTypeArray %25 %uint_128 -%_ptr_UniformConstant__arr_25_uint_128 = OpTypePointer UniformConstant %_arr_25_uint_128 -%texSampler1D = OpVariable %_ptr_UniformConstant__arr_25_uint_128 UniformConstant -%foo = OpTypeStruct %int -%_ptr_Uniform_foo = OpTypePointer Uniform %foo -%__0 = OpVariable %_ptr_Uniform_foo Uniform -%_ptr_Uniform_int = OpTypePointer Uniform %int -%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 -%_ptr_Output_v4float = OpTypePointer Output %v4float -%v2float = OpTypeVector %float 2 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%coords2D = OpVariable %_ptr_Input_v2float Input -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%54 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_61 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_61 = OpTypePointer StorageBuffer %_struct_61 -%63 = OpVariable %_ptr_StorageBuffer__struct_61 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_uint = OpTypePointer Input %uint -%gl_VertexIndex = OpVariable %_ptr_Input_uint Input -%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_74 = OpConstant %uint 74 -%106 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%lod = OpVariable %_ptr_Function_float Function -%coords1D = OpVariable %_ptr_Function_float Function -OpStore %lod %float_3 -OpStore %coords1D %float_1_78900003 -%31 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 -%32 = OpLoad %int %31 -%34 = OpAccessChain %_ptr_UniformConstant_22 %texSampler1D %32 -%35 = OpLoad %22 %34 -%36 = OpLoad %float %coords1D -%37 = OpLoad %float %lod -%38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 -%40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 -OpStore %40 %38 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %12 -%35 = OpLabel -%lod = OpVariable %_ptr_Function_float Function -%coords1D = OpVariable %_ptr_Function_float Function -OpStore %lod %float_3 -OpStore %coords1D %float_1_78900003 -%36 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 -%37 = OpLoad %int %36 -%38 = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D %37 -%39 = OpLoad %25 %38 -%40 = OpLoad %float %coords1D -%41 = OpLoad %float %lod -%46 = OpULessThan %bool %37 %uint_128 -OpSelectionMerge %47 None -OpBranchConditional %46 %48 %49 -%48 = OpLabel -%50 = OpLoad %25 %38 -%51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41 -OpBranch %47 -%49 = OpLabel -%52 = OpBitcast %uint %37 -%105 = OpFunctionCall %void %53 %uint_74 %uint_0 %52 %uint_128 -OpBranch %47 -%47 = OpLabel -%107 = OpPhi %v4float %51 %48 %106 %49 -%43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 -OpStore %43 %107 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%53 = OpFunction %void None %54 -%55 = OpFunctionParameter %uint -%56 = OpFunctionParameter %uint -%57 = OpFunctionParameter %uint -%58 = OpFunctionParameter %uint -%59 = OpLabel -%65 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_0 -%68 = OpAtomicIAdd %uint %65 %uint_4 %uint_0 %uint_9 -%69 = OpIAdd %uint %68 %uint_9 -%70 = OpArrayLength %uint %63 1 -%71 = OpULessThanEqual %bool %69 %70 -OpSelectionMerge %72 None -OpBranchConditional %71 %73 %72 -%73 = OpLabel -%74 = OpIAdd %uint %68 %uint_0 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %74 -OpStore %75 %uint_9 -%77 = OpIAdd %uint %68 %uint_1 -%78 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %77 -OpStore %78 %uint_23 -%80 = OpIAdd %uint %68 %uint_2 -%81 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %80 -OpStore %81 %55 -%83 = OpIAdd %uint %68 %uint_3 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83 -OpStore %84 %uint_0 -%87 = OpLoad %uint %gl_VertexIndex -%88 = OpIAdd %uint %68 %uint_4 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88 -OpStore %89 %87 -%91 = OpLoad %uint %gl_InstanceIndex -%93 = OpIAdd %uint %68 %uint_5 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93 -OpStore %94 %91 -%96 = OpIAdd %uint %68 %uint_6 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96 -OpStore %97 %56 -%99 = OpIAdd %uint %68 %uint_7 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99 -OpStore %100 %57 -%102 = OpIAdd %uint %68 %uint_8 -%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102 -OpStore %103 %58 -OpBranch %72 -%72 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 1u); -} - -TEST_F(InstBindlessTest, MultipleDebugFunctions) { - // Same source as Simple, but compiled -g and not optimized, especially not - // inlined. The OpSource has had the source extracted for the sake of brevity. - - const std::string defs_before = - R"(OpCapability Shader -%2 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -%1 = OpString "foo5.frag" -OpSource HLSL 500 %1 -OpName %MainPs "MainPs" -OpName %PS_INPUT "PS_INPUT" -OpMemberName %PS_INPUT 0 "vTextureCoords" -OpName %PS_OUTPUT "PS_OUTPUT" -OpMemberName %PS_OUTPUT 0 "vColor" -OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;" -OpName %i "i" -OpName %ps_output "ps_output" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_0 "i" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpName %param "param" -OpDecorate %g_tColor DescriptorSet 0 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %g_sAniso Binding 1 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%4 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%PS_INPUT = OpTypeStruct %v2float -%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT -%v4float = OpTypeVector %float 4 -%PS_OUTPUT = OpTypeStruct %v4float -%13 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT -%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%21 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_21_uint_128 = OpTypeArray %21 %uint_128 -%_ptr_UniformConstant__arr_21_uint_128 = OpTypePointer UniformConstant %_arr_21_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_21_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 -%36 = OpTypeSampler -%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36 -%g_sAniso = OpVariable %_ptr_UniformConstant_36 UniformConstant -%40 = OpTypeSampledImage %21 -%_ptr_Function_v2float = OpTypePointer Function %v2float -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -%5 = OpString "foo5.frag" -OpSource HLSL 500 %5 -OpName %MainPs "MainPs" -OpName %PS_INPUT "PS_INPUT" -OpMemberName %PS_INPUT 0 "vTextureCoords" -OpName %PS_OUTPUT "PS_OUTPUT" -OpMemberName %PS_OUTPUT 0 "vColor" -OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;" -OpName %i "i" -OpName %ps_output "ps_output" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_0 "i" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpName %param "param" -OpDecorate %g_tColor DescriptorSet 0 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %g_sAniso Binding 1 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_77 Block -OpMemberDecorate %_struct_77 0 Offset 0 -OpMemberDecorate %_struct_77 1 Offset 4 -OpDecorate %79 DescriptorSet 7 -OpDecorate %79 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%18 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%PS_INPUT = OpTypeStruct %v2float -%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT -%v4float = OpTypeVector %float 4 -%PS_OUTPUT = OpTypeStruct %v4float -%23 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT -%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%27 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_27_uint_128 = OpTypeArray %27 %uint_128 -%_ptr_UniformConstant__arr_27_uint_128 = OpTypePointer UniformConstant %_arr_27_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_27_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27 -%35 = OpTypeSampler -%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 -%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant -%37 = OpTypeSampledImage %27 -%_ptr_Function_v2float = OpTypePointer Function %v2float -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%70 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_77 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77 -%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_93 = OpConstant %uint 93 -%125 = OpConstantNull %v4float -)"; - - const std::string func1_before = - R"(%MainPs = OpFunction %void None %4 -%6 = OpLabel -%i_0 = OpVariable %_ptr_Function_PS_INPUT Function -%param = OpVariable %_ptr_Function_PS_INPUT Function -OpLine %1 21 0 -%54 = OpLoad %v2float %i_vTextureCoords -%55 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0 -OpStore %55 %54 -%59 = OpLoad %PS_INPUT %i_0 -OpStore %param %59 -%60 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param -%61 = OpCompositeExtract %v4float %60 0 -OpStore %_entryPointOutput_vColor %61 -OpReturn -OpFunctionEnd -)"; - - const std::string func1_after = - R"(%MainPs = OpFunction %void None %18 -%42 = OpLabel -%i_0 = OpVariable %_ptr_Function_PS_INPUT Function -%param = OpVariable %_ptr_Function_PS_INPUT Function -OpLine %5 21 0 -%43 = OpLoad %v2float %i_vTextureCoords -%44 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0 -OpStore %44 %43 -%45 = OpLoad %PS_INPUT %i_0 -OpStore %param %45 -%46 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param -%47 = OpCompositeExtract %v4float %46 0 -OpStore %_entryPointOutput_vColor %47 -OpReturn -OpFunctionEnd -)"; - - const std::string func2_before = - R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13 -%i = OpFunctionParameter %_ptr_Function_PS_INPUT -%16 = OpLabel -%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function -OpLine %1 24 0 -%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%32 = OpLoad %uint %31 -%34 = OpAccessChain %_ptr_UniformConstant_21 %g_tColor %32 -%35 = OpLoad %21 %34 -%39 = OpLoad %36 %g_sAniso -%41 = OpSampledImage %40 %35 %39 -%43 = OpAccessChain %_ptr_Function_v2float %i %int_0 -%44 = OpLoad %v2float %43 -%45 = OpImageSampleImplicitLod %v4float %41 %44 -%47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 -OpStore %47 %45 -OpLine %1 25 0 -%48 = OpLoad %PS_OUTPUT %ps_output -OpReturnValue %48 -OpFunctionEnd -)"; - - const std::string func2_after = - R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %23 -%i = OpFunctionParameter %_ptr_Function_PS_INPUT -%48 = OpLabel -%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function -OpLine %5 24 0 -%49 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%50 = OpLoad %uint %49 -%51 = OpAccessChain %_ptr_UniformConstant_27 %g_tColor %50 -%52 = OpLoad %27 %51 -%53 = OpLoad %35 %g_sAniso -%54 = OpSampledImage %37 %52 %53 -%55 = OpAccessChain %_ptr_Function_v2float %i %int_0 -%56 = OpLoad %v2float %55 -%62 = OpULessThan %bool %50 %uint_128 -OpSelectionMerge %63 None -OpBranchConditional %62 %64 %65 -%64 = OpLabel -%66 = OpLoad %27 %51 -%67 = OpSampledImage %37 %66 %53 -%68 = OpImageSampleImplicitLod %v4float %67 %56 -OpBranch %63 -%65 = OpLabel -%124 = OpFunctionCall %void %69 %uint_93 %uint_0 %50 %uint_128 -OpBranch %63 -%63 = OpLabel -%126 = OpPhi %v4float %68 %64 %125 %65 -%58 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 -OpStore %58 %126 -OpLine %5 25 0 -%59 = OpLoad %PS_OUTPUT %ps_output -OpReturnValue %59 -OpFunctionEnd -)"; - - const std::string output_func = - R"(%69 = OpFunction %void None %70 -%71 = OpFunctionParameter %uint -%72 = OpFunctionParameter %uint -%73 = OpFunctionParameter %uint -%74 = OpFunctionParameter %uint -%75 = OpLabel -%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0 -%84 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_9 -%85 = OpIAdd %uint %84 %uint_9 -%86 = OpArrayLength %uint %79 1 -%87 = OpULessThanEqual %bool %85 %86 -OpSelectionMerge %88 None -OpBranchConditional %87 %89 %88 -%89 = OpLabel -%90 = OpIAdd %uint %84 %uint_0 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %90 -OpStore %92 %uint_9 -%94 = OpIAdd %uint %84 %uint_1 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94 -OpStore %95 %uint_23 -%97 = OpIAdd %uint %84 %uint_2 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %97 -OpStore %98 %71 -%100 = OpIAdd %uint %84 %uint_3 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %100 -OpStore %101 %uint_4 -%104 = OpLoad %v4float %gl_FragCoord -%106 = OpBitcast %v4uint %104 -%107 = OpCompositeExtract %uint %106 0 -%108 = OpIAdd %uint %84 %uint_4 -%109 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %108 -OpStore %109 %107 -%110 = OpCompositeExtract %uint %106 1 -%112 = OpIAdd %uint %84 %uint_5 -%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112 -OpStore %113 %110 -%115 = OpIAdd %uint %84 %uint_6 -%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115 -OpStore %116 %72 -%118 = OpIAdd %uint %84 %uint_7 -%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118 -OpStore %119 %73 -%121 = OpIAdd %uint %84 %uint_8 -%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121 -OpStore %122 %74 -OpBranch %88 -%88 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func1_before + func2_before, - defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u, - false, false, 1u); -} - -TEST_F(InstBindlessTest, RuntimeArray) { - // This test verifies that the pass will correctly instrument shader - // with runtime descriptor array. This test was created by editing the - // SPIR-V from the Simple test. - - const std::string defs_before = - R"(OpCapability Shader -OpCapability RuntimeDescriptorArray -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 1 -OpDecorate %g_tColor Binding 2 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 1 -OpDecorate %g_sAniso Binding 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%20 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_rarr_20 = OpTypeRuntimeArray %20 -%_ptr_UniformConstant__arr_20 = OpTypePointer UniformConstant %_rarr_20 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_20 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 -%35 = OpTypeSampler -%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 -%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant -%39 = OpTypeSampledImage %20 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability RuntimeDescriptorArray -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 1 -OpDecorate %g_tColor Binding 2 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 1 -OpDecorate %g_sAniso Binding 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_46 Block -OpMemberDecorate %_struct_46 0 Offset 0 -OpDecorate %48 DescriptorSet 7 -OpDecorate %48 Binding 1 -OpDecorate %_struct_71 Block -OpMemberDecorate %_struct_71 0 Offset 0 -OpMemberDecorate %_struct_71 1 Offset 4 -OpDecorate %73 DescriptorSet 7 -OpDecorate %73 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%16 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%_runtimearr_16 = OpTypeRuntimeArray %16 -%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16 -%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 -%24 = OpTypeSampler -%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 -%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant -%26 = OpTypeSampledImage %16 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%uint_2 = OpConstant %uint 2 -%41 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_46 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46 -%48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%65 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_71 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_71 = OpTypePointer StorageBuffer %_struct_71 -%73 = OpVariable %_ptr_StorageBuffer__struct_71 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_59 = OpConstant %uint 59 -%116 = OpConstantNull %v4float -%119 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 -%5 = OpLabel -%53 = OpLoad %v2float %i_vTextureCoords -%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%64 = OpLoad %uint %63 -%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 -%66 = OpLoad %20 %65 -%67 = OpLoad %35 %g_sAniso -%68 = OpSampledImage %39 %66 %67 -%71 = OpImageSampleImplicitLod %v4float %68 %53 -OpStore %_entryPointOutput_vColor %71 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %10 -%29 = OpLabel -%30 = OpLoad %v2float %i_vTextureCoords -%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%32 = OpLoad %uint %31 -%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 -%34 = OpLoad %16 %33 -%35 = OpLoad %24 %g_sAniso -%36 = OpSampledImage %26 %34 %35 -%55 = OpFunctionCall %uint %40 %uint_2 %uint_2 -%57 = OpULessThan %bool %32 %55 -OpSelectionMerge %58 None -OpBranchConditional %57 %59 %60 -%59 = OpLabel -%61 = OpLoad %16 %33 -%62 = OpSampledImage %26 %61 %35 -%136 = OpFunctionCall %uint %118 %uint_0 %uint_1 %uint_2 %32 -%137 = OpINotEqual %bool %136 %uint_0 -OpSelectionMerge %138 None -OpBranchConditional %137 %139 %140 -%139 = OpLabel -%141 = OpLoad %16 %33 -%142 = OpSampledImage %26 %141 %35 -%143 = OpImageSampleImplicitLod %v4float %142 %30 -OpBranch %138 -%140 = OpLabel -%144 = OpFunctionCall %void %64 %uint_59 %uint_1 %32 %uint_0 -OpBranch %138 -%138 = OpLabel -%145 = OpPhi %v4float %143 %139 %116 %140 -OpBranch %58 -%60 = OpLabel -%115 = OpFunctionCall %void %64 %uint_59 %uint_0 %32 %55 -OpBranch %58 -%58 = OpLabel -%117 = OpPhi %v4float %145 %138 %116 %60 -OpStore %_entryPointOutput_vColor %117 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%40 = OpFunction %uint None %41 -%42 = OpFunctionParameter %uint -%43 = OpFunctionParameter %uint -%44 = OpLabel -%50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %42 -%51 = OpLoad %uint %50 -%52 = OpIAdd %uint %51 %43 -%53 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %52 -%54 = OpLoad %uint %53 -OpReturnValue %54 -OpFunctionEnd -%64 = OpFunction %void None %65 -%66 = OpFunctionParameter %uint -%67 = OpFunctionParameter %uint -%68 = OpFunctionParameter %uint -%69 = OpFunctionParameter %uint -%70 = OpLabel -%74 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_0 -%77 = OpAtomicIAdd %uint %74 %uint_4 %uint_0 %uint_9 -%78 = OpIAdd %uint %77 %uint_9 -%79 = OpArrayLength %uint %73 1 -%80 = OpULessThanEqual %bool %78 %79 -OpSelectionMerge %81 None -OpBranchConditional %80 %82 %81 -%82 = OpLabel -%83 = OpIAdd %uint %77 %uint_0 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %83 -OpStore %84 %uint_9 -%86 = OpIAdd %uint %77 %uint_1 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %86 -OpStore %87 %uint_23 -%88 = OpIAdd %uint %77 %uint_2 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %88 -OpStore %89 %66 -%91 = OpIAdd %uint %77 %uint_3 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %91 -OpStore %92 %uint_4 -%95 = OpLoad %v4float %gl_FragCoord -%97 = OpBitcast %v4uint %95 -%98 = OpCompositeExtract %uint %97 0 -%99 = OpIAdd %uint %77 %uint_4 -%100 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %99 -OpStore %100 %98 -%101 = OpCompositeExtract %uint %97 1 -%103 = OpIAdd %uint %77 %uint_5 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %103 -OpStore %104 %101 -%106 = OpIAdd %uint %77 %uint_6 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %106 -OpStore %107 %67 -%109 = OpIAdd %uint %77 %uint_7 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %109 -OpStore %110 %68 -%112 = OpIAdd %uint %77 %uint_8 -%113 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %112 -OpStore %113 %69 -OpBranch %81 -%81 = OpLabel -OpReturn -OpFunctionEnd -%118 = OpFunction %uint None %119 -%120 = OpFunctionParameter %uint -%121 = OpFunctionParameter %uint -%122 = OpFunctionParameter %uint -%123 = OpFunctionParameter %uint -%124 = OpLabel -%125 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %120 -%126 = OpLoad %uint %125 -%127 = OpIAdd %uint %126 %121 -%128 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %127 -%129 = OpLoad %uint %128 -%130 = OpIAdd %uint %129 %122 -%131 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %130 -%132 = OpLoad %uint %131 -%133 = OpIAdd %uint %132 %123 -%134 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %133 -%135 = OpLoad %uint %134 -OpReturnValue %135 -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); + 23u, false, false); } TEST_F(InstBindlessTest, NoInstrumentNonBindless) { @@ -2278,1960 +183,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>(whole_file, whole_file, true, - true, 7u, 23u, false, false, 1u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) { - // This test verifies that the pass will correctly instrument vanilla - // texture sample on a scalar descriptor with an initialization check if the - // input_init_enable argument is set to true. This can happen when the - // descriptor indexing extension is enabled in the API but the SPIR-V - // does not have the extension enabled because it does not contain a - // runtime array. This is the same shader as NoInstrumentNonBindless. - - const std::string defs_before = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 0 -OpDecorate %g_tColor Binding 0 -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %g_sAniso Binding 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%8 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%12 = OpTypeImage %float 2D 0 0 0 1 Unknown -%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 -%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant -%14 = OpTypeSampler -%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 -%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant -%16 = OpTypeSampledImage %12 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 0 -OpDecorate %g_tColor Binding 0 -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %g_sAniso Binding 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_35 Block -OpMemberDecorate %_struct_35 0 Offset 0 -OpDecorate %37 DescriptorSet 7 -OpDecorate %37 Binding 1 -OpDecorate %_struct_67 Block -OpMemberDecorate %_struct_67 0 Offset 0 -OpMemberDecorate %_struct_67 1 Offset 4 -OpDecorate %69 DescriptorSet 7 -OpDecorate %69 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%8 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%12 = OpTypeImage %float 2D 0 0 0 1 Unknown -%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 -%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant -%14 = OpTypeSampler -%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 -%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant -%16 = OpTypeSampledImage %12 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%28 = OpTypeFunction %uint %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_35 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_35 = OpTypePointer StorageBuffer %_struct_35 -%37 = OpVariable %_ptr_StorageBuffer__struct_35 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%uint_1 = OpConstant %uint 1 -%61 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_67 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67 -%69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_39 = OpConstant %uint 39 -%113 = OpConstantNull %v4float -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %8 -%19 = OpLabel -%20 = OpLoad %v2float %i_vTextureCoords -%21 = OpLoad %12 %g_tColor -%22 = OpLoad %14 %g_sAniso -%23 = OpSampledImage %16 %21 %22 -%24 = OpImageSampleImplicitLod %v4float %23 %20 -OpStore %_entryPointOutput_vColor %24 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %8 -%19 = OpLabel -%20 = OpLoad %v2float %i_vTextureCoords -%21 = OpLoad %12 %g_tColor -%22 = OpLoad %14 %g_sAniso -%23 = OpSampledImage %16 %21 %22 -%50 = OpFunctionCall %uint %27 %uint_0 %uint_0 %uint_0 %uint_0 -%52 = OpINotEqual %bool %50 %uint_0 -OpSelectionMerge %54 None -OpBranchConditional %52 %55 %56 -%55 = OpLabel -%57 = OpLoad %12 %g_tColor -%58 = OpSampledImage %16 %57 %22 -%59 = OpImageSampleImplicitLod %v4float %58 %20 -OpBranch %54 -%56 = OpLabel -%112 = OpFunctionCall %void %60 %uint_39 %uint_1 %uint_0 %uint_0 -OpBranch %54 -%54 = OpLabel -%114 = OpPhi %v4float %59 %55 %113 %56 -OpStore %_entryPointOutput_vColor %114 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%27 = OpFunction %uint None %28 -%29 = OpFunctionParameter %uint -%30 = OpFunctionParameter %uint -%31 = OpFunctionParameter %uint -%32 = OpFunctionParameter %uint -%33 = OpLabel -%39 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %29 -%40 = OpLoad %uint %39 -%41 = OpIAdd %uint %40 %30 -%42 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %41 -%43 = OpLoad %uint %42 -%44 = OpIAdd %uint %43 %31 -%45 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %44 -%46 = OpLoad %uint %45 -%47 = OpIAdd %uint %46 %32 -%48 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %47 -%49 = OpLoad %uint %48 -OpReturnValue %49 -OpFunctionEnd -%60 = OpFunction %void None %61 -%62 = OpFunctionParameter %uint -%63 = OpFunctionParameter %uint -%64 = OpFunctionParameter %uint -%65 = OpFunctionParameter %uint -%66 = OpLabel -%70 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 -%73 = OpAtomicIAdd %uint %70 %uint_4 %uint_0 %uint_9 -%74 = OpIAdd %uint %73 %uint_9 -%75 = OpArrayLength %uint %69 1 -%76 = OpULessThanEqual %bool %74 %75 -OpSelectionMerge %77 None -OpBranchConditional %76 %78 %77 -%78 = OpLabel -%79 = OpIAdd %uint %73 %uint_0 -%80 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %79 -OpStore %80 %uint_9 -%82 = OpIAdd %uint %73 %uint_1 -%83 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %82 -OpStore %83 %uint_23 -%85 = OpIAdd %uint %73 %uint_2 -%86 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %85 -OpStore %86 %62 -%88 = OpIAdd %uint %73 %uint_3 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %88 -OpStore %89 %uint_4 -%92 = OpLoad %v4float %gl_FragCoord -%94 = OpBitcast %v4uint %92 -%95 = OpCompositeExtract %uint %94 0 -%96 = OpIAdd %uint %73 %uint_4 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %96 -OpStore %97 %95 -%98 = OpCompositeExtract %uint %94 1 -%100 = OpIAdd %uint %73 %uint_5 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %100 -OpStore %101 %98 -%103 = OpIAdd %uint %73 %uint_6 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %103 -OpStore %104 %63 -%106 = OpIAdd %uint %73 %uint_7 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %106 -OpStore %107 %64 -%109 = OpIAdd %uint %73 %uint_8 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %109 -OpStore %110 %65 -OpBranch %77 -%77 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, SPV14AddToEntryPoint) { - const std::string text = R"( -; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] -; CHECK: OpDecorate [[v1]] DescriptorSet 7 -; CHECK: OpDecorate [[v2]] DescriptorSet 7 -; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer -; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer -OpCapability Shader -OpExtension "SPV_EXT_descriptor_indexing" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var -OpExecutionMode %foo OriginUpperLeft -OpDecorate %image_var DescriptorSet 0 -OpDecorate %image_var Binding 0 -OpDecorate %sampler_var DescriptorSet 0 -OpDecorate %sampler_var Binding 1 -OpDecorate %gid DescriptorSet 0 -OpDecorate %gid Binding 2 -OpDecorate %struct Block -OpMemberDecorate %struct 0 Offset 0 -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%int_0 = OpConstant %int 0 -%v3int = OpTypeVector %int 3 -%float = OpTypeFloat 32 -%v3float = OpTypeVector %float 3 -%v4float = OpTypeVector %float 4 -%struct = OpTypeStruct %v3int -%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct -%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int -%gid = OpVariable %ptr_ssbo_struct StorageBuffer -%image = OpTypeImage %float 3D 0 0 0 1 Unknown -%ptr_uc_image = OpTypePointer UniformConstant %image -%sampler = OpTypeSampler -%ptr_uc_sampler = OpTypePointer UniformConstant %sampler -%image_var = OpVariable %ptr_uc_image UniformConstant -%sampler_var = OpVariable %ptr_uc_sampler UniformConstant -%sampled = OpTypeSampledImage %image -%void_fn = OpTypeFunction %void -%foo = OpFunction %void None %void_fn -%entry = OpLabel -%ld_image = OpLoad %image %image_var -%ld_sampler = OpLoad %sampler %sampler_var -%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0 -%ld_gid = OpLoad %v3int %gep -%convert = OpConvertUToF %v3float %ld_gid -%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler -%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert -OpReturn -OpFunctionEnd -)"; - - SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true); -} - -TEST_F(InstBindlessTest, SPV14AddToEntryPoints) { - const std::string text = R"( -; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] -; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] -; CHECK: OpDecorate [[v1]] DescriptorSet 7 -; CHECK: OpDecorate [[v2]] DescriptorSet 7 -; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer -; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer -OpCapability Shader -OpExtension "SPV_EXT_descriptor_indexing" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var -OpEntryPoint Fragment %foo "bar" %gid %image_var %sampler_var -OpExecutionMode %foo OriginUpperLeft -OpDecorate %image_var DescriptorSet 0 -OpDecorate %image_var Binding 0 -OpDecorate %sampler_var DescriptorSet 0 -OpDecorate %sampler_var Binding 1 -OpDecorate %gid DescriptorSet 0 -OpDecorate %gid Binding 2 -OpDecorate %struct Block -OpMemberDecorate %struct 0 Offset 0 -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%int_0 = OpConstant %int 0 -%v3int = OpTypeVector %int 3 -%float = OpTypeFloat 32 -%v3float = OpTypeVector %float 3 -%v4float = OpTypeVector %float 4 -%struct = OpTypeStruct %v3int -%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct -%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int -%gid = OpVariable %ptr_ssbo_struct StorageBuffer -%image = OpTypeImage %float 3D 0 0 0 1 Unknown -%ptr_uc_image = OpTypePointer UniformConstant %image -%sampler = OpTypeSampler -%ptr_uc_sampler = OpTypePointer UniformConstant %sampler -%image_var = OpVariable %ptr_uc_image UniformConstant -%sampler_var = OpVariable %ptr_uc_sampler UniformConstant -%sampled = OpTypeSampledImage %image -%void_fn = OpTypeFunction %void -%foo = OpFunction %void None %void_fn -%entry = OpLabel -%ld_image = OpLoad %image %image_var -%ld_sampler = OpLoad %sampler %sampler_var -%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0 -%ld_gid = OpLoad %v3int %gep -%convert = OpConvertUToF %v3float %ld_gid -%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler -%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert -OpReturn -OpFunctionEnd -)"; - - SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true); -} - -TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) { - // #version 450 - // #extension GL_EXT_nonuniform_qualifier : enable - // - // layout(location=0) in nonuniformEXT flat int nu_ii; - // layout(location=0) out float b; - // - // layout(binding=3) uniform uname { float a; } uniformBuffer[]; - // - // void main() - // { - // b = uniformBuffer[nu_ii].a; - // } - - const std::string defs_before = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %16 NonUniform -OpDecorate %20 NonUniform -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%_runtimearr_uname = OpTypeRuntimeArray %uname -%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname -%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %102 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %130 NonUniform -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %127 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%_runtimearr_uname = OpTypeRuntimeArray %uname -%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname -%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_3 = OpConstant %uint 3 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%49 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_45 = OpConstant %uint 45 -%101 = OpConstantNull %float -%105 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%16 = OpLoad %int %nu_ii -%19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0 -%20 = OpLoad %float %19 -OpStore %b %20 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %10 -%19 = OpLabel -%7 = OpLoad %int %nu_ii -%20 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%103 = OpBitcast %uint %7 -%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 -%123 = OpINotEqual %bool %122 %uint_0 -OpSelectionMerge %124 None -OpBranchConditional %123 %125 %126 -%125 = OpLabel -%127 = OpLoad %float %20 -OpBranch %124 -%126 = OpLabel -%128 = OpBitcast %uint %7 -%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 -OpBranch %124 -%124 = OpLabel -%130 = OpPhi %float %127 %125 %101 %126 -OpBranch %43 -%45 = OpLabel -%47 = OpBitcast %uint %7 -%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 -OpBranch %43 -%43 = OpLabel -%102 = OpPhi %float %130 %124 %101 %45 -OpStore %b %102 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%48 = OpFunction %void None %49 -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpFunctionParameter %uint -%54 = OpLabel -%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_9 -%62 = OpIAdd %uint %61 %uint_9 -%63 = OpArrayLength %uint %57 1 -%64 = OpULessThanEqual %bool %62 %63 -OpSelectionMerge %65 None -OpBranchConditional %64 %66 %65 -%66 = OpLabel -%67 = OpIAdd %uint %61 %uint_0 -%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 -OpStore %68 %uint_9 -%70 = OpIAdd %uint %61 %uint_1 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 -OpStore %71 %uint_23 -%73 = OpIAdd %uint %61 %uint_2 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 -OpStore %74 %50 -%75 = OpIAdd %uint %61 %uint_3 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %uint_4 -%80 = OpLoad %v4float %gl_FragCoord -%82 = OpBitcast %v4uint %80 -%83 = OpCompositeExtract %uint %82 0 -%84 = OpIAdd %uint %61 %uint_4 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 -OpStore %85 %83 -%86 = OpCompositeExtract %uint %82 1 -%88 = OpIAdd %uint %61 %uint_5 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 -OpStore %89 %86 -%91 = OpIAdd %uint %61 %uint_6 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 -OpStore %92 %51 -%94 = OpIAdd %uint %61 %uint_7 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 -OpStore %95 %52 -%97 = OpIAdd %uint %61 %uint_8 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 -OpStore %98 %53 -OpBranch %65 -%65 = OpLabel -OpReturn -OpFunctionEnd -%104 = OpFunction %uint None %105 -%106 = OpFunctionParameter %uint -%107 = OpFunctionParameter %uint -%108 = OpFunctionParameter %uint -%109 = OpFunctionParameter %uint -%110 = OpLabel -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %107 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %108 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -%119 = OpIAdd %uint %118 %109 -%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 -%121 = OpLoad %uint %120 -OpReturnValue %121 -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) { - // #version 450 - // #extension GL_EXT_nonuniform_qualifier : enable - // - // layout(location=0) in nonuniformEXT flat int nu_ii; - // layout(location=0) out float b; - // - // layout(binding=3) buffer bname { float b; } storageBuffer[]; - // - // void main() - // { - // b = storageBuffer[nu_ii].b; - // } - - const std::string defs_before = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %bname "bname" -OpMemberName %bname 0 "a" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname BufferBlock -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %16 NonUniform -OpDecorate %20 NonUniform -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %bname "bname" -OpMemberName %bname 0 "a" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname BufferBlock -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %102 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %130 NonUniform -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %127 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_3 = OpConstant %uint 3 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%49 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_45 = OpConstant %uint 45 -%101 = OpConstantNull %float -%105 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%16 = OpLoad %int %nu_ii -%19 = OpAccessChain %_ptr_Uniform_float %storageBuffer %16 %int_0 -%20 = OpLoad %float %19 -OpStore %b %20 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %10 -%19 = OpLabel -%7 = OpLoad %int %nu_ii -%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%103 = OpBitcast %uint %7 -%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 -%123 = OpINotEqual %bool %122 %uint_0 -OpSelectionMerge %124 None -OpBranchConditional %123 %125 %126 -%125 = OpLabel -%127 = OpLoad %float %20 -OpBranch %124 -%126 = OpLabel -%128 = OpBitcast %uint %7 -%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 -OpBranch %124 -%124 = OpLabel -%130 = OpPhi %float %127 %125 %101 %126 -OpBranch %43 -%45 = OpLabel -%47 = OpBitcast %uint %7 -%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 -OpBranch %43 -%43 = OpLabel -%102 = OpPhi %float %130 %124 %101 %45 -OpStore %b %102 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%48 = OpFunction %void None %49 -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpFunctionParameter %uint -%54 = OpLabel -%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_9 -%62 = OpIAdd %uint %61 %uint_9 -%63 = OpArrayLength %uint %57 1 -%64 = OpULessThanEqual %bool %62 %63 -OpSelectionMerge %65 None -OpBranchConditional %64 %66 %65 -%66 = OpLabel -%67 = OpIAdd %uint %61 %uint_0 -%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 -OpStore %68 %uint_9 -%70 = OpIAdd %uint %61 %uint_1 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 -OpStore %71 %uint_23 -%73 = OpIAdd %uint %61 %uint_2 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 -OpStore %74 %50 -%75 = OpIAdd %uint %61 %uint_3 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %uint_4 -%80 = OpLoad %v4float %gl_FragCoord -%82 = OpBitcast %v4uint %80 -%83 = OpCompositeExtract %uint %82 0 -%84 = OpIAdd %uint %61 %uint_4 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 -OpStore %85 %83 -%86 = OpCompositeExtract %uint %82 1 -%88 = OpIAdd %uint %61 %uint_5 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 -OpStore %89 %86 -%91 = OpIAdd %uint %61 %uint_6 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 -OpStore %92 %51 -%94 = OpIAdd %uint %61 %uint_7 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 -OpStore %95 %52 -%97 = OpIAdd %uint %61 %uint_8 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 -OpStore %98 %53 -OpBranch %65 -%65 = OpLabel -OpReturn -OpFunctionEnd -%104 = OpFunction %uint None %105 -%106 = OpFunctionParameter %uint -%107 = OpFunctionParameter %uint -%108 = OpFunctionParameter %uint -%109 = OpFunctionParameter %uint -%110 = OpLabel -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %107 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %108 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -%119 = OpIAdd %uint %118 %109 -%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 -%121 = OpLoad %uint %120 -OpReturnValue %121 -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) { - // Same as Deprecated but declaring as StorageBuffer Block - - const std::string defs_before = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %bname "bname" -OpMemberName %bname 0 "a" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname Block -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %16 NonUniform -OpDecorate %20 NonUniform -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %bname "bname" -OpMemberName %bname 0 "a" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname Block -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %102 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %130 NonUniform -OpDecorate %_struct_55 Block -OpMemberDecorate %_struct_55 0 Offset 0 -OpMemberDecorate %_struct_55 1 Offset 4 -OpDecorate %57 DescriptorSet 7 -OpDecorate %57 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %127 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_3 = OpConstant %uint 3 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%49 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_55 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 -%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_45 = OpConstant %uint 45 -%101 = OpConstantNull %float -%105 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%16 = OpLoad %int %nu_ii -%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 -%20 = OpLoad %float %19 -OpStore %b %20 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %10 -%19 = OpLabel -%7 = OpLoad %int %nu_ii -%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%103 = OpBitcast %uint %7 -%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 -%123 = OpINotEqual %bool %122 %uint_0 -OpSelectionMerge %124 None -OpBranchConditional %123 %125 %126 -%125 = OpLabel -%127 = OpLoad %float %20 -OpBranch %124 -%126 = OpLabel -%128 = OpBitcast %uint %7 -%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 -OpBranch %124 -%124 = OpLabel -%130 = OpPhi %float %127 %125 %101 %126 -OpBranch %43 -%45 = OpLabel -%47 = OpBitcast %uint %7 -%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 -OpBranch %43 -%43 = OpLabel -%102 = OpPhi %float %130 %124 %101 %45 -OpStore %b %102 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%48 = OpFunction %void None %49 -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpFunctionParameter %uint -%54 = OpLabel -%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 -%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_9 -%62 = OpIAdd %uint %61 %uint_9 -%63 = OpArrayLength %uint %57 1 -%64 = OpULessThanEqual %bool %62 %63 -OpSelectionMerge %65 None -OpBranchConditional %64 %66 %65 -%66 = OpLabel -%67 = OpIAdd %uint %61 %uint_0 -%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 -OpStore %68 %uint_9 -%70 = OpIAdd %uint %61 %uint_1 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 -OpStore %71 %uint_23 -%73 = OpIAdd %uint %61 %uint_2 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 -OpStore %74 %50 -%75 = OpIAdd %uint %61 %uint_3 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 -OpStore %76 %uint_4 -%80 = OpLoad %v4float %gl_FragCoord -%82 = OpBitcast %v4uint %80 -%83 = OpCompositeExtract %uint %82 0 -%84 = OpIAdd %uint %61 %uint_4 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 -OpStore %85 %83 -%86 = OpCompositeExtract %uint %82 1 -%88 = OpIAdd %uint %61 %uint_5 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 -OpStore %89 %86 -%91 = OpIAdd %uint %61 %uint_6 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 -OpStore %92 %51 -%94 = OpIAdd %uint %61 %uint_7 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 -OpStore %95 %52 -%97 = OpIAdd %uint %61 %uint_8 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 -OpStore %98 %53 -OpBranch %65 -%65 = OpLabel -OpReturn -OpFunctionEnd -%104 = OpFunction %uint None %105 -%106 = OpFunctionParameter %uint -%107 = OpFunctionParameter %uint -%108 = OpFunctionParameter %uint -%109 = OpFunctionParameter %uint -%110 = OpLabel -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %107 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %108 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -%119 = OpIAdd %uint %118 %109 -%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 -%121 = OpLoad %uint %120 -OpReturnValue %121 -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, InstInitLoadUBOScalar) { - // #version 450 - // #extension GL_EXT_nonuniform_qualifier : enable - // - // layout(location=0) out float b; - // layout(binding=3) uniform uname { float a; } uniformBuffer; - // - // void main() - // { - // b = uniformBuffer.a; - // } - - const std::string defs_before = - R"(OpCapability Shader -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%_ptr_Uniform_uname = OpTypePointer Uniform %uname -%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_28 Block -OpMemberDecorate %_struct_28 0 Offset 0 -OpDecorate %30 DescriptorSet 7 -OpDecorate %30 Binding 1 -OpDecorate %_struct_58 Block -OpMemberDecorate %_struct_58 0 Offset 0 -OpMemberDecorate %_struct_58 1 Offset 4 -OpDecorate %60 DescriptorSet 7 -OpDecorate %60 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%_ptr_Uniform_uname = OpTypePointer Uniform %uname -%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_3 = OpConstant %uint 3 -%21 = OpTypeFunction %uint %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_28 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_28 = OpTypePointer StorageBuffer %_struct_28 -%30 = OpVariable %_ptr_StorageBuffer__struct_28 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%uint_1 = OpConstant %uint 1 -%52 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_58 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58 -%60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_32 = OpConstant %uint 32 -%104 = OpConstantNull %float -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 -%16 = OpLoad %float %15 -OpStore %b %16 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %7 -%14 = OpLabel -%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 -%43 = OpFunctionCall %uint %20 %uint_0 %uint_0 %uint_3 %uint_0 -%45 = OpINotEqual %bool %43 %uint_0 -OpSelectionMerge %47 None -OpBranchConditional %45 %48 %49 -%48 = OpLabel -%50 = OpLoad %float %15 -OpBranch %47 -%49 = OpLabel -%103 = OpFunctionCall %void %51 %uint_32 %uint_1 %uint_0 %uint_0 -OpBranch %47 -%47 = OpLabel -%105 = OpPhi %float %50 %48 %104 %49 -OpStore %b %105 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%20 = OpFunction %uint None %21 -%22 = OpFunctionParameter %uint -%23 = OpFunctionParameter %uint -%24 = OpFunctionParameter %uint -%25 = OpFunctionParameter %uint -%26 = OpLabel -%32 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %22 -%33 = OpLoad %uint %32 -%34 = OpIAdd %uint %33 %23 -%35 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %34 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %24 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %37 -%39 = OpLoad %uint %38 -%40 = OpIAdd %uint %39 %25 -%41 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %40 -%42 = OpLoad %uint %41 -OpReturnValue %42 -OpFunctionEnd -%51 = OpFunction %void None %52 -%53 = OpFunctionParameter %uint -%54 = OpFunctionParameter %uint -%55 = OpFunctionParameter %uint -%56 = OpFunctionParameter %uint -%57 = OpLabel -%61 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0 -%64 = OpAtomicIAdd %uint %61 %uint_4 %uint_0 %uint_9 -%65 = OpIAdd %uint %64 %uint_9 -%66 = OpArrayLength %uint %60 1 -%67 = OpULessThanEqual %bool %65 %66 -OpSelectionMerge %68 None -OpBranchConditional %67 %69 %68 -%69 = OpLabel -%70 = OpIAdd %uint %64 %uint_0 -%71 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %70 -OpStore %71 %uint_9 -%73 = OpIAdd %uint %64 %uint_1 -%74 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %73 -OpStore %74 %uint_23 -%76 = OpIAdd %uint %64 %uint_2 -%77 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %76 -OpStore %77 %53 -%78 = OpIAdd %uint %64 %uint_3 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78 -OpStore %79 %uint_4 -%83 = OpLoad %v4float %gl_FragCoord -%85 = OpBitcast %v4uint %83 -%86 = OpCompositeExtract %uint %85 0 -%87 = OpIAdd %uint %64 %uint_4 -%88 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %87 -OpStore %88 %86 -%89 = OpCompositeExtract %uint %85 1 -%91 = OpIAdd %uint %64 %uint_5 -%92 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %91 -OpStore %92 %89 -%94 = OpIAdd %uint %64 %uint_6 -%95 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %94 -OpStore %95 %54 -%97 = OpIAdd %uint %64 %uint_7 -%98 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %97 -OpStore %98 %55 -%100 = OpIAdd %uint %64 %uint_8 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %100 -OpStore %101 %56 -OpBranch %68 -%68 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) { - // #version 450 - // #extension GL_EXT_nonuniform_qualifier : enable - // - // layout(location=0) in nonuniformEXT flat int nu_ii; - // layout(location=1) in float b; - // - // layout(binding=4) buffer bname { float b; } storageBuffer[]; - // - // void main() - // { - // storageBuffer[nu_ii].b = b; - // } - - const std::string defs_before = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %nu_ii %b -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %bname "bname" -OpMemberName %bname 0 "b" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpName %b "b" -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname BufferBlock -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 4 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %14 NonUniform -OpDecorate %b Location 1 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Input_float = OpTypePointer Input %float -%b = OpVariable %_ptr_Input_float Input -%_ptr_Uniform_float = OpTypePointer Uniform %float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability RuntimeDescriptorArray -OpCapability StorageBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %bname "bname" -OpMemberName %bname 0 "b" -OpName %storageBuffer "storageBuffer" -OpName %nu_ii "nu_ii" -OpName %b "b" -OpMemberDecorate %bname 0 Offset 0 -OpDecorate %bname BufferBlock -OpDecorate %storageBuffer DescriptorSet 0 -OpDecorate %storageBuffer Binding 4 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %b Location 1 -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_31 Block -OpMemberDecorate %_struct_31 0 Offset 0 -OpDecorate %33 DescriptorSet 7 -OpDecorate %33 Binding 1 -OpDecorate %_struct_54 Block -OpMemberDecorate %_struct_54 0 Offset 0 -OpMemberDecorate %_struct_54 1 Offset 4 -OpDecorate %56 DescriptorSet 7 -OpDecorate %56 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%bname = OpTypeStruct %float -%_runtimearr_bname = OpTypeRuntimeArray %bname -%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname -%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Input_float = OpTypePointer Input %float -%b = OpVariable %_ptr_Input_float Input -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint = OpTypeInt 32 0 -%uint_0 = OpConstant %uint 0 -%uint_1 = OpConstant %uint 1 -%uint_4 = OpConstant %uint 4 -%26 = OpTypeFunction %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_31 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 -%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%bool = OpTypeBool -%48 = OpTypeFunction %void %uint %uint %uint %uint -%_struct_54 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_54 = OpTypePointer StorageBuffer %_struct_54 -%56 = OpVariable %_ptr_StorageBuffer__struct_54 StorageBuffer -%uint_9 = OpConstant %uint 9 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_45 = OpConstant %uint 45 -%102 = OpTypeFunction %uint %uint %uint %uint %uint -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%14 = OpLoad %int %nu_ii -%18 = OpLoad %float %b -%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0 -OpStore %20 %18 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %9 -%18 = OpLabel -%7 = OpLoad %int %nu_ii -%19 = OpLoad %float %b -%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0 -%40 = OpFunctionCall %uint %25 %uint_1 %uint_4 -%42 = OpULessThan %bool %7 %40 -OpSelectionMerge %43 None -OpBranchConditional %42 %44 %45 -%44 = OpLabel -%100 = OpBitcast %uint %7 -%119 = OpFunctionCall %uint %101 %uint_0 %uint_0 %uint_4 %100 -%120 = OpINotEqual %bool %119 %uint_0 -OpSelectionMerge %121 None -OpBranchConditional %120 %122 %123 -%122 = OpLabel -OpStore %20 %19 -OpBranch %121 -%123 = OpLabel -%124 = OpBitcast %uint %7 -%125 = OpFunctionCall %void %47 %uint_45 %uint_1 %124 %uint_0 -OpBranch %121 -%121 = OpLabel -OpBranch %43 -%45 = OpLabel -%46 = OpBitcast %uint %7 -%99 = OpFunctionCall %void %47 %uint_45 %uint_0 %46 %40 -OpBranch %43 -%43 = OpLabel -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%25 = OpFunction %uint None %26 -%27 = OpFunctionParameter %uint -%28 = OpFunctionParameter %uint -%29 = OpLabel -%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 -%36 = OpLoad %uint %35 -%37 = OpIAdd %uint %36 %28 -%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 -%39 = OpLoad %uint %38 -OpReturnValue %39 -OpFunctionEnd -%47 = OpFunction %void None %48 -%49 = OpFunctionParameter %uint -%50 = OpFunctionParameter %uint -%51 = OpFunctionParameter %uint -%52 = OpFunctionParameter %uint -%53 = OpLabel -%57 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_0 -%59 = OpAtomicIAdd %uint %57 %uint_4 %uint_0 %uint_9 -%60 = OpIAdd %uint %59 %uint_9 -%61 = OpArrayLength %uint %56 1 -%62 = OpULessThanEqual %bool %60 %61 -OpSelectionMerge %63 None -OpBranchConditional %62 %64 %63 -%64 = OpLabel -%65 = OpIAdd %uint %59 %uint_0 -%66 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %65 -OpStore %66 %uint_9 -%68 = OpIAdd %uint %59 %uint_1 -%69 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %68 -OpStore %69 %uint_23 -%71 = OpIAdd %uint %59 %uint_2 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %71 -OpStore %72 %49 -%74 = OpIAdd %uint %59 %uint_3 -%75 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %74 -OpStore %75 %uint_4 -%79 = OpLoad %v4float %gl_FragCoord -%81 = OpBitcast %v4uint %79 -%82 = OpCompositeExtract %uint %81 0 -%83 = OpIAdd %uint %59 %uint_4 -%84 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %83 -OpStore %84 %82 -%85 = OpCompositeExtract %uint %81 1 -%87 = OpIAdd %uint %59 %uint_5 -%88 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %87 -OpStore %88 %85 -%90 = OpIAdd %uint %59 %uint_6 -%91 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %90 -OpStore %91 %50 -%93 = OpIAdd %uint %59 %uint_7 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %93 -OpStore %94 %51 -%96 = OpIAdd %uint %59 %uint_8 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %96 -OpStore %97 %52 -OpBranch %63 -%63 = OpLabel -OpReturn -OpFunctionEnd -%101 = OpFunction %uint None %102 -%103 = OpFunctionParameter %uint -%104 = OpFunctionParameter %uint -%105 = OpFunctionParameter %uint -%106 = OpFunctionParameter %uint -%107 = OpLabel -%108 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %103 -%109 = OpLoad %uint %108 -%110 = OpIAdd %uint %109 %104 -%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %110 -%112 = OpLoad %uint %111 -%113 = OpIAdd %uint %112 %105 -%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 -%115 = OpLoad %uint %114 -%116 = OpIAdd %uint %115 %106 -%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 -%118 = OpLoad %uint %117 -OpReturnValue %118 -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) { - // #version 450 - // #extension GL_EXT_nonuniform_qualifier : enable - // - // layout(location=0) in nonuniformEXT flat int nu_ii; - // layout(location=0) out float b; - // - // layout(binding=3) uniform uname { float a; } uniformBuffer[128]; - // - // void main() - // { - // b = uniformBuffer[nu_ii].a; - // } - - const std::string defs_before = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %18 NonUniform -OpDecorate %22 NonUniform -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_uname_uint_128 = OpTypeArray %uname %uint_128 -%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128 -%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpCapability ShaderNonUniform -OpCapability UniformBufferArrayNonUniformIndexing -OpExtension "SPV_EXT_descriptor_indexing" -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpSourceExtension "GL_EXT_nonuniform_qualifier" -OpName %main "main" -OpName %b "b" -OpName %uname "uname" -OpMemberName %uname 0 "a" -OpName %uniformBuffer "uniformBuffer" -OpName %nu_ii "nu_ii" -OpDecorate %b Location 0 -OpMemberDecorate %uname 0 Offset 0 -OpDecorate %uname Block -OpDecorate %uniformBuffer DescriptorSet 0 -OpDecorate %uniformBuffer Binding 3 -OpDecorate %nu_ii Flat -OpDecorate %nu_ii Location 0 -OpDecorate %nu_ii NonUniform -OpDecorate %7 NonUniform -OpDecorate %89 NonUniform -OpDecorate %120 NonUniform -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_39 Block -OpMemberDecorate %_struct_39 0 Offset 0 -OpMemberDecorate %_struct_39 1 Offset 4 -OpDecorate %41 DescriptorSet 7 -OpDecorate %41 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %_struct_98 Block -OpMemberDecorate %_struct_98 0 Offset 0 -OpDecorate %100 DescriptorSet 7 -OpDecorate %100 Binding 1 -OpDecorate %117 NonUniform -%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%_ptr_Output_float = OpTypePointer Output %float -%b = OpVariable %_ptr_Output_float Output -%uname = OpTypeStruct %float -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_uname_uint_128 = OpTypeArray %uname %uint_128 -%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128 -%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%nu_ii = OpVariable %_ptr_Input_int Input -%int_0 = OpConstant %int 0 -%_ptr_Uniform_float = OpTypePointer Uniform %float -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%32 = OpTypeFunction %void %uint %uint %uint %uint -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_39 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 -%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_9 = OpConstant %uint 9 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_3 = OpConstant %uint 3 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%uint_46 = OpConstant %uint 46 -%88 = OpConstantNull %float -%92 = OpTypeFunction %uint %uint %uint %uint %uint -%_struct_98 = OpTypeStruct %_runtimearr_uint -%_ptr_StorageBuffer__struct_98 = OpTypePointer StorageBuffer %_struct_98 -%100 = OpVariable %_ptr_StorageBuffer__struct_98 StorageBuffer -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %3 -%5 = OpLabel -%18 = OpLoad %int %nu_ii -%21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0 -%22 = OpLoad %float %21 -OpStore %b %22 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%main = OpFunction %void None %10 -%21 = OpLabel -%7 = OpLoad %int %nu_ii -%22 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0 -%25 = OpULessThan %bool %7 %uint_128 -OpSelectionMerge %26 None -OpBranchConditional %25 %27 %28 -%27 = OpLabel -%90 = OpBitcast %uint %7 -%112 = OpFunctionCall %uint %91 %uint_0 %uint_0 %uint_3 %90 -%113 = OpINotEqual %bool %112 %uint_0 -OpSelectionMerge %114 None -OpBranchConditional %113 %115 %116 -%115 = OpLabel -%117 = OpLoad %float %22 -OpBranch %114 -%116 = OpLabel -%118 = OpBitcast %uint %7 -%119 = OpFunctionCall %void %31 %uint_46 %uint_1 %118 %uint_0 -OpBranch %114 -%114 = OpLabel -%120 = OpPhi %float %117 %115 %88 %116 -OpBranch %26 -%28 = OpLabel -%30 = OpBitcast %uint %7 -%87 = OpFunctionCall %void %31 %uint_46 %uint_0 %30 %uint_128 -OpBranch %26 -%26 = OpLabel -%89 = OpPhi %float %120 %114 %88 %28 -OpStore %b %89 -OpReturn -OpFunctionEnd -)"; - - const std::string new_funcs = - R"(%31 = OpFunction %void None %32 -%33 = OpFunctionParameter %uint -%34 = OpFunctionParameter %uint -%35 = OpFunctionParameter %uint -%36 = OpFunctionParameter %uint -%37 = OpLabel -%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 -%46 = OpAtomicIAdd %uint %43 %uint_4 %uint_0 %uint_9 -%47 = OpIAdd %uint %46 %uint_9 -%48 = OpArrayLength %uint %41 1 -%49 = OpULessThanEqual %bool %47 %48 -OpSelectionMerge %50 None -OpBranchConditional %49 %51 %50 -%51 = OpLabel -%52 = OpIAdd %uint %46 %uint_0 -%54 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %52 -OpStore %54 %uint_9 -%56 = OpIAdd %uint %46 %uint_1 -%57 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %56 -OpStore %57 %uint_23 -%59 = OpIAdd %uint %46 %uint_2 -%60 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %59 -OpStore %60 %33 -%62 = OpIAdd %uint %46 %uint_3 -%63 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %62 -OpStore %63 %uint_4 -%67 = OpLoad %v4float %gl_FragCoord -%69 = OpBitcast %v4uint %67 -%70 = OpCompositeExtract %uint %69 0 -%71 = OpIAdd %uint %46 %uint_4 -%72 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %71 -OpStore %72 %70 -%73 = OpCompositeExtract %uint %69 1 -%75 = OpIAdd %uint %46 %uint_5 -%76 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %75 -OpStore %76 %73 -%78 = OpIAdd %uint %46 %uint_6 -%79 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %78 -OpStore %79 %34 -%81 = OpIAdd %uint %46 %uint_7 -%82 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %81 -OpStore %82 %35 -%84 = OpIAdd %uint %46 %uint_8 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %84 -OpStore %85 %36 -OpBranch %50 -%50 = OpLabel -OpReturn -OpFunctionEnd -%91 = OpFunction %uint None %92 -%93 = OpFunctionParameter %uint -%94 = OpFunctionParameter %uint -%95 = OpFunctionParameter %uint -%96 = OpFunctionParameter %uint -%97 = OpLabel -%101 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %93 -%102 = OpLoad %uint %101 -%103 = OpIAdd %uint %102 %94 -%104 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %103 -%105 = OpLoad %uint %104 -%106 = OpIAdd %uint %105 %95 -%107 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %106 -%108 = OpLoad %uint %107 -%109 = OpIAdd %uint %108 %96 -%110 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %109 -%111 = OpLoad %uint %110 -OpReturnValue %111 -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - defs_before + func_before, defs_after + func_after + new_funcs, true, - true); -} - -TEST_F(InstBindlessTest, SimpleV2) { +TEST_F(InstBindlessTest, Simple) { // Texture2D g_tColor[128]; // // layout(push_constant) cbuffer PerViewConstantBuffer_t @@ -4452,16 +407,15 @@ )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool, - uint32_t>( + SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>( entry_before + names_annots + consts_types_vars + func_pt1 + func_pt2_before, entry_after + names_annots + new_annots + consts_types_vars + new_consts_types_vars + func_pt1 + func_pt2_after + output_func, - true, true, 7u, 23u, false, false, 2u); + true, true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentMultipleInstructionsV2) { +TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { // Texture2D g_tColor[128]; // // layout(push_constant) cbuffer PerViewConstantBuffer_t @@ -4753,10 +707,10 @@ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 2u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentOpImageV2) { +TEST_F(InstBindlessTest, InstrumentOpImage) { // This test verifies that the pass will correctly instrument shader // using OpImage. This test was created by editing the SPIR-V // from the Simple test. @@ -4981,10 +935,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 2u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentSampledImageV2) { +TEST_F(InstBindlessTest, InstrumentSampledImage) { // This test verifies that the pass will correctly instrument shader // using sampled image. This test was created by editing the SPIR-V // from the Simple test. @@ -5204,10 +1158,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 2u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentImageWriteV2) { +TEST_F(InstBindlessTest, InstrumentImageWrite) { // This test verifies that the pass will correctly instrument shader // doing bindless image write. This test was created by editing the SPIR-V // from the Simple test. @@ -5429,10 +1383,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 2u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentVertexSimpleV2) { +TEST_F(InstBindlessTest, InstrumentVertexSimple) { // This test verifies that the pass will correctly instrument shader // doing bindless image write. This test was created by editing the SPIR-V // from the Simple test. @@ -5703,10 +1657,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 2u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, InstrumentTeseSimpleV2) { +TEST_F(InstBindlessTest, InstrumentTeseSimple) { // This test verifies that the pass will correctly instrument tessellation // evaluation shader doing bindless buffer load. // @@ -5979,10 +1933,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + output_func, true, - true, 7u, 23u, false, false, 2u); + true, 7u, 23u, false, false); } -TEST_F(InstBindlessTest, MultipleDebugFunctionsV2) { +TEST_F(InstBindlessTest, MultipleDebugFunctions) { // Same source as Simple, but compiled -g and not optimized, especially not // inlined. The OpSource has had the source extracted for the sake of brevity. @@ -6303,10 +2257,10 @@ SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func1_before + func2_before, defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u, - false, false, 2u); + false, false); } -TEST_F(InstBindlessTest, RuntimeArrayV2) { +TEST_F(InstBindlessTest, RuntimeArray) { // This test verifies that the pass will correctly instrument shader // with runtime descriptor array. This test was created by editing the // SPIR-V from the Simple test. @@ -6599,10 +2553,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptorV2) { +TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) { // This test verifies that the pass will correctly instrument vanilla // texture sample on a scalar descriptor with an initialization check if the // input_init_enable argument is set to true. This can happen when the @@ -6835,10 +2789,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, SPV14AddToEntryPointV2) { +TEST_F(InstBindlessTest, SPV14AddToEntryPoint) { const std::string text = R"( ; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] ; CHECK: OpDecorate [[v1]] DescriptorSet 7 @@ -6891,11 +2845,10 @@ )"; SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true, - 2u); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, SPV14AddToEntryPointsV2) { +TEST_F(InstBindlessTest, SPV14AddToEntryPoints) { const std::string text = R"( ; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] ; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] @@ -6950,11 +2903,10 @@ )"; SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true, - 2u); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArrayV2) { +TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) { // #version 450 // #extension GL_EXT_nonuniform_qualifier : enable // @@ -7232,10 +3184,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecatedV2) { +TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) { // #version 450 // #extension GL_EXT_nonuniform_qualifier : enable // @@ -7513,10 +3465,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayV2) { +TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) { // Same as Deprecated but declaring as StorageBuffer Block const std::string defs_before = @@ -7783,10 +3735,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstInitLoadUBOScalarV2) { +TEST_F(InstBindlessTest, InstInitLoadUBOScalar) { // #version 450 // #extension GL_EXT_nonuniform_qualifier : enable // @@ -8008,10 +3960,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArrayV2) { +TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) { // #version 450 // #extension GL_EXT_nonuniform_qualifier : enable // @@ -8282,10 +4234,10 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } -TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArrayV2) { +TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) { // #version 450 // #extension GL_EXT_nonuniform_qualifier : enable // @@ -8551,7 +4503,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -8873,7 +4825,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -9197,7 +5149,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -9521,7 +5473,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -9845,7 +5797,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -10169,7 +6121,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -10493,7 +6445,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, @@ -10817,7 +6769,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) { @@ -11226,7 +7178,7 @@ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBindlessCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, true, true, 2u); + true, 7u, 23u, true, true); } // TODO(greg-lunarg): Add tests to verify handling of these cases:
diff --git a/third_party/SPIRV-Tools/test/opt/inst_buff_addr_check_test.cpp b/third_party/SPIRV-Tools/test/opt/inst_buff_addr_check_test.cpp index f8448a9..41ead67 100644 --- a/third_party/SPIRV-Tools/test/opt/inst_buff_addr_check_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/inst_buff_addr_check_test.cpp
@@ -310,7 +310,7 @@ // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBuffAddrCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, 2u); + true, 7u, 23u); } TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { @@ -612,7 +612,7 @@ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck<InstBuffAddrCheckPass>( defs_before + func_before, defs_after + func_after + new_funcs, true, - true, 7u, 23u, 2u); + true, 7u, 23u); } } // namespace
diff --git a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp index f10d118..7afbb4c 100644 --- a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
@@ -193,7 +193,24 @@ )"; const std::string before = - R"(%main = OpFunction %void None %9 + R"( +; CHECK: = OpFunction +; CHECK-NEXT: [[entry:%\w+]] = OpLabel +; CHECK: [[outer_header:%\w+]] = OpLabel +; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]] +; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]] +; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]] +; CHECK: [[inner_pre_header:%\w+]] = OpLabel +; CHECK: [[inner_header:%\w+]] = OpLabel +; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]] +; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]] +; CHECK: [[inner_be]] = OpLabel +; CHECK: [[f_next]] = OpFAdd %float [[inner_f]] +; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1 +; CHECK: [[outer_be]] = OpLabel +; CHECK: [[i_next]] = OpIAdd +; CHECK: OpStore %fo [[outer_f]] +%main = OpFunction %void None %9 %24 = OpLabel %f = OpVariable %_ptr_Function_float Function %i = OpVariable %_ptr_Function_int Function @@ -212,8 +229,8 @@ %31 = OpLabel %32 = OpLoad %int %j %33 = OpSLessThan %bool %32 %int_4 -OpLoopMerge %29 %34 None -OpBranchConditional %33 %34 %29 +OpLoopMerge %50 %34 None +OpBranchConditional %33 %34 %50 %34 = OpLabel %35 = OpLoad %float %f %36 = OpLoad %int %i @@ -226,6 +243,8 @@ %42 = OpIAdd %int %41 %int_1 OpStore %j %42 OpBranch %31 +%50 = OpLabel +OpBranch %29 %29 = OpLabel %43 = OpLoad %int %i %44 = OpIAdd %int %43 %int_1 @@ -238,50 +257,7 @@ OpFunctionEnd )"; - const std::string after = - R"(%main = OpFunction %void None %9 -%24 = OpLabel -%f = OpVariable %_ptr_Function_float Function -%i = OpVariable %_ptr_Function_int Function -%j = OpVariable %_ptr_Function_int Function -OpStore %f %float_0 -OpStore %i %int_0 -OpBranch %25 -%25 = OpLabel -%47 = OpPhi %float %float_0 %24 %50 %29 -%46 = OpPhi %int %int_0 %24 %44 %29 -%27 = OpSLessThan %bool %46 %int_4 -OpLoopMerge %28 %29 None -OpBranchConditional %27 %30 %28 -%30 = OpLabel -OpStore %j %int_0 -OpBranch %31 -%31 = OpLabel -%50 = OpPhi %float %47 %30 %40 %34 -%48 = OpPhi %int %int_0 %30 %42 %34 -%33 = OpSLessThan %bool %48 %int_4 -OpLoopMerge %29 %34 None -OpBranchConditional %33 %34 %29 -%34 = OpLabel -%38 = OpAccessChain %_ptr_Input_float %BC %46 %48 -%39 = OpLoad %float %38 -%40 = OpFAdd %float %50 %39 -OpStore %f %40 -%42 = OpIAdd %int %48 %int_1 -OpStore %j %42 -OpBranch %31 -%29 = OpLabel -%44 = OpIAdd %int %46 %int_1 -OpStore %i %44 -OpBranch %25 -%28 = OpLabel -OpStore %fo %47 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true, - true); + SinglePassRunAndMatch<SSARewritePass>(predefs + before, true); } TEST_F(LocalSSAElimTest, ForLoopWithContinue) {
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/fusion_legal.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/fusion_legal.cpp index 41d796f..56b0b76 100644 --- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/fusion_legal.cpp +++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/fusion_legal.cpp
@@ -3177,7 +3177,7 @@ %21 = OpLabel %29 = OpSMod %6 %96 %28 %30 = OpIEqual %17 %29 %9 - OpSelectionMerge %23 None + OpSelectionMerge %sel_merge None OpBranchConditional %30 %31 %48 %31 = OpLabel %44 = OpAccessChain %7 %41 %91 %96 @@ -3187,8 +3187,10 @@ OpStore %47 %46 OpBranch %32 %48 = OpLabel - OpBranch %23 + OpBranch %sel_merge %32 = OpLabel + OpBranch %sel_merge + %sel_merge = OpLabel OpBranch %23 %23 = OpLabel %52 = OpIAdd %6 %96 %51
diff --git a/third_party/SPIRV-Tools/test/opt/optimizer_test.cpp b/third_party/SPIRV-Tools/test/opt/optimizer_test.cpp index 5c0707d..945aa78 100644 --- a/third_party/SPIRV-Tools/test/opt/optimizer_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/optimizer_test.cpp
@@ -234,7 +234,6 @@ "eliminate-dead-code-aggressive", "eliminate-dead-const", "flatten-decorations", - "strip-debug", "strip-atomic-counter-memory", "generate-webgpu-initializers", "legalize-vector-shuffle", @@ -330,35 +329,6 @@ "OpFunctionEnd\n", // pass "flatten-decorations"}, - // Strip Debug - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %func \"shader\"\n" - "OpName %main \"main\"\n" - "OpName %void_fn \"void_fn\"\n" - "%void = OpTypeVoid\n" - "%void_f = OpTypeFunction %void\n" - "%func = OpFunction %void None %void_f\n" - "%label = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%void = OpTypeVoid\n" - "%3 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %3\n" - "%4 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "strip-debug"}, // Eliminate Dead Constants {// input "OpCapability Shader\n"
diff --git a/third_party/SPIRV-Tools/test/opt/pass_fixture.h b/third_party/SPIRV-Tools/test/opt/pass_fixture.h index 53fb206..64c089d 100644 --- a/third_party/SPIRV-Tools/test/opt/pass_fixture.h +++ b/third_party/SPIRV-Tools/test/opt/pass_fixture.h
@@ -60,8 +60,7 @@ // from pass Process() function. std::tuple<std::vector<uint32_t>, Pass::Status> OptimizeToBinary( Pass* pass, const std::string& original, bool skip_nop) { - context_ = - std::move(BuildModule(env_, consumer_, original, assemble_options_)); + context_ = BuildModule(env_, consumer_, original, assemble_options_); EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n" << original << std::endl; if (!context()) { @@ -197,8 +196,7 @@ // messages. template <typename PassT, typename... Args> void SinglePassRunAndFail(const std::string& original, Args&&... args) { - context_ = - std::move(BuildModule(env_, consumer_, original, assemble_options_)); + context_ = BuildModule(env_, consumer_, original, assemble_options_); EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n" << original << std::endl; std::ostringstream errs; @@ -235,8 +233,7 @@ void RunAndCheck(const std::string& original, const std::string& expected) { assert(manager_->NumPasses()); - context_ = - std::move(BuildModule(env_, nullptr, original, assemble_options_)); + context_ = BuildModule(env_, nullptr, original, assemble_options_); ASSERT_NE(nullptr, context()); context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_);
diff --git a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp index a305680..4118169 100644 --- a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
@@ -1390,7 +1390,6 @@ OpLoopMerge %36 %40 None OpBranch %35 %35 = OpLabel - OpSelectionMerge %40 None OpBranchConditional %77 %39 %40 %39 = OpLabel OpReturnValue %16 @@ -1402,7 +1401,6 @@ OpLoopMerge %45 %49 None OpBranch %44 %44 = OpLabel - OpSelectionMerge %49 None OpBranchConditional %77 %48 %49 %48 = OpLabel OpReturnValue %16 @@ -1415,7 +1413,6 @@ OpLoopMerge %64 %68 None OpBranch %63 %63 = OpLabel - OpSelectionMerge %68 None OpBranchConditional %77 %67 %68 %67 = OpLabel OpReturnValue %16 @@ -1813,12 +1810,14 @@ OpLoopMerge %11 %12 None OpBranch %13 %13 = OpLabel - OpLoopMerge %12 %14 None - OpBranchConditional %8 %15 %12 + OpLoopMerge %18 %14 None + OpBranchConditional %8 %15 %18 %15 = OpLabel OpReturn %14 = OpLabel OpBranch %13 + %18 = OpLabel + OpBranch %12 %12 = OpLabel %16 = OpUndef %float OpBranchConditional %8 %10 %11
diff --git a/third_party/SPIRV-Tools/test/opt/register_liveness.cpp b/third_party/SPIRV-Tools/test/opt/register_liveness.cpp index cb973d2..7cb210f 100644 --- a/third_party/SPIRV-Tools/test/opt/register_liveness.cpp +++ b/third_party/SPIRV-Tools/test/opt/register_liveness.cpp
@@ -1277,6 +1277,46 @@ } } +// Test that register liveness does not fail when there is an unreachable block. +// We are not testing if the liveness is computed correctly because the specific +// results do not matter for unreachable blocks. +TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginLowerLeft + OpSource GLSL 330 + OpSourceExtension "GL_ARB_shading_language_420pack" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %2 = OpFunction %void None %4 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %9 + %9 = OpLabel + OpBranch %7 + %8 = OpLabel + OpBranch %6 + %7 = 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 shader:\n" + << text << std::endl; + Function* f = &*module->begin(); + LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); + liveness_analysis->Get(f); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/strip_debug_info_test.cpp b/third_party/SPIRV-Tools/test/opt/strip_debug_info_test.cpp index 2f2ff46..088bba9 100644 --- a/third_party/SPIRV-Tools/test/opt/strip_debug_info_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/strip_debug_info_test.cpp
@@ -152,6 +152,53 @@ /* do_validation */ true); } +TEST_F(StripDebugStringTest, OpStringRemovedWithNonSemantic) { + std::vector<const char*> input{ + // clang-format off + "OpCapability Shader", + "OpExtension \"SPV_KHR_non_semantic_info\"", + "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + // this string is not referenced, should be removed fully + "%3 = OpString \"minimal.vert\"", + "OpName %3 \"bob\"", + // this string is referenced and cannot be removed, + // but the name should be + "%4 = OpString \"secondary.inc\"", + "OpName %4 \"sue\"", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void", + "%2 = OpFunction %void None %6", + "%7 = OpLabel", + "%8 = OpExtInst %void %1 5 %4", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::vector<const char*> output{ + // clang-format off + "OpCapability Shader", + "OpExtension \"SPV_KHR_non_semantic_info\"", + "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%4 = OpString \"secondary.inc\"", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void", + "%2 = OpFunction %void None %6", + "%7 = OpLabel", + "%8 = OpExtInst %void %1 5 %4", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck<StripDebugInfoPass>(JoinAllInsts(input), + JoinAllInsts(output), + /* skip_nop = */ false, + /* do_validation */ true); +} + using StripDebugInfoTest = PassTest<::testing::TestWithParam<const char*>>; TEST_P(StripDebugInfoTest, Kind) {
diff --git a/third_party/SPIRV-Tools/test/opt/strip_reflect_info_test.cpp b/third_party/SPIRV-Tools/test/opt/strip_reflect_info_test.cpp index a9cfd47..5db34b7 100644 --- a/third_party/SPIRV-Tools/test/opt/strip_reflect_info_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/strip_reflect_info_test.cpp
@@ -13,6 +13,9 @@ // limitations under the License. #include <string> +#include "gmock/gmock.h" + +#include "spirv-tools/optimizer.hpp" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" @@ -23,6 +26,50 @@ using StripLineReflectInfoTest = PassTest<::testing::Test>; +// This test acts as an end-to-end code example on how to strip +// reflection info from a SPIR-V module. Use this code pattern +// when you have compiled HLSL code with Glslang or DXC using +// option -fhlsl_functionality1 to insert reflection information, +// but then want to filter out the extra instructions before sending +// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. +TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::vector<uint32_t> binary_in; + tools.Assemble(before, &binary_in); + + // Instantiate the optimizer, and run the strip-reflection-info + // pass over the |binary_in| module, and place the modified module + // into |binary_out|. + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); + optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass()); + std::vector<uint32_t> binary_out; + optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); + + // Check results + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + EXPECT_THAT(disassembly, testing::Eq(after)); +} + +// This test is functionally the same as the end-to-end test above, +// but uses the test SinglePassRunAndCheck test fixture instead. TEST_F(StripLineReflectInfoTest, StripHlslSemantic) { // This is a non-sensical example, but exercises the instructions. std::string before = R"(OpCapability Shader
diff --git a/third_party/SPIRV-Tools/test/opt/value_table_test.cpp b/third_party/SPIRV-Tools/test/opt/value_table_test.cpp index 0b7530c..a0942cc 100644 --- a/third_party/SPIRV-Tools/test/opt/value_table_test.cpp +++ b/third_party/SPIRV-Tools/test/opt/value_table_test.cpp
@@ -653,6 +653,37 @@ EXPECT_NE(vtable.GetValueNumber(phi1), vtable.GetValueNumber(phi2)); } +// Test to make sure that OpPhi instructions with no in operands are handled +// correctly. +TEST_F(ValueTableTest, EmptyPhiTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %2 = OpFunction %void None %4 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %true %9 %8 + %9 = OpLabel + OpKill + %8 = OpLabel + %10 = OpPhi %bool + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst = context->get_def_use_mgr()->GetDef(10); + vtable.GetValueNumber(inst); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp b/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp index a650d3b..59f2803 100644 --- a/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp +++ b/third_party/SPIRV-Tools/test/reduce/reducer_test.cpp
@@ -125,19 +125,21 @@ %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel @@ -193,11 +195,13 @@ OpLoopMerge %12 %34 None OpBranchConditional %100 %11 %12 %11 = OpLabel - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %100 %33 %46 %33 = OpLabel - OpBranch %34 + OpBranch %90 %46 = OpLabel + OpBranch %90 + %90 = OpLabel OpBranch %34 %34 = OpLabel OpBranch %10 @@ -345,7 +349,6 @@ OpLoopMerge %33 %38 None OpBranch %32 %32 = OpLabel - OpSelectionMerge %38 None OpBranchConditional %30 %37 %38 %37 = OpLabel OpSelectionMerge %42 None
diff --git a/third_party/SPIRV-Tools/test/reduce/validation_during_reduction_test.cpp b/third_party/SPIRV-Tools/test/reduce/validation_during_reduction_test.cpp index 4051bac..2981c2e 100644 --- a/third_party/SPIRV-Tools/test/reduce/validation_during_reduction_test.cpp +++ b/third_party/SPIRV-Tools/test/reduce/validation_during_reduction_test.cpp
@@ -13,7 +13,6 @@ // limitations under the License. #include "source/reduce/reducer.h" - #include "source/reduce/reduction_opportunity.h" #include "source/reduce/remove_instruction_reduction_opportunity.h" #include "test/reduce/reduce_test_util.h" @@ -23,8 +22,8 @@ namespace { using opt::Function; -using opt::IRContext; using opt::Instruction; +using opt::IRContext; // A dumb reduction opportunity finder that finds opportunities to remove global // values regardless of whether they are referenced. This is very likely to make @@ -181,19 +180,21 @@ %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel @@ -303,19 +304,21 @@ %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel @@ -392,19 +395,21 @@ %29 = OpAccessChain %28 %27 %9 %30 = OpLoad %24 %29 %32 = OpFOrdGreaterThan %22 %30 %31 - OpSelectionMerge %34 None + OpSelectionMerge %90 None OpBranchConditional %32 %33 %46 %33 = OpLabel %40 = OpFAdd %24 %71 %30 %45 = OpISub %6 %73 %21 - OpBranch %34 + OpBranch %90 %46 = OpLabel %50 = OpFMul %24 %71 %30 %54 = OpSDiv %6 %73 %21 - OpBranch %34 - %34 = OpLabel + OpBranch %90 + %90 = OpLabel %77 = OpPhi %6 %45 %33 %54 %46 %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel %57 = OpIAdd %6 %70 %56 OpBranch %10 %12 = OpLabel
diff --git a/third_party/SPIRV-Tools/test/tools/CMakeLists.txt b/third_party/SPIRV-Tools/test/tools/CMakeLists.txt index cee95ca..99f9780 100644 --- a/third_party/SPIRV-Tools/test/tools/CMakeLists.txt +++ b/third_party/SPIRV-Tools/test/tools/CMakeLists.txt
@@ -12,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -spirv_add_nosetests(expect) -spirv_add_nosetests(spirv_test_framework) - +add_test(NAME spirv-tools_expect_unittests + COMMAND ${PYTHON_EXECUTABLE} -m unittest expect_unittest.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_test(NAME spirv-tools_spirv_test_framework_unittests + COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(opt)
diff --git a/third_party/SPIRV-Tools/test/tools/expect.py b/third_party/SPIRV-Tools/test/tools/expect.py index 52999ce..0b51adc 100755 --- a/third_party/SPIRV-Tools/test/tools/expect.py +++ b/third_party/SPIRV-Tools/test/tools/expect.py
@@ -510,7 +510,7 @@ if not status.stderr: return False, 'Expected error message, but no output on stderr' if self.expected_error_substr not in convert_to_unix_line_endings( - status.stderr.decode('utf8')): + status.stderr): return False, ('Incorrect stderr output:\n{act}\n' 'Expected substring not found in stderr:\n{exp}'.format( act=status.stderr, exp=self.expected_error_substr)) @@ -530,7 +530,7 @@ ' command execution') if not status.stderr: return False, 'Expected warning message, but no output on stderr' - if self.expected_warning != convert_to_unix_line_endings(status.stderr.decode('utf8')): + if self.expected_warning != convert_to_unix_line_endings(status.stderr): return False, ('Incorrect stderr output:\n{act}\n' 'Expected:\n{exp}'.format( act=status.stderr, exp=self.expected_warning)) @@ -590,16 +590,16 @@ if not status.stdout: return False, 'Expected something on stdout' elif type(self.expected_stdout) == str: - if self.expected_stdout != convert_to_unix_line_endings(status.stdout.decode('utf8')): + if self.expected_stdout != convert_to_unix_line_endings(status.stdout): return False, ('Incorrect stdout output:\n{ac}\n' 'Expected:\n{ex}'.format( ac=status.stdout, ex=self.expected_stdout)) else: - converted = convert_to_unix_line_endings(status.stdout.decode('utf8')) + converted = convert_to_unix_line_endings(status.stdout) if not self.expected_stdout.search(converted): return False, ('Incorrect stdout output:\n{ac}\n' 'Expected to match regex:\n{ex}'.format( - ac=status.stdout.decode('utf8'), ex=self.expected_stdout.pattern)) + ac=status.stdout, ex=self.expected_stdout.pattern)) return True, '' @@ -624,13 +624,13 @@ if not status.stderr: return False, 'Expected something on stderr' elif type(self.expected_stderr) == str: - if self.expected_stderr != convert_to_unix_line_endings(status.stderr.decode('utf8')): + if self.expected_stderr != convert_to_unix_line_endings(status.stderr): return False, ('Incorrect stderr output:\n{ac}\n' 'Expected:\n{ex}'.format( ac=status.stderr, ex=self.expected_stderr)) else: if not self.expected_stderr.search( - convert_to_unix_line_endings(status.stderr.decode('utf8'))): + convert_to_unix_line_endings(status.stderr)): return False, ('Incorrect stderr output:\n{ac}\n' 'Expected to match regex:\n{ex}'.format( ac=status.stderr, ex=self.expected_stderr.pattern)) @@ -695,7 +695,7 @@ # Collect all the output lines containing a pass name. pass_names = [] pass_name_re = re.compile(r'.*IR before pass (?P<pass_name>[\S]+)') - for line in status.stderr.decode('utf8').splitlines(): + for line in status.stderr.splitlines(): match = pass_name_re.match(line) if match: pass_names.append(match.group('pass_name'))
diff --git a/third_party/SPIRV-Tools/test/tools/expect_nosetest.py b/third_party/SPIRV-Tools/test/tools/expect_nosetest.py deleted file mode 100755 index b591a2d..0000000 --- a/third_party/SPIRV-Tools/test/tools/expect_nosetest.py +++ /dev/null
@@ -1,80 +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. -"""Tests for the expect module.""" - -import expect -from spirv_test_framework import TestStatus -from nose.tools import assert_equal, assert_true, assert_false -import re - - -def nosetest_get_object_name(): - """Tests get_object_filename().""" - source_and_object_names = [('a.vert', 'a.vert.spv'), ('b.frag', 'b.frag.spv'), - ('c.tesc', 'c.tesc.spv'), ('d.tese', 'd.tese.spv'), - ('e.geom', 'e.geom.spv'), ('f.comp', 'f.comp.spv'), - ('file', 'file.spv'), ('file.', 'file.spv'), - ('file.uk', - 'file.spv'), ('file.vert.', - 'file.vert.spv'), ('file.vert.bla', - 'file.vert.spv')] - actual_object_names = [ - expect.get_object_filename(f[0]) for f in source_and_object_names - ] - expected_object_names = [f[1] for f in source_and_object_names] - - assert_equal(actual_object_names, expected_object_names) - - -class TestStdoutMatchADotC(expect.StdoutMatch): - expected_stdout = re.compile('a.c') - - -def nosetest_stdout_match_regex_has_match(): - test = TestStdoutMatchADotC() - status = TestStatus( - test_manager=None, - returncode=0, - stdout='0abc1', - stderr=None, - directory=None, - inputs=None, - input_filenames=None) - assert_true(test.check_stdout_match(status)[0]) - - -def nosetest_stdout_match_regex_no_match(): - test = TestStdoutMatchADotC() - status = TestStatus( - test_manager=None, - returncode=0, - stdout='ab', - stderr=None, - directory=None, - inputs=None, - input_filenames=None) - assert_false(test.check_stdout_match(status)[0]) - - -def nosetest_stdout_match_regex_empty_stdout(): - test = TestStdoutMatchADotC() - status = TestStatus( - test_manager=None, - returncode=0, - stdout='', - stderr=None, - directory=None, - inputs=None, - input_filenames=None) - assert_false(test.check_stdout_match(status)[0])
diff --git a/third_party/SPIRV-Tools/test/tools/expect_unittest.py b/third_party/SPIRV-Tools/test/tools/expect_unittest.py new file mode 100644 index 0000000..a28de1b --- /dev/null +++ b/third_party/SPIRV-Tools/test/tools/expect_unittest.py
@@ -0,0 +1,82 @@ +# Copyright (c) 2019 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. +"""Tests for the expect module.""" + +import expect +from spirv_test_framework import TestStatus +import re +import unittest + + +class TestStdoutMatchADotC(expect.StdoutMatch): + expected_stdout = re.compile('a.c') + + +class TestExpect(unittest.TestCase): + def test_get_object_name(self): + """Tests get_object_filename().""" + source_and_object_names = [('a.vert', 'a.vert.spv'), + ('b.frag', 'b.frag.spv'), + ('c.tesc', 'c.tesc.spv'), + ('d.tese', 'd.tese.spv'), + ('e.geom', 'e.geom.spv'), + ('f.comp', 'f.comp.spv'), + ('file', 'file.spv'), ('file.', 'file.spv'), + ('file.uk', + 'file.spv'), ('file.vert.', + 'file.vert.spv'), + ('file.vert.bla', + 'file.vert.spv')] + actual_object_names = [ + expect.get_object_filename(f[0]) for f in source_and_object_names + ] + expected_object_names = [f[1] for f in source_and_object_names] + + self.assertEqual(actual_object_names, expected_object_names) + + def test_stdout_match_regex_has_match(self): + test = TestStdoutMatchADotC() + status = TestStatus( + test_manager=None, + returncode=0, + stdout=b'0abc1', + stderr=None, + directory=None, + inputs=None, + input_filenames=None) + self.assertTrue(test.check_stdout_match(status)[0]) + + def test_stdout_match_regex_no_match(self): + test = TestStdoutMatchADotC() + status = TestStatus( + test_manager=None, + returncode=0, + stdout=b'ab', + stderr=None, + directory=None, + inputs=None, + input_filenames=None) + self.assertFalse(test.check_stdout_match(status)[0]) + + def test_stdout_match_regex_empty_stdout(self): + test = TestStdoutMatchADotC() + status = TestStatus( + test_manager=None, + returncode=0, + stdout=b'', + stderr=None, + directory=None, + inputs=None, + input_filenames=None) + self.assertFalse(test.check_stdout_match(status)[0])
diff --git a/third_party/SPIRV-Tools/test/tools/opt/flags.py b/third_party/SPIRV-Tools/test/tools/opt/flags.py index b34a168..2f6c0a7 100644 --- a/third_party/SPIRV-Tools/test/tools/opt/flags.py +++ b/third_party/SPIRV-Tools/test/tools/opt/flags.py
@@ -187,28 +187,35 @@ 'eliminate-dead-branches', 'merge-return', 'inline-entry-points-exhaustive', - 'eliminate-dead-code-aggressive', + 'eliminate-dead-functions', 'private-to-local', - 'scalar-replacement=100', - 'convert-local-access-chains', - 'eliminate-local-single-block', - 'eliminate-local-single-store', - 'eliminate-dead-code-aggressive', - 'simplify-instructions', - 'eliminate-dead-inserts', + 'scalar-replacement=0', 'ssa-rewrite', - 'eliminate-dead-code-aggressive', 'ccp', + 'loop-unroll', + 'eliminate-dead-branches', + 'simplify-instructions', + 'scalar-replacement=0', + 'eliminate-local-single-store', + 'if-conversion', + 'simplify-instructions', 'eliminate-dead-code-aggressive', 'eliminate-dead-branches', - 'if-conversion', - 'eliminate-dead-code-aggressive', 'merge-blocks', - 'simplify-instructions', - 'eliminate-dead-inserts', - 'redundancy-elimination', - 'cfg-cleanup', + 'convert-local-access-chains', + 'eliminate-local-single-block', 'eliminate-dead-code-aggressive', + 'copy-propagate-arrays', + 'vector-dce', + 'eliminate-dead-inserts', + 'eliminate-dead-members', + 'eliminate-local-single-store', + 'merge-blocks', + 'ssa-rewrite', + 'redundancy-elimination', + 'simplify-instructions', + 'eliminate-dead-code-aggressive', + 'cfg-cleanup', ] shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') output = placeholder.TempFileName('output.spv')
diff --git a/third_party/SPIRV-Tools/test/tools/spirv_test_framework.py b/third_party/SPIRV-Tools/test/tools/spirv_test_framework.py index d8d64f3..42f83c6 100755 --- a/third_party/SPIRV-Tools/test/tools/spirv_test_framework.py +++ b/third_party/SPIRV-Tools/test/tools/spirv_test_framework.py
@@ -70,7 +70,7 @@ def get_all_superclasses(cls): - """Returns all superclasses of a given class. + """Returns all superclasses of a given class. Omits root 'object' superclass. Returns: A list of superclasses of the given class. The order guarantees that @@ -83,11 +83,12 @@ classes = [] for superclass in cls.__bases__: for c in get_all_superclasses(superclass): - if c not in classes: + if c is not object and c not in classes: classes.append(c) for superclass in cls.__bases__: - if superclass not in classes: + if superclass is not object and superclass not in classes: classes.append(superclass) + return classes @@ -142,8 +143,28 @@ inputs, input_filenames): self.test_manager = test_manager self.returncode = returncode - self.stdout = stdout - self.stderr = stderr + # Some of our MacOS bots still run Python 2, so need to be backwards + # compatible here. + if type(stdout) is not str: + if sys.version_info[0] is 2: + self.stdout = stdout.decode('utf-8') + elif sys.version_info[0] is 3: + self.stdout = str(stdout, encoding='utf-8') if stdout is not None else stdout + else: + raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info)) + else: + self.stdout = stdout + + if type(stderr) is not str: + if sys.version_info[0] is 2: + self.stderr = stderr.decode('utf-8') + elif sys.version_info[0] is 3: + self.stderr = str(stderr, encoding='utf-8') if stderr is not None else stderr + else: + raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info)) + else: + self.stderr = stderr + # temporary directory where the test runs self.directory = directory # List of inputs, as PlaceHolder objects.
diff --git a/third_party/SPIRV-Tools/test/tools/spirv_test_framework_nosetest.py b/third_party/SPIRV-Tools/test/tools/spirv_test_framework_nosetest.py deleted file mode 100755 index c0fbed5..0000000 --- a/third_party/SPIRV-Tools/test/tools/spirv_test_framework_nosetest.py +++ /dev/null
@@ -1,155 +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. - -from spirv_test_framework import get_all_test_methods, get_all_superclasses -from nose.tools import assert_equal, with_setup - - -# Classes to be used in testing get_all_{superclasses|test_methods}() -class Root: - - def check_root(self): - pass - - -class A(Root): - - def check_a(self): - pass - - -class B(Root): - - def check_b(self): - pass - - -class C(Root): - - def check_c(self): - pass - - -class D(Root): - - def check_d(self): - pass - - -class E(Root): - - def check_e(self): - pass - - -class H(B, C, D): - - def check_h(self): - pass - - -class I(E): - - def check_i(self): - pass - - -class O(H, I): - - def check_o(self): - pass - - -class U(A, O): - - def check_u(self): - pass - - -class X(U, A): - - def check_x(self): - pass - - -class R1: - - def check_r1(self): - pass - - -class R2: - - def check_r2(self): - pass - - -class Multi(R1, R2): - - def check_multi(self): - pass - - -def nosetest_get_all_superclasses(): - """Tests get_all_superclasses().""" - - assert_equal(get_all_superclasses(A), [Root]) - assert_equal(get_all_superclasses(B), [Root]) - assert_equal(get_all_superclasses(C), [Root]) - assert_equal(get_all_superclasses(D), [Root]) - assert_equal(get_all_superclasses(E), [Root]) - - assert_equal(get_all_superclasses(H), [Root, B, C, D]) - assert_equal(get_all_superclasses(I), [Root, E]) - - assert_equal(get_all_superclasses(O), [Root, B, C, D, E, H, I]) - - assert_equal(get_all_superclasses(U), [Root, B, C, D, E, H, I, A, O]) - assert_equal(get_all_superclasses(X), [Root, B, C, D, E, H, I, A, O, U]) - - assert_equal(get_all_superclasses(Multi), [R1, R2]) - - -def nosetest_get_all_methods(): - """Tests get_all_test_methods().""" - assert_equal(get_all_test_methods(A), ['check_root', 'check_a']) - assert_equal(get_all_test_methods(B), ['check_root', 'check_b']) - assert_equal(get_all_test_methods(C), ['check_root', 'check_c']) - assert_equal(get_all_test_methods(D), ['check_root', 'check_d']) - assert_equal(get_all_test_methods(E), ['check_root', 'check_e']) - - assert_equal( - get_all_test_methods(H), - ['check_root', 'check_b', 'check_c', 'check_d', 'check_h']) - assert_equal(get_all_test_methods(I), ['check_root', 'check_e', 'check_i']) - - assert_equal( - get_all_test_methods(O), [ - 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', - 'check_i', 'check_o' - ]) - - assert_equal( - get_all_test_methods(U), [ - 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', - 'check_i', 'check_a', 'check_o', 'check_u' - ]) - assert_equal( - get_all_test_methods(X), [ - 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', - 'check_i', 'check_a', 'check_o', 'check_u', 'check_x' - ]) - - assert_equal( - get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi'])
diff --git a/third_party/SPIRV-Tools/test/tools/spirv_test_framework_unittest.py b/third_party/SPIRV-Tools/test/tools/spirv_test_framework_unittest.py new file mode 100644 index 0000000..e64e86c --- /dev/null +++ b/third_party/SPIRV-Tools/test/tools/spirv_test_framework_unittest.py
@@ -0,0 +1,158 @@ +# Copyright (c) 2019 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. +"""Tests for the spirv test framework module.""" + +from spirv_test_framework import get_all_test_methods, get_all_superclasses +import unittest + +# Classes to be used in testing get_all_{superclasses|test_methods}() + + +class Root: + + def check_root(self): + pass + + +class A(Root): + + def check_a(self): + pass + + +class B(Root): + + def check_b(self): + pass + + +class C(Root): + + def check_c(self): + pass + + +class D(Root): + + def check_d(self): + pass + + +class E(Root): + + def check_e(self): + pass + + +class H(B, C, D): + + def check_h(self): + pass + + +class I(E): + + def check_i(self): + pass + + +class O(H, I): + + def check_o(self): + pass + + +class U(A, O): + + def check_u(self): + pass + + +class X(U, A): + + def check_x(self): + pass + + +class R1: + + def check_r1(self): + pass + + +class R2: + + def check_r2(self): + pass + + +class Multi(R1, R2): + + def check_multi(self): + pass + + +class TestSpirvTestFramework(unittest.TestCase): + def test_get_all_superclasses(self): + self.assertEqual(get_all_superclasses(A), [Root]) + self.assertEqual(get_all_superclasses(B), [Root]) + self.assertEqual(get_all_superclasses(C), [Root]) + self.assertEqual(get_all_superclasses(D), [Root]) + self.assertEqual(get_all_superclasses(E), [Root]) + + self.assertEqual(get_all_superclasses(H), [Root, B, C, D]) + self.assertEqual(get_all_superclasses(I), [Root, E]) + + self.assertEqual(get_all_superclasses(O), [Root, B, C, D, E, H, I]) + + self.assertEqual(get_all_superclasses( + U), [Root, B, C, D, E, H, I, A, O]) + self.assertEqual(get_all_superclasses( + X), [Root, B, C, D, E, H, I, A, O, U]) + + self.assertEqual(get_all_superclasses(Multi), [R1, R2]) + + def test_get_all_methods(self): + self.assertEqual(get_all_test_methods(A), ['check_root', 'check_a']) + self.assertEqual(get_all_test_methods(B), ['check_root', 'check_b']) + self.assertEqual(get_all_test_methods(C), ['check_root', 'check_c']) + self.assertEqual(get_all_test_methods(D), ['check_root', 'check_d']) + self.assertEqual(get_all_test_methods(E), ['check_root', 'check_e']) + + self.assertEqual( + get_all_test_methods(H), + ['check_root', 'check_b', 'check_c', 'check_d', 'check_h']) + self.assertEqual(get_all_test_methods( + I), ['check_root', 'check_e', 'check_i']) + + self.assertEqual( + get_all_test_methods(O), [ + 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', + 'check_i', 'check_o' + ]) + + self.assertEqual( + get_all_test_methods(U), [ + 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', + 'check_i', 'check_a', 'check_o', 'check_u' + ]) + + self.assertEqual( + get_all_test_methods(X), [ + 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', + 'check_i', 'check_a', 'check_o', 'check_u', 'check_x' + ]) + + self.assertEqual( + get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi'])
diff --git a/third_party/SPIRV-Tools/test/val/CMakeLists.txt b/third_party/SPIRV-Tools/test/val/CMakeLists.txt index d4bfe1d..138e711 100644 --- a/third_party/SPIRV-Tools/test/val/CMakeLists.txt +++ b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
@@ -70,6 +70,7 @@ val_memory_test.cpp val_misc_test.cpp val_modes_test.cpp + val_non_semantic_test.cpp val_non_uniform_test.cpp val_opencl_test.cpp val_primitives_test.cpp
diff --git a/third_party/SPIRV-Tools/test/val/val_adjacency_test.cpp b/third_party/SPIRV-Tools/test/val/val_adjacency_test.cpp index e61c03d..0b09de0 100644 --- a/third_party/SPIRV-Tools/test/val/val_adjacency_test.cpp +++ b/third_party/SPIRV-Tools/test/val/val_adjacency_test.cpp
@@ -315,6 +315,243 @@ "non-OpPhi instructions")); } +TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +%dummy = OpExtInst %void %extinst 123 %int_1 +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + const std::string extra = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +%result1 = OpPhi %bool %true %true_label %false %false_label +%dummy = OpExtInst %void %extinst 123 %int_1 +%result2 = OpPhi %bool %true %true_label %false %false_label +)"; + + const std::string extra = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpLine %string 0 0 +%result = OpPhi %bool %true %true_label %false %false_label +%dummy = OpExtInst %void %extinst 123 %int_1 +)"; + + const std::string extra = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%dummy = OpExtInst %void %extinst 123 %int_1 +%a = OpFunctionParameter %int +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +OpReturn +OpFunctionEnd + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Non-semantic OpExtInst within function definition " + "must appear in a block")); +} + +TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%a = OpFunctionParameter %int +%dummy = OpExtInst %void %extinst 123 %int_1 +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +OpReturn +OpFunctionEnd + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Non-semantic OpExtInst within function definition " + "must appear in a block")); +} + +TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%a = OpFunctionParameter %int +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +%dummy = OpExtInst %void %extinst 123 %int_1 +OpReturn +OpFunctionEnd + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%a = OpFunctionParameter %int +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +OpReturn +OpFunctionEnd + +%dummy = OpExtInst %void %extinst 123 %int_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateAdjacency, OpVariableInFunctionGood) { const std::string body = R"( OpLine %string 1 1
diff --git a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp index f06f36c..8d8de37 100644 --- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp +++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -23,7 +23,6 @@ #include <vector> #include "gmock/gmock.h" - #include "source/diagnostic.h" #include "source/spirv_target_env.h" #include "source/val/validate.h" @@ -1355,7 +1354,7 @@ } if (cap == SpvCapabilityShader) { branch.AppendBody("OpLoopMerge %merge %target None\n"); - body.AppendBody("OpSelectionMerge %target None\n"); + body.AppendBody("OpSelectionMerge %f None\n"); } if (!spvIsWebGPUEnv(env)) @@ -1650,25 +1649,27 @@ Block entry("entry"); Block loop1("loop1", SpvOpBranchConditional); Block loop2("loop2", SpvOpBranchConditional); - Block loop2_merge("loop2_merge", SpvOpBranchConditional); + Block loop2_merge("loop2_merge"); + Block loop1_cont("loop1_cont", SpvOpBranchConditional); Block be_block("be_block"); Block exit("exit", SpvOpReturn); entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { - loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n"); + loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n"); loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); } - std::string str = GetDefaultHeader(GetParam()) + - nameOps("loop1", "loop2", "be_block", "loop2_merge") + - types_consts() + - "%func = OpFunction %voidt None %funct\n"; + std::string str = + GetDefaultHeader(GetParam()) + + nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") + + types_consts() + "%func = OpFunction %voidt None %funct\n"; str += entry >> loop1; str += loop1 >> std::vector<Block>({loop2, exit}); str += loop2 >> std::vector<Block>({loop2, loop2_merge}); - str += loop2_merge >> std::vector<Block>({be_block, exit}); + str += loop2_merge >> loop1_cont; + str += loop1_cont >> std::vector<Block>({be_block, exit}); str += be_block >> loop1; str += exit; str += "OpFunctionEnd"; @@ -1678,7 +1679,7 @@ ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), MatchesRegex("The continue construct with the continue target " - ".\\[%loop2_merge\\] is not post dominated by the " + ".\\[%loop1_cont\\] is not post dominated by the " "back-edge block .\\[%be_block\\]\n" " %be_block = OpLabel\n")); } else { @@ -2037,13 +2038,10 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); } -TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) { - // This example is valid. It shows that the validator can't just add - // an edge from the loop head to the continue target. If that edge - // is added, then the "if_merge" block is both the continue target - // for the loop and also the merge block for the nested selection, but - // then it wouldn't be dominated by "if_head", the header block for the - // nested selection. +TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) { + // The continue construct cannot be the merge target of a nested selection + // because the loop construct must contain "if_merge" because it contains + // "if_head". bool is_shader = GetParam() == SpvCapabilityShader; Block entry("entry"); Block loop("loop"); @@ -2072,7 +2070,16 @@ str += "OpFunctionEnd"; CompileSuccessfully(str); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); + if (is_shader) { + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Header block 3[%if_head] is contained in the loop construct " + "headed " + "by 2[%loop], but its merge block 5[%if_merge] is not")); + } else { + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); + } } TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) { @@ -3681,17 +3688,21 @@ OpLoopMerge %8 %9 None OpBranch %10 %10 = OpLabel -OpLoopMerge %9 %11 None +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %3 %14 %15 +%15 = OpLabel +OpBranch %8 +%14 = OpLabel OpBranch %12 %12 = OpLabel -OpSelectionMerge %11 None -OpBranchConditional %3 %11 %13 -%13 = OpLabel -OpBranch %8 +OpBranchConditional %3 %10 %11 %11 = OpLabel -OpBranchConditional %3 %9 %10 +OpBranch %9 %9 = OpLabel -OpBranchConditional %3 %8 %7 +OpBranchConditional %3 %7 %8 %8 = OpLabel OpReturn OpFunctionEnd @@ -3700,7 +3711,7 @@ CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("block <ID> 13[%13] exits the loop headed by <ID> " + HasSubstr("block <ID> 15[%15] exits the loop headed by <ID> " "10[%10], but not via a structured exit")); } @@ -3726,9 +3737,11 @@ OpSelectionMerge %31 None OpBranchConditional %undef %30 %31 %30 = OpLabel -OpSelectionMerge %37 None -OpBranchConditional %undef %36 %37 +OpSelectionMerge %38 None +OpBranchConditional %undef %36 %38 %36 = OpLabel +OpBranch %38 +%38 = OpLabel OpBranch %37 %37 = OpLabel OpBranch %10 @@ -4100,6 +4113,80 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); } +TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %loop "loop" +OpName %continue "continue" +OpName %body "body" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpSelectionMerge %continue None +OpBranchConditional %undef %exit %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Header block 3[%body] is contained in the loop construct headed by " + "1[%loop], but its merge block 2[%continue] is not")); +} + +TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %loop "loop" +OpName %continue "continue" +OpName %inner "inner" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranchConditional %undef %exit %inner +%inner = OpLabel +OpLoopMerge %continue %inner None +OpBranchConditional %undef %inner %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Header block 3[%inner] is contained in the loop construct headed by " + "1[%loop], but its merge block 2[%continue] is not")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_id_test.cpp b/third_party/SPIRV-Tools/test/val/val_id_test.cpp index 327aef1..019d91a 100644 --- a/third_party/SPIRV-Tools/test/val/val_id_test.cpp +++ b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
@@ -749,20 +749,40 @@ // Signed or unsigned. enum Signed { kSigned, kUnsigned }; -// Creates an assembly snippet declaring OpTypeArray with the given length. -std::string MakeArrayLength(const std::string& len, Signed isSigned, - int width) { +// Creates an assembly module declaring OpTypeArray with the given length. +std::string MakeArrayLength(const std::string& len, Signed isSigned, int width, + int max_int_width = 64, + bool use_vulkan_memory_model = false) { std::ostringstream ss; ss << R"( OpCapability Shader - OpCapability Linkage - OpCapability Int16 - OpCapability Int64 )"; - ss << "OpMemoryModel Logical GLSL450\n"; + if (use_vulkan_memory_model) { + ss << " OpCapability VulkanMemoryModel\n"; + } + if (width == 16) { + ss << " OpCapability Int16\n"; + } + if (max_int_width > 32) { + ss << "\n OpCapability Int64\n"; + } + if (use_vulkan_memory_model) { + ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; + ss << "OpMemoryModel Logical Vulkan\n"; + } else { + ss << "OpMemoryModel Logical GLSL450\n"; + } + ss << "OpEntryPoint GLCompute %main \"main\"\n"; + ss << "OpExecutionMode %main LocalSize 1 1 1\n"; ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0"); ss << " %l = OpConstant %t " << len; ss << " %a = OpTypeArray %t %l"; + ss << " %void = OpTypeVoid \n" + " %voidfn = OpTypeFunction %void \n" + " %main = OpFunction %void None %voidfn \n" + " %entry = OpLabel\n" + " OpReturn\n" + " OpFunctionEnd\n"; return ss.str(); } @@ -772,7 +792,8 @@ : public spvtest::TextToBinaryTestBase<::testing::TestWithParam<int>> { protected: OpTypeArrayLengthTest() - : position_(spv_position_t{0, 0, 0}), + : env_(SPV_ENV_UNIVERSAL_1_0), + position_(spv_position_t{0, 0, 0}), diagnostic_(spvDiagnosticCreate(&position_, "")) {} ~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); } @@ -783,7 +804,7 @@ spvDiagnosticDestroy(diagnostic_); diagnostic_ = nullptr; const auto status = - spvValidate(ScopedContext().context, &cbinary, &diagnostic_); + spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_); if (status != SPV_SUCCESS) { spvDiagnosticPrint(diagnostic_); EXPECT_THAT(std::string(diagnostic_->error), @@ -792,12 +813,15 @@ return status; } + protected: + spv_target_env env_; + private: spv_position_t position_; // For creating diagnostic_. spv_diagnostic diagnostic_; }; -TEST_P(OpTypeArrayLengthTest, LengthPositive) { +TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) { const int width = GetParam(); EXPECT_EQ(SPV_SUCCESS, Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width)))); @@ -814,20 +838,19 @@ const std::string fpad(width / 4 - 1, 'F'); EXPECT_EQ( SPV_SUCCESS, - Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width)))); - EXPECT_EQ(SPV_SUCCESS, Val(CompileSuccessfully( - MakeArrayLength("0xF" + fpad, kUnsigned, width)))); + Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width)))) + << MakeArrayLength("0x7" + fpad, kSigned, width); } TEST_P(OpTypeArrayLengthTest, LengthZero) { const int width = GetParam(); EXPECT_EQ(SPV_ERROR_INVALID_ID, Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)), - "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at " + "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " "least 1.")); EXPECT_EQ(SPV_ERROR_INVALID_ID, Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)), - "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at " + "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " "least 1.")); } @@ -835,23 +858,88 @@ const int width = GetParam(); EXPECT_EQ(SPV_ERROR_INVALID_ID, Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)), - "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at " + "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " "least 1.")); EXPECT_EQ(SPV_ERROR_INVALID_ID, Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)), - "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at " + "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " "least 1.")); EXPECT_EQ(SPV_ERROR_INVALID_ID, Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)), - "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at " + "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " "least 1.")); const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0'); EXPECT_EQ(SPV_ERROR_INVALID_ID, Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)), - "OpTypeArray Length <id> '2\\[%.*\\]' default value must be at " + "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at " "least 1.")); } +// Returns the string form of an integer of the form 0x80....0 of the +// given bit width. +std::string big_num_ending_0(int bit_width) { + return "0x8" + std::string(bit_width / 4 - 1, '0'); +} + +// Returns the string form of an integer of the form 0x80..001 of the +// given bit width. +std::string big_num_ending_1(int bit_width) { + return "0x8" + std::string(bit_width / 4 - 2, '0') + "1"; +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) { + env_ = SPV_ENV_VULKAN_1_0; + const int width = GetParam(); + for (int max_int_width : {32, 64}) { + if (width > max_int_width) { + // Not valid to even make the OpConstant in this case. + continue; + } + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_0(width), kUnsigned, width, max_int_width)); + EXPECT_EQ(SPV_SUCCESS, Val(module)); + } +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) { + env_ = SPV_ENV_VULKAN_1_0; + const int width = GetParam(); + for (int max_int_width : {32, 64}) { + if (width > max_int_width) { + // Not valid to even make the OpConstant in this case. + continue; + } + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_1(width), kUnsigned, width, max_int_width)); + EXPECT_EQ(SPV_SUCCESS, Val(module)); + } +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) { + env_ = SPV_ENV_WEBGPU_0; + const int width = GetParam(); + // WebGPU only has 32 bit integers. + if (width != 32) return; + const int max_int_width = 32; + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_0(width), kUnsigned, width, max_int_width, true)); + EXPECT_EQ(SPV_SUCCESS, Val(module)); +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) { + env_ = SPV_ENV_WEBGPU_0; + const int width = GetParam(); + // WebGPU only has 32 bit integers. + if (width != 32) return; + const int max_int_width = 32; + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_1(width), kUnsigned, width, max_int_width, true)); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(module, + "OpTypeArray Length <id> '3\\[%.*\\]' size exceeds max value " + "2147483648 permitted by WebGPU: got 2147483649")); +} + // The only valid widths for integers are 8, 16, 32, and 64. // Since the Int8 capability requires the Kernel capability, and the Kernel // capability prohibits usage of signed integers, we can skip 8-bit integers
diff --git a/third_party/SPIRV-Tools/test/val/val_non_semantic_test.cpp b/third_party/SPIRV-Tools/test/val/val_non_semantic_test.cpp new file mode 100644 index 0000000..b80bb1a --- /dev/null +++ b/third_party/SPIRV-Tools/test/val/val_non_semantic_test.cpp
@@ -0,0 +1,195 @@ +// Copyright (c) 2019 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. + +// Validation tests for non-semantic instructions + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +struct TestResult { + TestResult(spv_result_t in_validation_result = SPV_SUCCESS, + const char* in_error_str = nullptr, + const char* in_error_str2 = nullptr) + : validation_result(in_validation_result), + error_str(in_error_str), + error_str2(in_error_str2) {} + spv_result_t validation_result; + const char* error_str; + const char* error_str2; +}; + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateNonSemanticGenerated = spvtest::ValidateBase< + std::tuple<bool, bool, const char*, const char*, TestResult>>; +using ValidateNonSemanticString = spvtest::ValidateBase<bool>; + +CodeGenerator GetNonSemanticCodeGenerator(const bool declare_ext, + const bool declare_extinst, + const char* const global_extinsts, + const char* const function_extinsts) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + + if (declare_ext) { + generator.extensions_ += "OpExtension \"SPV_KHR_non_semantic_info\"\n"; + } + if (declare_extinst) { + generator.extensions_ += + "%extinst = OpExtInstImport \"NonSemantic.Testing.Set\"\n"; + } + + generator.after_types_ = global_extinsts; + + generator.before_types_ = "%decorate_group = OpDecorationGroup"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Vertex"; + + entry_point.body = R"( +)"; + entry_point.body += function_extinsts; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_P(ValidateNonSemanticGenerated, InTest) { + const bool declare_ext = std::get<0>(GetParam()); + const bool declare_extinst = std::get<1>(GetParam()); + const char* const global_extinsts = std::get<2>(GetParam()); + const char* const function_extinsts = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = GetNonSemanticCodeGenerator( + declare_ext, declare_extinst, global_extinsts, function_extinsts); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), + testing::ContainsRegex(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), + testing::ContainsRegex(test_result.error_str2)); + } +} + +INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated, + Combine(Values(true), Values(false), Values(""), + Values(""), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + MissingOpExtension, ValidateNonSemanticGenerated, + Combine(Values(false), Values(true), Values(""), Values(""), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "NonSemantic extended instruction sets cannot be declared " + "without SPV_KHR_non_semantic_info.")))); + +INSTANTIATE_TEST_SUITE_P(NoExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values(""), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SimpleGlobalExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %i32"), Values(""), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ComplexGlobalExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %i32 %u32_2 " + "%f32vec4_1234 %u32_0"), + Values(""), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SimpleFunctionLevelExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values("%result = OpExtInst %void %extinst 123 %i32"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FunctionTypeReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %func"), Values(""), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + EntryPointReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values("%result = OpExtInst %void %extinst 123 %main"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + DecorationGroupReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values("%result = OpExtInst %void %extinst 123 %decorate_group"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + UnknownIDReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %undefined_id"), + Values(""), + Values(TestResult(SPV_ERROR_INVALID_ID, + "ID .* has not been defined")))); + +INSTANTIATE_TEST_SUITE_P( + NonSemanticUseInSemantic, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %f32 %extinst 123 %i32\n" + "%invalid = OpConstantComposite %f32vec2 %f32_0 %result"), + Values(""), + Values(TestResult(SPV_ERROR_INVALID_ID, + "in semantic instruction cannot be a " + "non-semantic instruction")))); + +TEST_F(ValidateNonSemanticString, InvalidSectionOpExtInst) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +%test = OpExtInst %void %extinst 4 %void +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + + // there's no specific error for using an OpExtInst too early, it requires a + // type so by definition any use of a type in it will be an undefined ID + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 2[%2] has not been defined")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp b/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp index 8f62555..e81fc7c 100644 --- a/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp +++ b/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
@@ -54,117 +54,170 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed")); } -TEST_F(ValidateWebGPU, OpNameIsDisallowed) { +TEST_F(ValidateWebGPU, OpNameIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpName %1 "foo" -%1 = OpTypeFloat 32 + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpName %1 "foo" + %1 = OpTypeFloat 32 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpName %foo \"foo\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) { +TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpMemberName %2 0 "foo" -%1 = OpTypeFloat 32 -%2 = OpTypeStruct %1 + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpMemberName %2 0 "foo" + %1 = OpTypeFloat 32 + %2 = OpTypeStruct %1 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd + )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpMemberName %_struct_1 0 " - "\"foo\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateWebGPU, OpSourceIsDisallowed) { +TEST_F(ValidateWebGPU, OpSourceIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpSource GLSL 450 + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSource GLSL 450 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpSource GLSL 450\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -// OpSourceContinued does not have a test case, because it requires being -// preceded by OpSource, which will cause a validation error. - -TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) { +TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpSourceExtension "bar" + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSource GLSL 450 + OpSourceContinued "I am a happy shader! Yay! ;" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpSourceExtension " - "\"bar\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateWebGPU, OpStringIsDisallowed) { +TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR -%1 = OpString "foo" + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSourceExtension "bar" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n %1 = OpString \"foo\"\n")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -// OpLine does not have a test case, because it requires being preceded by -// OpString, which will cause a validation error. - -TEST_F(ValidateWebGPU, OpNoLineDisallowed) { +TEST_F(ValidateWebGPU, OpStringIsAllowed) { std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpNoLine + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + %1 = OpString "foo" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd )"; CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Debugging instructions are not allowed in the WebGPU " - "execution environment.\n OpNoLine\n")); +TEST_F(ValidateWebGPU, OpLineIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + %1 = OpString "minimal.vert" + OpLine %1 1 1 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpNoLineIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpNoLine + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) { @@ -183,7 +236,6 @@ )"; CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); }
diff --git a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp index 5d582f5..cb98914 100644 --- a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp +++ b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
@@ -17,6 +17,7 @@ #include <cstring> #include <fstream> #include <functional> +#include <random> #include <sstream> #include <string> @@ -106,6 +107,10 @@ facts to make the guard non-obviously false. This option is a helper for massaging crash-inducing tests into a runnable format; it does not perform any fuzzing. + --fuzzer-pass-validation + Run the validator after applying each fuzzer pass during + fuzzing. Aborts fuzzing early if an invalid binary is created. + Useful for debugging spirv-fuzz. --replay File from which to read a sequence of transformations to replay (instead of fuzzing) @@ -179,6 +184,9 @@ } else if (0 == strncmp(cur_arg, "--force-render-red", sizeof("--force-render-red") - 1)) { force_render_red = true; + } else if (0 == strncmp(cur_arg, "--fuzzer-pass-validation", + sizeof("--fuzzer-pass-validation") - 1)) { + fuzzer_options->enable_fuzzer_pass_validation(); } else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) { const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); *replay_transformations_file = std::string(split_flag.second); @@ -397,16 +405,21 @@ } bool Fuzz(const spv_target_env& target_env, - const spvtools::FuzzerOptions& fuzzer_options, + spv_const_fuzzer_options fuzzer_options, const std::vector<uint32_t>& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, std::vector<uint32_t>* binary_out, spvtools::fuzz::protobufs::TransformationSequence* transformations_applied) { - spvtools::fuzz::Fuzzer fuzzer(target_env); + spvtools::fuzz::Fuzzer fuzzer( + target_env, + fuzzer_options->has_random_seed + ? fuzzer_options->random_seed + : static_cast<uint32_t>(std::random_device()()), + fuzzer_options->fuzzer_pass_validation_enabled); fuzzer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); - auto fuzz_result_status = fuzzer.Run(binary_in, initial_facts, fuzzer_options, - binary_out, transformations_applied); + auto fuzz_result_status = + fuzzer.Run(binary_in, initial_facts, binary_out, transformations_applied); if (fuzz_result_status != spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
diff --git a/third_party/SPIRV-Tools/tools/opt/opt.cpp b/third_party/SPIRV-Tools/tools/opt/opt.cpp index 4364e3e..0ff937a 100644 --- a/third_party/SPIRV-Tools/tools/opt/opt.cpp +++ b/third_party/SPIRV-Tools/tools/opt/opt.cpp
@@ -467,7 +467,8 @@ printf(R"( --strip-reflect Remove all reflection information. For now, this covers - reflection information defined by SPV_GOOGLE_hlsl_functionality1.)"); + reflection information defined by SPV_GOOGLE_hlsl_functionality1 + and SPV_KHR_non_semantic_info)"); printf(R"( --target-env=<env> Set the target environment. Without this flag the target
diff --git a/third_party/SPIRV-Tools/utils/check_copyright.py b/third_party/SPIRV-Tools/utils/check_copyright.py index cfeef80..969371d 100755 --- a/third_party/SPIRV-Tools/utils/check_copyright.py +++ b/third_party/SPIRV-Tools/utils/check_copyright.py
@@ -34,7 +34,7 @@ 'Samsung Inc'] CURRENT_YEAR='2019' -YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018|2019)' +YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019)' COPYRIGHT_RE = re.compile( 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
diff --git a/third_party/SPIRV-Tools/utils/generate_grammar_tables.py b/third_party/SPIRV-Tools/utils/generate_grammar_tables.py index ed24bd0..06fcf94 100755 --- a/third_party/SPIRV-Tools/utils/generate_grammar_tables.py +++ b/third_party/SPIRV-Tools/utils/generate_grammar_tables.py
@@ -30,6 +30,7 @@ SPV_AMD_gpu_shader_half_float SPV_AMD_gpu_shader_int16 SPV_AMD_shader_trinary_minmax +SPV_KHR_non_semantic_info """ @@ -359,14 +360,22 @@ return '{}\n\n{}\n\n{}'.format(caps_arrays, exts_arrays, '\n'.join(insts)) -def generate_extended_instruction_table(inst_table, set_name): +def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""): """Returns the info table containing all SPIR-V extended instructions, sorted by opcode, and prefixed by capability arrays. Arguments: - inst_table: a list containing all SPIR-V instructions. - set_name: the name of the extended instruction set. + - operand_kind_prefix: the prefix, if any, to add to the front + of operand kind names. """ + if operand_kind_prefix: + prefix_operand_kind_names(operand_kind_prefix, json_grammar) + + inst_table = json_grammar["instructions"] + set_name = set_name.replace(".", "_") + inst_table = sorted(inst_table, key=lambda k: k['opcode']) caps = [inst.get('capabilities', []) for inst in inst_table] caps_arrays = generate_capability_arrays(caps) @@ -451,6 +460,7 @@ def generate_enum_operand_kind(enum, synthetic_exts_list): """Returns the C definition for the given operand kind. + It's a static const named array of spv_operand_desc_t. Also appends to |synthetic_exts_list| a list of extension lists used. @@ -680,6 +690,26 @@ return operand_kinds +def prefix_operand_kind_names(prefix, json_dict): + """Modifies json_dict, by prefixing all the operand kind names + with the given prefix. Also modifies their uses in the instructions + to match. + """ + + old_to_new = {} + for operand_kind in json_dict["operand_kinds"]: + old_name = operand_kind["kind"] + new_name = prefix + old_name + operand_kind["kind"] = new_name + old_to_new[old_name] = new_name + + for instruction in json_dict["instructions"]: + for operand in instruction.get("operands", []): + replacement = old_to_new.get(operand["kind"]) + if replacement is not None: + operand["kind"] = replacement + + def main(): import argparse parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') @@ -692,6 +722,10 @@ type=str, required=False, default=None, help='input JSON grammar file for DebugInfo extended ' 'instruction set') + parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>', + type=str, required=False, default=None, + help='input JSON grammar file for OpenCL.DebugInfo.100 ' + 'extended instruction set') parser.add_argument('--extinst-glsl-grammar', metavar='<path>', type=str, required=False, default=None, help='input JSON grammar file for GLSL extended ' @@ -726,6 +760,9 @@ parser.add_argument('--vendor-insts-output', metavar='<path>', type=str, required=False, default=None, help='output file for vendor extended instruction set') + parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>', + type=str, required=False, default=None, + help='prefix for operand kinds (to disambiguate operand type enums)') args = parser.parse_args() if (args.core_insts_output is None) != \ @@ -733,9 +770,12 @@ print('error: --core-insts-output and --operand-kinds-output ' 'should be specified together.') exit(1) - if args.operand_kinds_output and not (args.spirv_core_grammar and args.extinst_debuginfo_grammar): + if args.operand_kinds_output and not (args.spirv_core_grammar and + args.extinst_debuginfo_grammar and + args.extinst_cldebuginfo100_grammar): print('error: --operand-kinds-output requires --spirv-core-grammar ' - 'and --exinst-debuginfo-grammar') + 'and --exinst-debuginfo-grammar ' + 'and --exinst-cldebuginfo100-grammar') exit(1) if (args.glsl_insts_output is None) != \ (args.extinst_glsl_grammar is None): @@ -766,14 +806,19 @@ core_grammar = json.loads(json_file.read()) with open(args.extinst_debuginfo_grammar) as debuginfo_json_file: debuginfo_grammar = json.loads(debuginfo_json_file.read()) - instructions = [] - instructions.extend(core_grammar['instructions']) - instructions.extend(debuginfo_grammar['instructions']) - operand_kinds = [] - operand_kinds.extend(core_grammar['operand_kinds']) - operand_kinds.extend(debuginfo_grammar['operand_kinds']) - extensions = get_extension_list(instructions, operand_kinds) - operand_kinds = precondition_operand_kinds(operand_kinds) + with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file: + cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read()) + prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar) + instructions = [] + instructions.extend(core_grammar['instructions']) + instructions.extend(debuginfo_grammar['instructions']) + instructions.extend(cldebuginfo100_grammar['instructions']) + operand_kinds = [] + operand_kinds.extend(core_grammar['operand_kinds']) + operand_kinds.extend(debuginfo_grammar['operand_kinds']) + operand_kinds.extend(cldebuginfo100_grammar['operand_kinds']) + extensions = get_extension_list(instructions, operand_kinds) + operand_kinds = precondition_operand_kinds(operand_kinds) if args.core_insts_output is not None: make_path_to_file(args.core_insts_output) make_path_to_file(args.operand_kinds_output) @@ -798,7 +843,7 @@ make_path_to_file(args.glsl_insts_output) with open(args.glsl_insts_output, 'w') as f: f.write(generate_extended_instruction_table( - grammar['instructions'], 'glsl')) + grammar, 'glsl')) if args.extinst_opencl_grammar is not None: with open(args.extinst_opencl_grammar) as json_file: @@ -806,7 +851,7 @@ make_path_to_file(args.opencl_insts_output) with open(args.opencl_insts_output, 'w') as f: f.write(generate_extended_instruction_table( - grammar['instructions'], 'opencl')) + grammar, 'opencl')) if args.extinst_vendor_grammar is not None: with open(args.extinst_vendor_grammar) as json_file: @@ -817,7 +862,7 @@ name = name[start:-len('.grammar.json')].replace('-', '_') with open(args.vendor_insts_output, 'w') as f: f.write(generate_extended_instruction_table( - grammar['instructions'], name)) + grammar, name, args.vendor_operand_kind_prefix)) if __name__ == '__main__':
diff --git a/third_party/SPIRV-Tools/utils/vscode/README.md b/third_party/SPIRV-Tools/utils/vscode/README.md index afbb246..bc02211 100644 --- a/third_party/SPIRV-Tools/utils/vscode/README.md +++ b/third_party/SPIRV-Tools/utils/vscode/README.md
@@ -1,7 +1,17 @@ # Visual Studio Code extension for SPIR-V disassembly files -This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V disassembly files (.spirv) +This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V assembly files (`.spvasm`) + +## 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/) ## Installing (macOS / Linux) -Simply run `install.sh` +Run `install.sh` + +## Installing (Windows) + +Run `install.bat`
diff --git a/third_party/SPIRV-Tools/utils/vscode/install.bat b/third_party/SPIRV-Tools/utils/vscode/install.bat new file mode 100644 index 0000000..21a52ec --- /dev/null +++ b/third_party/SPIRV-Tools/utils/vscode/install.bat
@@ -0,0 +1,30 @@ +@REM Copyright (c) 2019 Google Inc. +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. + +@set EXT_PATH=%userprofile%\.vscode\extensions\google.spirvls-0.0.1 +@set ROOT_PATH=%~dp0 + +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 + +if not exist %EXT_PATH% mkdir -p %EXT_PATH% +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 + +@pushd %EXT_PATH% +call npm install +@popd
diff --git a/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go b/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go index f9980b9..42cbbe9 100644 --- a/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go +++ b/third_party/SPIRV-Tools/utils/vscode/src/tools/gen-grammar.go
@@ -77,9 +77,12 @@ } return rel } + escape := func(str string) string { + return strings.ReplaceAll(str, `\`, `/`) + } args := []string{ - "--template=" + relPath(*templatePath), - "--out=" + relPath(*outputPath), + "--template=" + escape(relPath(*templatePath)), + "--out=" + escape(relPath(*outputPath)), } return "gen-grammar.go " + strings.Join(args, " ") },