diff --git a/third_party/spirv-tools/.appveyor.yml b/third_party/spirv-tools/.appveyor.yml deleted file mode 100644 index 0a4cca05dc..0000000000 --- a/third_party/spirv-tools/.appveyor.yml +++ /dev/null @@ -1,90 +0,0 @@ -# Windows Build Configuration for AppVeyor -# http://www.appveyor.com/docs/appveyor-yml - -# version format -version: "{build}" - -# The most recent compiler gives the most interesting new results. -# Put it first so we get its feedback first. -os: - - Visual Studio 2017 - #- Visual Studio 2013 - -platform: - - x64 - -configuration: - - Debug - #- Release - -branches: - only: - - master - -# Travis advances the master-tot tag to current top of the tree after -# each push into the master branch, because it relies on that tag to -# upload build artifacts to the master-tot release. This will cause -# double testing for each push on Appveyor: one for the push, one for -# the tag advance. Disable testing tags. -skip_tags: true - -clone_depth: 1 - -matrix: - fast_finish: true # Show final status immediately if a test fails. - #exclude: - # - os: Visual Studio 2013 - # configuration: Debug - -# scripts that run after cloning repository -install: - # Install ninja - - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip" - - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip - - 7z x ninja.zip -oC:\ninja > nul - - set PATH=C:\ninja;C:\Python36;%PATH% - -before_build: - - git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers - - git clone https://github.com/google/googletest.git external/googletest - - cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. - - git clone --depth=1 https://github.com/google/effcee.git external/effcee - - git clone --depth=1 https://github.com/google/re2.git external/re2 - # Set path and environment variables for the current Visual Studio version - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64) - - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64) - -build: - parallel: true # enable MSBuild parallel builds - verbosity: minimal - -build_script: - - mkdir build && cd build - - cmake -GNinja -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF .. - - ninja install - -test_script: - - ctest -C %CONFIGURATION% --output-on-failure --timeout 310 - -after_test: - # Zip build artifacts for uploading and deploying - - cd install - - 7z a SPIRV-Tools-master-windows-"%PLATFORM%"-"%CONFIGURATION%".zip *\* - -artifacts: - - path: build\install\*.zip - name: artifacts-zip - -deploy: - - provider: GitHub - auth_token: - secure: TMfcScKzzFIm1YgeV/PwCRXFDCw8Xm0wY2Vb2FU6WKlbzb5eUITTpr6I5vHPnAxS - release: master-tot - description: "Continuous build of the latest master branch by Appveyor and Travis CI" - artifact: artifacts-zip - draft: false - prerelease: false - force_update: true - on: - branch: master - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 diff --git a/third_party/spirv-tools/.github/workflows/wasm.yml b/third_party/spirv-tools/.github/workflows/wasm.yml new file mode 100644 index 0000000000..d9a9c5cb7c --- /dev/null +++ b/third_party/spirv-tools/.github/workflows/wasm.yml @@ -0,0 +1,14 @@ +name: Wasm Build + +on: [ push, pull_request ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build web + run: docker-compose up + - name: Run tests + run: node test/wasm/test.js diff --git a/third_party/spirv-tools/.gitignore b/third_party/spirv-tools/.gitignore index b2af56e92b..22fb106d62 100644 --- a/third_party/spirv-tools/.gitignore +++ b/third_party/spirv-tools/.gitignore @@ -5,8 +5,6 @@ compile_commands.json /build/ /buildtools/ /external/googletest -/external/SPIRV-Headers -/external/spirv-headers /external/effcee /external/re2 /external/protobuf diff --git a/third_party/spirv-tools/Android.mk b/third_party/spirv-tools/Android.mk index 1bc844d400..bc748e535b 100644 --- a/third_party/spirv-tools/Android.mk +++ b/third_party/spirv-tools/Android.mk @@ -88,8 +88,11 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/composite.cpp \ source/opt/const_folding_rules.cpp \ source/opt/constants.cpp \ + source/opt/control_dependence.cpp \ + source/opt/convert_to_sampled_image_pass.cpp \ source/opt/convert_to_half_pass.cpp \ source/opt/copy_prop_arrays.cpp \ + source/opt/dataflow.cpp \ source/opt/dead_branch_elim_pass.cpp \ source/opt/dead_insert_elim_pass.cpp \ source/opt/dead_variable_elimination.cpp \ @@ -97,6 +100,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/debug_info_manager.cpp \ source/opt/def_use_manager.cpp \ source/opt/desc_sroa.cpp \ + source/opt/desc_sroa_util.cpp \ source/opt/dominator_analysis.cpp \ source/opt/dominator_tree.cpp \ source/opt/eliminate_dead_constant_pass.cpp \ @@ -154,6 +158,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/relax_float_ops_pass.cpp \ source/opt/remove_duplicates_pass.cpp \ source/opt/remove_unused_interface_variables_pass.cpp \ + source/opt/replace_desc_array_access_using_var_index.cpp \ source/opt/replace_invalid_opc.cpp \ source/opt/scalar_analysis.cpp \ source/opt/scalar_analysis_simplification.cpp \ @@ -181,6 +186,7 @@ SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.st SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json +SPV_VKDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json define gen_spvtools_grammar_tables $(call generate-file-dir,$(1)/core.insts-unified1.inc) @@ -212,6 +218,7 @@ $(LOCAL_PATH)/source/ext_inst.cpp: \ $(1)/opencl.std.insts.inc \ $(1)/debuginfo.insts.inc \ $(1)/opencl.debuginfo.100.insts.inc \ + $(1)/nonsemantic.shader.debuginfo.100.insts.inc \ $(1)/spv-amd-gcn-shader.insts.inc \ $(1)/spv-amd-shader-ballot.insts.inc \ $(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \ @@ -241,6 +248,7 @@ endef # We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100 $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR))) $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR))) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR))) define gen_spvtools_vendor_tables @@ -258,6 +266,7 @@ endef # Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree. $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.shader.debuginfo.100,"SHDEBUG100_")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,"")) diff --git a/third_party/spirv-tools/BUILD.bazel b/third_party/spirv-tools/BUILD.bazel index 52290cfb34..b2031dede2 100644 --- a/third_party/spirv-tools/BUILD.bazel +++ b/third_party/spirv-tools/BUILD.bazel @@ -3,6 +3,7 @@ load( "COMMON_COPTS", "DEBUGINFO_GRAMMAR_JSON_FILE", "CLDEBUGINFO100_GRAMMAR_JSON_FILE", + "SHDEBUGINFO100_GRAMMAR_JSON_FILE", "TEST_COPTS", "base_test", "generate_core_tables", @@ -12,6 +13,7 @@ load( "generate_opencl_tables", "generate_vendor_tables", "link_test", + "lint_test", "opt_test", "reduce_test", "util_test", @@ -59,12 +61,16 @@ generate_vendor_tables("debuginfo") generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_") +generate_vendor_tables("nonsemantic.shader.debuginfo.100", "SHDEBUG100_") + generate_vendor_tables("nonsemantic.clspvreflection") generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) +generate_extinst_lang_headers("NonSemanticShaderDebugInfo100", SHDEBUGINFO100_GRAMMAR_JSON_FILE) + py_binary( name = "generate_registry_tables", srcs = ["utils/generate_registry_tables.py"], @@ -101,12 +107,14 @@ cc_library( ":gen_enum_string_mapping", ":gen_extinst_lang_headers_DebugInfo", ":gen_extinst_lang_headers_OpenCLDebugInfo100", + ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", ":gen_registry_tables", ":gen_vendor_tables_debuginfo", ":gen_vendor_tables_nonsemantic_clspvreflection", ":gen_vendor_tables_opencl_debuginfo_100", + ":gen_vendor_tables_nonsemantic_shader_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", @@ -225,6 +233,19 @@ cc_library( ], ) +cc_library( + name = "spirv_tools_lint", + srcs = glob(["source/lint/*.cpp", "source/lint/*.h"]), + hdrs = ["include/spirv-tools/linter.hpp"], + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ], +) + cc_library( name = "tools_util", srcs = glob(["tools/util/*.cpp"]), @@ -322,6 +343,21 @@ cc_binary( ], ) +cc_binary( + name = "spirv-lint", + srcs = [ + "tools/io.h", + "tools/lint/lint.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_lint", + ":tools_util", + ], +) + cc_binary( name = "spirv-cfg", srcs = [ @@ -465,6 +501,13 @@ base_test( ["test/link/*.cpp"], )] +[lint_test( + name = f[10:-4], # strip test/lint/, .cpp + srcs = [f], +) for f in glob( + ["test/lint/*.cpp"], +)] + [opt_test( name = f[9:-4], # strip test/opt/, .cpp srcs = [f], diff --git a/third_party/spirv-tools/BUILD.gn b/third_party/spirv-tools/BUILD.gn index 996905a2d7..309d51373b 100644 --- a/third_party/spirv-tools/BUILD.gn +++ b/third_party/spirv-tools/BUILD.gn @@ -16,6 +16,7 @@ import("//build_overrides/build.gni") import("//build_overrides/spirv_tools.gni") if (build_with_chromium) { import("//testing/test.gni") + import("//third_party/protobuf/proto_library.gni") } spirv_headers = spirv_tools_spirv_headers_dir @@ -275,6 +276,10 @@ spvtools_language_header("cldebuginfo100") { name = "OpenCLDebugInfo100" grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" } +spvtools_language_header("vkdebuginfo100") { + name = "NonSemanticShaderDebugInfo100" + grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json" +} spvtools_vendor_tables = [ [ @@ -305,6 +310,10 @@ spvtools_vendor_tables = [ "nonsemantic.clspvreflection", "...nil...", ], + [ + "nonsemantic.shader.debuginfo.100", + "SHDEBUG100_", + ], ] foreach(table_def, spvtools_vendor_tables) { @@ -318,20 +327,28 @@ config("spvtools_public_config") { include_dirs = [ "include" ] } +config("spvtools_include_gen_dirs") { + include_dirs = [ "$target_gen_dir" ] +} + config("spvtools_internal_config") { include_dirs = [ ".", - "$target_gen_dir", "${spirv_headers}/include", ] - configs = [ ":spvtools_public_config" ] + configs = [ + ":spvtools_public_config", + ":spvtools_include_gen_dirs", + ] cflags = [] if (is_clang) { cflags += [ "-Wno-implicit-fallthrough", "-Wno-newline-eof", + "-Wno-unreachable-code-break", + "-Wno-unreachable-code-return", ] } else if (!is_win) { # Work around a false-positive on a Skia GCC 10 builder. @@ -351,6 +368,14 @@ source_set("spvtools_headers") { public_configs = [ ":spvtools_public_config" ] } +group("spvtools_language_headers") { + public_deps = [ + ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", + ] +} + static_library("spvtools") { deps = [ ":spvtools_core_tables_unified1", @@ -358,6 +383,7 @@ static_library("spvtools") { ":spvtools_glsl_tables_glsl1-0", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_opencl_tables_opencl1-0", ] foreach(table_def, spvtools_vendor_tables) { @@ -371,6 +397,7 @@ static_library("spvtools") { "source/binary.cpp", "source/binary.h", "source/cfa.h", + "source/common_debug_info.h", "source/diagnostic.cpp", "source/diagnostic.h", "source/disassemble.cpp", @@ -402,8 +429,12 @@ static_library("spvtools") { "source/spirv_definition.h", "source/spirv_endian.cpp", "source/spirv_endian.h", + "source/spirv_fuzzer_options.cpp", + "source/spirv_fuzzer_options.h", "source/spirv_optimizer_options.cpp", "source/spirv_optimizer_options.h", + "source/spirv_reducer_options.cpp", + "source/spirv_reducer_options.h", "source/spirv_target_env.cpp", "source/spirv_target_env.h", "source/spirv_validator_options.cpp", @@ -499,6 +530,7 @@ static_library("spvtools_val") { ":spvtools", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ] public_deps = [ ":spvtools_headers" ] @@ -541,10 +573,16 @@ static_library("spvtools_opt") { "source/opt/const_folding_rules.h", "source/opt/constants.cpp", "source/opt/constants.h", + "source/opt/control_dependence.cpp", + "source/opt/control_dependence.h", "source/opt/convert_to_half_pass.cpp", "source/opt/convert_to_half_pass.h", + "source/opt/convert_to_sampled_image_pass.cpp", + "source/opt/convert_to_sampled_image_pass.h", "source/opt/copy_prop_arrays.cpp", "source/opt/copy_prop_arrays.h", + "source/opt/dataflow.cpp", + "source/opt/dataflow.h", "source/opt/dead_branch_elim_pass.cpp", "source/opt/dead_branch_elim_pass.h", "source/opt/dead_insert_elim_pass.cpp", @@ -559,6 +597,8 @@ static_library("spvtools_opt") { "source/opt/def_use_manager.h", "source/opt/desc_sroa.cpp", "source/opt/desc_sroa.h", + "source/opt/desc_sroa_util.cpp", + "source/opt/desc_sroa_util.h", "source/opt/dominator_analysis.cpp", "source/opt/dominator_analysis.h", "source/opt/dominator_tree.cpp", @@ -678,6 +718,8 @@ static_library("spvtools_opt") { "source/opt/remove_duplicates_pass.h", "source/opt/remove_unused_interface_variables_pass.cpp", "source/opt/remove_unused_interface_variables_pass.h", + "source/opt/replace_desc_array_access_using_var_index.cpp", + "source/opt/replace_desc_array_access_using_var_index.h", "source/opt/replace_invalid_opc.cpp", "source/opt/replace_invalid_opc.h", "source/opt/scalar_analysis.cpp", @@ -721,11 +763,14 @@ static_library("spvtools_opt") { deps = [ ":spvtools", - ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", ":spvtools_vendor_tables_spv-amd-shader-ballot", ] - public_deps = [ ":spvtools_headers" ] + public_deps = [ + ":spvtools_headers", + ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_vkdebuginfo100", + ] if (build_with_chromium) { configs -= [ "//build/config/compiler:chromium_code" ] @@ -811,8 +856,6 @@ static_library("spvtools_reduce") { "source/reduce/structured_loop_to_selection_reduction_opportunity.h", "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp", "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h", - "source/spirv_reducer_options.cpp", - "source/spirv_reducer_options.h", ] deps = [ ":spvtools", @@ -826,6 +869,423 @@ static_library("spvtools_reduce") { configs += [ ":spvtools_internal_config" ] } +if (build_with_chromium) { + # The spirv-fuzz library is only built when in a Chromium checkout + # due to its dependency on protobuf. + + proto_library("spvtools_fuzz_proto") { + sources = [ "source/fuzz/protobufs/spvtoolsfuzz.proto" ] + generate_python = false + use_protobuf_full = true + } + + static_library("spvtools_fuzz") { + sources = [ + "source/fuzz/added_function_reducer.cpp", + "source/fuzz/added_function_reducer.h", + "source/fuzz/available_instructions.cpp", + "source/fuzz/available_instructions.h", + "source/fuzz/call_graph.cpp", + "source/fuzz/call_graph.h", + "source/fuzz/comparator_deep_blocks_first.h", + "source/fuzz/counter_overflow_id_source.cpp", + "source/fuzz/counter_overflow_id_source.h", + "source/fuzz/data_descriptor.cpp", + "source/fuzz/data_descriptor.h", + "source/fuzz/equivalence_relation.h", + "source/fuzz/fact_manager/constant_uniform_facts.cpp", + "source/fuzz/fact_manager/constant_uniform_facts.h", + "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp", + "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h", + "source/fuzz/fact_manager/dead_block_facts.cpp", + "source/fuzz/fact_manager/dead_block_facts.h", + "source/fuzz/fact_manager/fact_manager.cpp", + "source/fuzz/fact_manager/fact_manager.h", + "source/fuzz/fact_manager/irrelevant_value_facts.cpp", + "source/fuzz/fact_manager/irrelevant_value_facts.h", + "source/fuzz/fact_manager/livesafe_function_facts.cpp", + "source/fuzz/fact_manager/livesafe_function_facts.h", + "source/fuzz/force_render_red.cpp", + "source/fuzz/force_render_red.h", + "source/fuzz/fuzzer.cpp", + "source/fuzz/fuzzer.h", + "source/fuzz/fuzzer_context.cpp", + "source/fuzz/fuzzer_context.h", + "source/fuzz/fuzzer_pass.cpp", + "source/fuzz/fuzzer_pass.h", + "source/fuzz/fuzzer_pass_add_access_chains.cpp", + "source/fuzz/fuzzer_pass_add_access_chains.h", + "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h", + "source/fuzz/fuzzer_pass_add_composite_extract.cpp", + "source/fuzz/fuzzer_pass_add_composite_extract.h", + "source/fuzz/fuzzer_pass_add_composite_inserts.cpp", + "source/fuzz/fuzzer_pass_add_composite_inserts.h", + "source/fuzz/fuzzer_pass_add_composite_types.cpp", + "source/fuzz/fuzzer_pass_add_composite_types.h", + "source/fuzz/fuzzer_pass_add_copy_memory.cpp", + "source/fuzz/fuzzer_pass_add_copy_memory.h", + "source/fuzz/fuzzer_pass_add_dead_blocks.cpp", + "source/fuzz/fuzzer_pass_add_dead_blocks.h", + "source/fuzz/fuzzer_pass_add_dead_breaks.cpp", + "source/fuzz/fuzzer_pass_add_dead_breaks.h", + "source/fuzz/fuzzer_pass_add_dead_continues.cpp", + "source/fuzz/fuzzer_pass_add_dead_continues.h", + "source/fuzz/fuzzer_pass_add_equation_instructions.cpp", + "source/fuzz/fuzzer_pass_add_equation_instructions.h", + "source/fuzz/fuzzer_pass_add_function_calls.cpp", + "source/fuzz/fuzzer_pass_add_function_calls.h", + "source/fuzz/fuzzer_pass_add_global_variables.cpp", + "source/fuzz/fuzzer_pass_add_global_variables.h", + "source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp", + "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h", + "source/fuzz/fuzzer_pass_add_loads.cpp", + "source/fuzz/fuzzer_pass_add_loads.h", + "source/fuzz/fuzzer_pass_add_local_variables.cpp", + "source/fuzz/fuzzer_pass_add_local_variables.h", + "source/fuzz/fuzzer_pass_add_loop_preheaders.cpp", + "source/fuzz/fuzzer_pass_add_loop_preheaders.h", + "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h", + "source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp", + "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h", + "source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_opphi_synonyms.h", + "source/fuzz/fuzzer_pass_add_parameters.cpp", + "source/fuzz/fuzzer_pass_add_parameters.h", + "source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp", + "source/fuzz/fuzzer_pass_add_relaxed_decorations.h", + "source/fuzz/fuzzer_pass_add_stores.cpp", + "source/fuzz/fuzzer_pass_add_stores.h", + "source/fuzz/fuzzer_pass_add_synonyms.cpp", + "source/fuzz/fuzzer_pass_add_synonyms.h", + "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp", + "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h", + "source/fuzz/fuzzer_pass_adjust_branch_weights.cpp", + "source/fuzz/fuzzer_pass_adjust_branch_weights.h", + "source/fuzz/fuzzer_pass_adjust_function_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_function_controls.h", + "source/fuzz/fuzzer_pass_adjust_loop_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_loop_controls.h", + "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp", + "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h", + "source/fuzz/fuzzer_pass_adjust_selection_controls.cpp", + "source/fuzz/fuzzer_pass_adjust_selection_controls.h", + "source/fuzz/fuzzer_pass_apply_id_synonyms.cpp", + "source/fuzz/fuzzer_pass_apply_id_synonyms.h", + "source/fuzz/fuzzer_pass_construct_composites.cpp", + "source/fuzz/fuzzer_pass_construct_composites.h", + "source/fuzz/fuzzer_pass_copy_objects.cpp", + "source/fuzz/fuzzer_pass_copy_objects.h", + "source/fuzz/fuzzer_pass_donate_modules.cpp", + "source/fuzz/fuzzer_pass_donate_modules.h", + "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp", + "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h", + "source/fuzz/fuzzer_pass_expand_vector_reductions.cpp", + "source/fuzz/fuzzer_pass_expand_vector_reductions.h", + "source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp", + "source/fuzz/fuzzer_pass_flatten_conditional_branches.h", + "source/fuzz/fuzzer_pass_inline_functions.cpp", + "source/fuzz/fuzzer_pass_inline_functions.h", + "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp", + "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h", + "source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp", + "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h", + "source/fuzz/fuzzer_pass_invert_comparison_operators.cpp", + "source/fuzz/fuzzer_pass_invert_comparison_operators.h", + "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp", + "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h", + "source/fuzz/fuzzer_pass_merge_blocks.cpp", + "source/fuzz/fuzzer_pass_merge_blocks.h", + "source/fuzz/fuzzer_pass_merge_function_returns.cpp", + "source/fuzz/fuzzer_pass_merge_function_returns.h", + "source/fuzz/fuzzer_pass_mutate_pointers.cpp", + "source/fuzz/fuzzer_pass_mutate_pointers.h", + "source/fuzz/fuzzer_pass_obfuscate_constants.cpp", + "source/fuzz/fuzzer_pass_obfuscate_constants.h", + "source/fuzz/fuzzer_pass_outline_functions.cpp", + "source/fuzz/fuzzer_pass_outline_functions.h", + "source/fuzz/fuzzer_pass_permute_blocks.cpp", + "source/fuzz/fuzzer_pass_permute_blocks.h", + "source/fuzz/fuzzer_pass_permute_function_parameters.cpp", + "source/fuzz/fuzzer_pass_permute_function_parameters.h", + "source/fuzz/fuzzer_pass_permute_function_variables.cpp", + "source/fuzz/fuzzer_pass_permute_function_variables.h", + "source/fuzz/fuzzer_pass_permute_instructions.cpp", + "source/fuzz/fuzzer_pass_permute_instructions.h", + "source/fuzz/fuzzer_pass_permute_phi_operands.cpp", + "source/fuzz/fuzzer_pass_permute_phi_operands.h", + "source/fuzz/fuzzer_pass_propagate_instructions_down.cpp", + "source/fuzz/fuzzer_pass_propagate_instructions_down.h", + "source/fuzz/fuzzer_pass_propagate_instructions_up.cpp", + "source/fuzz/fuzzer_pass_propagate_instructions_up.h", + "source/fuzz/fuzzer_pass_push_ids_through_variables.cpp", + "source/fuzz/fuzzer_pass_push_ids_through_variables.h", + "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp", + "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h", + "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp", + "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h", + "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp", + "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h", + "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp", + "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h", + "source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp", + "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h", + "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp", + "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h", + "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp", + "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h", + "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp", + "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h", + "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp", + "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h", + "source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp", + "source/fuzz/fuzzer_pass_replace_parameter_with_global.h", + "source/fuzz/fuzzer_pass_replace_params_with_struct.cpp", + "source/fuzz/fuzzer_pass_replace_params_with_struct.h", + "source/fuzz/fuzzer_pass_split_blocks.cpp", + "source/fuzz/fuzzer_pass_split_blocks.h", + "source/fuzz/fuzzer_pass_swap_commutable_operands.cpp", + "source/fuzz/fuzzer_pass_swap_commutable_operands.h", + "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp", + "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h", + "source/fuzz/fuzzer_pass_swap_functions.cpp", + "source/fuzz/fuzzer_pass_swap_functions.h", + "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp", + "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h", + "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp", + "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h", + "source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp", + "source/fuzz/fuzzer_pass_wrap_vector_synonym.h", + "source/fuzz/fuzzer_util.cpp", + "source/fuzz/fuzzer_util.h", + "source/fuzz/id_use_descriptor.cpp", + "source/fuzz/id_use_descriptor.h", + "source/fuzz/instruction_descriptor.cpp", + "source/fuzz/instruction_descriptor.h", + "source/fuzz/instruction_message.cpp", + "source/fuzz/instruction_message.h", + "source/fuzz/overflow_id_source.cpp", + "source/fuzz/overflow_id_source.h", + "source/fuzz/pass_management/repeated_pass_instances.h", + "source/fuzz/pass_management/repeated_pass_manager.cpp", + "source/fuzz/pass_management/repeated_pass_manager.h", + "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp", + "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h", + "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp", + "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h", + "source/fuzz/pass_management/repeated_pass_manager_simple.cpp", + "source/fuzz/pass_management/repeated_pass_manager_simple.h", + "source/fuzz/pass_management/repeated_pass_recommender.cpp", + "source/fuzz/pass_management/repeated_pass_recommender.h", + "source/fuzz/pass_management/repeated_pass_recommender_standard.cpp", + "source/fuzz/pass_management/repeated_pass_recommender_standard.h", + "source/fuzz/protobufs/spirvfuzz_protobufs.h", + "source/fuzz/pseudo_random_generator.cpp", + "source/fuzz/pseudo_random_generator.h", + "source/fuzz/random_generator.cpp", + "source/fuzz/random_generator.h", + "source/fuzz/replayer.cpp", + "source/fuzz/replayer.h", + "source/fuzz/shrinker.cpp", + "source/fuzz/shrinker.h", + "source/fuzz/transformation.cpp", + "source/fuzz/transformation.h", + "source/fuzz/transformation_access_chain.cpp", + "source/fuzz/transformation_access_chain.h", + "source/fuzz/transformation_add_bit_instruction_synonym.cpp", + "source/fuzz/transformation_add_bit_instruction_synonym.h", + "source/fuzz/transformation_add_constant_boolean.cpp", + "source/fuzz/transformation_add_constant_boolean.h", + "source/fuzz/transformation_add_constant_composite.cpp", + "source/fuzz/transformation_add_constant_composite.h", + "source/fuzz/transformation_add_constant_null.cpp", + "source/fuzz/transformation_add_constant_null.h", + "source/fuzz/transformation_add_constant_scalar.cpp", + "source/fuzz/transformation_add_constant_scalar.h", + "source/fuzz/transformation_add_copy_memory.cpp", + "source/fuzz/transformation_add_copy_memory.h", + "source/fuzz/transformation_add_dead_block.cpp", + "source/fuzz/transformation_add_dead_block.h", + "source/fuzz/transformation_add_dead_break.cpp", + "source/fuzz/transformation_add_dead_break.h", + "source/fuzz/transformation_add_dead_continue.cpp", + "source/fuzz/transformation_add_dead_continue.h", + "source/fuzz/transformation_add_early_terminator_wrapper.cpp", + "source/fuzz/transformation_add_early_terminator_wrapper.h", + "source/fuzz/transformation_add_function.cpp", + "source/fuzz/transformation_add_function.h", + "source/fuzz/transformation_add_global_undef.cpp", + "source/fuzz/transformation_add_global_undef.h", + "source/fuzz/transformation_add_global_variable.cpp", + "source/fuzz/transformation_add_global_variable.h", + "source/fuzz/transformation_add_image_sample_unused_components.cpp", + "source/fuzz/transformation_add_image_sample_unused_components.h", + "source/fuzz/transformation_add_local_variable.cpp", + "source/fuzz/transformation_add_local_variable.h", + "source/fuzz/transformation_add_loop_preheader.cpp", + "source/fuzz/transformation_add_loop_preheader.h", + "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp", + "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h", + "source/fuzz/transformation_add_no_contraction_decoration.cpp", + "source/fuzz/transformation_add_no_contraction_decoration.h", + "source/fuzz/transformation_add_opphi_synonym.cpp", + "source/fuzz/transformation_add_opphi_synonym.h", + "source/fuzz/transformation_add_parameter.cpp", + "source/fuzz/transformation_add_parameter.h", + "source/fuzz/transformation_add_relaxed_decoration.cpp", + "source/fuzz/transformation_add_relaxed_decoration.h", + "source/fuzz/transformation_add_spec_constant_op.cpp", + "source/fuzz/transformation_add_spec_constant_op.h", + "source/fuzz/transformation_add_synonym.cpp", + "source/fuzz/transformation_add_synonym.h", + "source/fuzz/transformation_add_type_array.cpp", + "source/fuzz/transformation_add_type_array.h", + "source/fuzz/transformation_add_type_boolean.cpp", + "source/fuzz/transformation_add_type_boolean.h", + "source/fuzz/transformation_add_type_float.cpp", + "source/fuzz/transformation_add_type_float.h", + "source/fuzz/transformation_add_type_function.cpp", + "source/fuzz/transformation_add_type_function.h", + "source/fuzz/transformation_add_type_int.cpp", + "source/fuzz/transformation_add_type_int.h", + "source/fuzz/transformation_add_type_matrix.cpp", + "source/fuzz/transformation_add_type_matrix.h", + "source/fuzz/transformation_add_type_pointer.cpp", + "source/fuzz/transformation_add_type_pointer.h", + "source/fuzz/transformation_add_type_struct.cpp", + "source/fuzz/transformation_add_type_struct.h", + "source/fuzz/transformation_add_type_vector.cpp", + "source/fuzz/transformation_add_type_vector.h", + "source/fuzz/transformation_adjust_branch_weights.cpp", + "source/fuzz/transformation_adjust_branch_weights.h", + "source/fuzz/transformation_composite_construct.cpp", + "source/fuzz/transformation_composite_construct.h", + "source/fuzz/transformation_composite_extract.cpp", + "source/fuzz/transformation_composite_extract.h", + "source/fuzz/transformation_composite_insert.cpp", + "source/fuzz/transformation_composite_insert.h", + "source/fuzz/transformation_compute_data_synonym_fact_closure.cpp", + "source/fuzz/transformation_compute_data_synonym_fact_closure.h", + "source/fuzz/transformation_context.cpp", + "source/fuzz/transformation_context.h", + "source/fuzz/transformation_duplicate_region_with_selection.cpp", + "source/fuzz/transformation_duplicate_region_with_selection.h", + "source/fuzz/transformation_equation_instruction.cpp", + "source/fuzz/transformation_equation_instruction.h", + "source/fuzz/transformation_expand_vector_reduction.cpp", + "source/fuzz/transformation_expand_vector_reduction.h", + "source/fuzz/transformation_flatten_conditional_branch.cpp", + "source/fuzz/transformation_flatten_conditional_branch.h", + "source/fuzz/transformation_function_call.cpp", + "source/fuzz/transformation_function_call.h", + "source/fuzz/transformation_inline_function.cpp", + "source/fuzz/transformation_inline_function.h", + "source/fuzz/transformation_invert_comparison_operator.cpp", + "source/fuzz/transformation_invert_comparison_operator.h", + "source/fuzz/transformation_load.cpp", + "source/fuzz/transformation_load.h", + "source/fuzz/transformation_make_vector_operation_dynamic.cpp", + "source/fuzz/transformation_make_vector_operation_dynamic.h", + "source/fuzz/transformation_merge_blocks.cpp", + "source/fuzz/transformation_merge_blocks.h", + "source/fuzz/transformation_merge_function_returns.cpp", + "source/fuzz/transformation_merge_function_returns.h", + "source/fuzz/transformation_move_block_down.cpp", + "source/fuzz/transformation_move_block_down.h", + "source/fuzz/transformation_move_instruction_down.cpp", + "source/fuzz/transformation_move_instruction_down.h", + "source/fuzz/transformation_mutate_pointer.cpp", + "source/fuzz/transformation_mutate_pointer.h", + "source/fuzz/transformation_outline_function.cpp", + "source/fuzz/transformation_outline_function.h", + "source/fuzz/transformation_permute_function_parameters.cpp", + "source/fuzz/transformation_permute_function_parameters.h", + "source/fuzz/transformation_permute_phi_operands.cpp", + "source/fuzz/transformation_permute_phi_operands.h", + "source/fuzz/transformation_propagate_instruction_down.cpp", + "source/fuzz/transformation_propagate_instruction_down.h", + "source/fuzz/transformation_propagate_instruction_up.cpp", + "source/fuzz/transformation_propagate_instruction_up.h", + "source/fuzz/transformation_push_id_through_variable.cpp", + "source/fuzz/transformation_push_id_through_variable.h", + "source/fuzz/transformation_record_synonymous_constants.cpp", + "source/fuzz/transformation_record_synonymous_constants.h", + "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp", + "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h", + "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp", + "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h", + "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp", + "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h", + "source/fuzz/transformation_replace_constant_with_uniform.cpp", + "source/fuzz/transformation_replace_constant_with_uniform.h", + "source/fuzz/transformation_replace_copy_memory_with_load_store.cpp", + "source/fuzz/transformation_replace_copy_memory_with_load_store.h", + "source/fuzz/transformation_replace_copy_object_with_store_load.cpp", + "source/fuzz/transformation_replace_copy_object_with_store_load.h", + "source/fuzz/transformation_replace_id_with_synonym.cpp", + "source/fuzz/transformation_replace_id_with_synonym.h", + "source/fuzz/transformation_replace_irrelevant_id.cpp", + "source/fuzz/transformation_replace_irrelevant_id.h", + "source/fuzz/transformation_replace_linear_algebra_instruction.cpp", + "source/fuzz/transformation_replace_linear_algebra_instruction.h", + "source/fuzz/transformation_replace_load_store_with_copy_memory.cpp", + "source/fuzz/transformation_replace_load_store_with_copy_memory.h", + "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp", + "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h", + "source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp", + "source/fuzz/transformation_replace_opselect_with_conditional_branch.h", + "source/fuzz/transformation_replace_parameter_with_global.cpp", + "source/fuzz/transformation_replace_parameter_with_global.h", + "source/fuzz/transformation_replace_params_with_struct.cpp", + "source/fuzz/transformation_replace_params_with_struct.h", + "source/fuzz/transformation_set_function_control.cpp", + "source/fuzz/transformation_set_function_control.h", + "source/fuzz/transformation_set_loop_control.cpp", + "source/fuzz/transformation_set_loop_control.h", + "source/fuzz/transformation_set_memory_operands_mask.cpp", + "source/fuzz/transformation_set_memory_operands_mask.h", + "source/fuzz/transformation_set_selection_control.cpp", + "source/fuzz/transformation_set_selection_control.h", + "source/fuzz/transformation_split_block.cpp", + "source/fuzz/transformation_split_block.h", + "source/fuzz/transformation_store.cpp", + "source/fuzz/transformation_store.h", + "source/fuzz/transformation_swap_commutable_operands.cpp", + "source/fuzz/transformation_swap_commutable_operands.h", + "source/fuzz/transformation_swap_conditional_branch_operands.cpp", + "source/fuzz/transformation_swap_conditional_branch_operands.h", + "source/fuzz/transformation_swap_function_variables.cpp", + "source/fuzz/transformation_swap_function_variables.h", + "source/fuzz/transformation_swap_two_functions.cpp", + "source/fuzz/transformation_swap_two_functions.h", + "source/fuzz/transformation_toggle_access_chain_instruction.cpp", + "source/fuzz/transformation_toggle_access_chain_instruction.h", + "source/fuzz/transformation_vector_shuffle.cpp", + "source/fuzz/transformation_vector_shuffle.h", + "source/fuzz/transformation_wrap_early_terminator_in_function.cpp", + "source/fuzz/transformation_wrap_early_terminator_in_function.h", + "source/fuzz/transformation_wrap_region_in_selection.cpp", + "source/fuzz/transformation_wrap_region_in_selection.h", + "source/fuzz/transformation_wrap_vector_synonym.cpp", + "source/fuzz/transformation_wrap_vector_synonym.h", + "source/fuzz/uniform_buffer_element_descriptor.cpp", + "source/fuzz/uniform_buffer_element_descriptor.h", + ] + deps = [ + ":spvtools", + ":spvtools_fuzz_proto", + ":spvtools_opt", + ":spvtools_reduce", + "//third_party/protobuf:protobuf_full", + ] + public_deps = [ ":spvtools_headers" ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + configs += [ ":spvtools_internal_config" ] + } +} + group("SPIRV-Tools") { public_deps = [ ":spvtools", @@ -905,6 +1365,7 @@ if (build_with_chromium) { ":spvtools", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", + ":spvtools_language_header_vkdebuginfo100", ":spvtools_val", "//testing/gmock", "//testing/gtest", @@ -1012,8 +1473,31 @@ executable("spirv-link") { configs += [ ":spvtools_internal_config" ] } +if (!is_ios && !spirv_is_winuwp && build_with_chromium) { + # iOS and UWP do not allow std::system calls which spirv-fuzz + # requires. Additionally, spirv-fuzz is only built when in a + # Chromium checkout due to its dependency on protobuf. + + executable("spirv-fuzz") { + sources = [ "tools/fuzz/fuzz.cpp" ] + deps = [ + ":spvtools", + ":spvtools_fuzz", + ":spvtools_opt", + ":spvtools_reduce", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + "//third_party/protobuf:protobuf_full", + ] + configs += [ ":spvtools_internal_config" ] + } +} + if (!is_ios && !spirv_is_winuwp) { - # iOS and UWP do not allow std::system calls which spirv-reduce requires + # iOS and UWP do not allow std::system calls which spirv-reduce + # requires. + executable("spirv-reduce") { sources = [ "tools/reduce/reduce.cpp" ] deps = [ @@ -1037,6 +1521,9 @@ group("all_spirv_tools") { ":spirv-opt", ":spirv-val", ] + if (!is_ios && !spirv_is_winuwp && build_with_chromium) { + deps += [ ":spirv-fuzz" ] + } if (!is_ios && !spirv_is_winuwp) { deps += [ ":spirv-reduce" ] } diff --git a/third_party/spirv-tools/CHANGES b/third_party/spirv-tools/CHANGES index 467655ccfe..ee232b6075 100644 --- a/third_party/spirv-tools/CHANGES +++ b/third_party/spirv-tools/CHANGES @@ -1,7 +1,44 @@ Revision history for SPIRV-Tools -v2021.3-dev 2021-06-22 - - Start v2021.3-dev +v2021.4 2021-11-11 + - General + - Add a WebAssembly build (#3752) + - Make cxx exceptions controllable (#4591) + - Validator + - Improve decoration validation (#4490) + - Optimizer + - Add spirv-opt pass to replace descriptor accesses based on variable indices (#4574) + - Do not fold snegate feeding sdiv (#4600) + - Handle overflowing id in merge return (#4606) + - Fuzzer + - Add libFuzzer target for spirv-fuzz (#4434) + - Linter + +v2021.3 2021-08-24 + - General + - Initial support for SPV_KHR_integer_dot_product (#4327) + - Add non-semantic vulkan extended instruction set (#4362) + - Add common enum for debug info instructions from either opencl or vulkan (#4377) + - Validator + - Add validation for SPV_EXT_shader_atomic_float16_add (#4325) + - Disallow loading a runtime-sized array (#4473) + - spirv-val: Validate vulkan debug info similarly to opencl debug info (#4466) + - Optimizer + - spirv-opt: support SPV_EXT_shader_image_int64 (#4379) + - spirv-opt: Add dataflow analysis framework (#4402) + - Add control dependence analysis to opt (#4380) + - Add spirv-opt convert-to-sampled-image pass (#4340) + - spirv-opt: Add handling of vulkan debug info to DebugInfoManager (#4423) + - Fuzz + - spirv-fuzz: support AtomicLoad (#4330) + - spirv-fuzz: Support AtomicStore (#4440) + - spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376) + - spirv-fuzz: Add minimal SPIR-V example to test shaders (#4415) + - spirv-fuzz: support building using gn (#4365) + - Linter + - Add new target for spirv-lint (#4446) + - spirv-lint: add basic CLI argument handling (#4478) + - Add divergence analysis to linter (#4465) v2021.2 2021-06-18 - General diff --git a/third_party/spirv-tools/CMakeLists.txt b/third_party/spirv-tools/CMakeLists.txt index 5530f8c4f3..3991bf6605 100755 --- a/third_party/spirv-tools/CMakeLists.txt +++ b/third_party/spirv-tools/CMakeLists.txt @@ -44,6 +44,7 @@ include(GNUInstallDirs) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_STANDARD 11) +option(ENABLE_RTTI "Enables RTTI" OFF) option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") @@ -96,6 +97,10 @@ endif(SPIRV_BUILD_COMPRESSION) option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) +set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to control, via link options, which fuzzing engine should be used") + +option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF) + # Filament specific changes # option(SPIRV_WERROR "Enable error on warning" ON) set(SPIRV_WERROR OFF) @@ -175,6 +180,7 @@ endif() # Note this target provides no API stability guarantees. # # Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909. +option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON) option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON) if(SPIRV_TOOLS_BUILD_STATIC) set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static) @@ -194,11 +200,14 @@ function(spvtools_default_compile_options TARGET) target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS}) if (${COMPILER_IS_LIKE_GNU}) - target_compile_options(${TARGET} PRIVATE - -std=c++11 -fno-exceptions -fno-rtti) + target_compile_options(${TARGET} PRIVATE -std=c++11 -fno-exceptions) target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion -Wno-sign-conversion) + + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() # For good call stacks in profiles, keep the frame pointers. if(NOT "${SPIRV_PERF}" STREQUAL "") target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer) @@ -223,7 +232,9 @@ function(spvtools_default_compile_options TARGET) if (MSVC) # Specify /EHs for exception handling. This makes using SPIRV-Tools as # dependencies in other projects easier. - target_compile_options(${TARGET} PRIVATE /EHs) + if(ENABLE_EXCEPTIONS_ON_MSVC) + target_compile_options(${TARGET} PRIVATE /EHs) + endif() endif() # For MinGW cross compile, statically link to the C++ runtime. @@ -248,11 +259,13 @@ if(NOT COMMAND find_host_program) endif() # Tests require Python3 +# Filament specific changes if(CMAKE_VERSION VERSION_LESS "3.12" OR ${CMAKE_SYSTEM_NAME} MATCHES "Windows") find_host_package(PythonInterp 3 REQUIRED) else() find_package(Python3 COMPONENTS Interpreter) endif() +# End Filament specific changes # Check for symbol exports on Linux. # At the moment, this check will fail on the OSX build machines for the Android NDK. @@ -296,10 +309,10 @@ endif() # Defaults to OFF if the user didn't set it. # Filament specific changes -option(SPIRV_SKIP_EXECUTABLES - "Skip building the executable and tests along with the library" + option(SPIRV_SKIP_EXECUTABLES + "Skip building the executable and tests along with the library" ${SPIRV_SKIP_EXECUTABLES_OPTION}) -option(SPIRV_SKIP_TESTS + option(SPIRV_SKIP_TESTS "Skip building tests along with the library" ${SPIRV_SKIP_TESTS_OPTION}) # End Filament specific changes if ("${SPIRV_SKIP_EXECUTABLES}") @@ -310,7 +323,7 @@ endif() # Turn off if they take too long. option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON) if (${SPIRV_CHECK_CONTEXT}) - add_definitions(-DSPIRV_CHECK_CONTEXT) + add_compile_options($<$:-DSPIRV_CHECK_CONTEXT>) endif() # Precompiled header macro. Parameters are source file list and filename for pch cpp file. diff --git a/third_party/spirv-tools/CONTRIBUTING.md b/third_party/spirv-tools/CONTRIBUTING.md index b46ae31ec4..1eb8b689e8 100644 --- a/third_party/spirv-tools/CONTRIBUTING.md +++ b/third_party/spirv-tools/CONTRIBUTING.md @@ -98,13 +98,6 @@ should pay particular attention to: scenarios? The respective SPIR-V dialects are slightly different. * Changes are made to a container while iterating through it. You have to be careful that iterators are not invalidated or that elements are not skipped. -* C++11 and VS2013. We generally assume that we have a C++11 compliant - compiler. However, on Windows, we still support Visual Studio 2013, which is - not fully C++11 compliant. See - [here](https://msdn.microsoft.com/en-us/library/hh567368.aspx). In - particular, note that it does not provide default move-constructors or - move-assignments for classes. In general, r-value references do not work the - way you might assume they do. * For SPIR-V transforms: The module is changed, but the analyses are not updated. For example, a new instruction is added, but the def-use manager is not updated. Later on, it is possible that the def-use manager will be used, diff --git a/third_party/spirv-tools/DEPS b/third_party/spirv-tools/DEPS index 718927d040..2aaf11f926 100644 --- a/third_party/spirv-tools/DEPS +++ b/third_party/spirv-tools/DEPS @@ -3,10 +3,10 @@ use_relative_paths = True vars = { 'github': 'https://github.com', - 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', - 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', - 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', - 'spirv_headers_revision': 'ddf3230c14c71e81fc0eae9b781cc4bcc2d1f0f5', + 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133', + 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77', + 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', + 'spirv_headers_revision': '814e728b30ddd0f4509233099a3ad96fd4318c07', } deps = { diff --git a/third_party/spirv-tools/FILAMENT_README.md b/third_party/spirv-tools/FILAMENT_README.md new file mode 100644 index 0000000000..23bb8a02bf --- /dev/null +++ b/third_party/spirv-tools/FILAMENT_README.md @@ -0,0 +1,30 @@ +When updating spirv-tools to a new version, make sure to preserve all the changes marked with +the following in `CMakeLists.txt` and `source/CMakeLists.txt`: + +`# Filament specific changes` + +You can easily apply these changes by running the following command at Filament's root: + +``` +git apply third_party/spirv-tools/filament-specific-changes.patch +``` + +The following procedure can be used to update spirv-tools. Note that there is a secondary repository +that needs to be downloaded (spirv-headers). + +``` +curl -L https://github.com/KhronosGroup/spirv-tools/archive/master.zip > master.zip +unzip master.zip +rsync -r SPIRV-Tools-master/ spirv-tools/ --delete +rm -rf SPIRV-Tools-master master.zip +curl -L https://github.com/KhronosGroup/spirv-headers/archive/master.zip > master.zip +unzip master.zip +mv SPIRV-Headers-master/ spirv-tools/external/spirv-headers +rm master.zip + +git add spirv-tools +``` + +Edit the .gitignore so it doesn't prevent `spirv-headers` from being committed. + +Finally, remember to bring back the Filament-specific changes in CMakeLists. diff --git a/third_party/spirv-tools/README.md b/third_party/spirv-tools/README.md index 3637305bae..14db1e7045 100644 --- a/third_party/spirv-tools/README.md +++ b/third_party/spirv-tools/README.md @@ -21,7 +21,6 @@ headers, and XML registry. ## Downloads -[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master) Linux[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) MacOS[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) Windows[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) @@ -45,6 +44,20 @@ following versions are ordered from oldest to newest: Use the `--version` option on each command line tool to see the software version. An API call reports the software version as a C-style string. +## Releases + +Some versions of SPIRV-Tools are tagged as stable releases (see +[tags](https://github.com/KhronosGroup/SPIRV-Tools/tags) on github). +These versions undergo extra testing. +Releases are not directly related to releases (or versions) of +[SPIRV-Headers][spirv-headers]. +Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed +in the [DEPS](DEPS) file. +The release generally uses the most recent compatible version of SPIRV-Headers +available at the time of release. +No version of SPIRV-Headers other than the one listed in the DEPS file is +guaranteed to work with the SPIRV-Tools release. + ## Supported features ### Assembler, binary parser, and disassembler @@ -242,6 +255,34 @@ Contributions via merge request are welcome. Changes should: We intend to maintain a linear history on the GitHub `master` branch. +### Getting the source + +Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: + + git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools + cd spirv-tools + + # Check out sources for dependencies, at versions known to work together, + # as listed in the DEPS file. + python3 utils/git-sync-deps + +For some kinds of development, you may need the latest sources from the third-party projects: + + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers + git clone https://github.com/google/googletest.git spirv-tools/external/googletest + git clone https://github.com/google/effcee.git spirv-tools/external/effcee + git clone https://github.com/google/re2.git spirv-tools/external/re2 + +#### Dependency on Effcee + +Some tests depend on the [Effcee][effcee] library for stateful matching. +Effcee itself depends on [RE2][re2]. + +* If SPIRV-Tools is configured as part of a larger project that already uses + Effcee, then that project should include Effcee before SPIRV-Tools. +* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` + and RE2 sources to appear in `external/re2`. + ### Source code organization * `example`: demo code of using SPIRV-Tools APIs @@ -260,14 +301,6 @@ We intend to maintain a linear history on the GitHub `master` branch. * `test/`: Tests, using the [googletest][googletest] framework * `tools/`: Command line executables -Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: - - git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools - git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers - git clone https://github.com/google/googletest.git spirv-tools/external/googletest - git clone https://github.com/google/effcee.git spirv-tools/external/effcee - git clone https://github.com/google/re2.git spirv-tools/external/re2 - ### Tests The project contains a number of tests, used to drive development @@ -281,46 +314,12 @@ tests: `googletest` source into the `/external/googletest` directory before configuring and building the project. -*Note*: You must use a version of googletest that includes -[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610]. -The fix is included on the googletest master branch any time after 2015-11-10. -In particular, googletest must be newer than version 1.7.0. - -### Dependency on Effcee - -Some tests depend on the [Effcee][effcee] library for stateful matching. -Effcee itself depends on [RE2][re2]. - -* If SPIRV-Tools is configured as part of a larger project that already uses - Effcee, then that project should include Effcee before SPIRV-Tools. -* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` - and RE2 sources to appear in `external/re2`. - - ## Build -Instead of building manually, you can also download the binaries for your -platform directly from the [master-tot release][master-tot-release] on GitHub. -Those binaries are automatically uploaded by the buildbots after successful -testing and they always reflect the current top of the tree of the master -branch. +*Note*: Prebuilt binaries are available from the [downloads](docs/downloads.md) page. -In order to build the code, you first need to sync the external repositories -that it depends on. Assume that `` is the root directory of the -checked out code: - -```sh -cd -git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers -git clone https://github.com/google/effcee.git external/effcee -git clone https://github.com/google/re2.git external/re2 -git clone https://github.com/google/googletest.git external/googletest # optional - -``` - -*Note*: -The script `utils/git-sync-deps` can be used to checkout and/or update the -contents of the repos under `external/` instead of manually maintaining them. +First [get the sources](#getting-the-source). +Then build using CMake, Bazel, Android ndk-build, or the Emscripten SDK. ### Build using CMake You can build the project using [CMake][cmake]: @@ -348,7 +347,7 @@ option, like so: ```sh # In (the SPIRV-Tools repo root): -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf # In your build directory: cmake [-G ] -DSPIRV_BUILD_FUZZER=ON @@ -365,6 +364,30 @@ You can also use [Bazel](https://bazel.build/) to build the project. cd bazel build :all ``` +### Build a node.js package using Emscripten + +The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org) +module. The resulting `SpirvTools` WebAssembly module only exports methods to +assemble and disassemble SPIR-V modules. + +First, make sure you have the [Emscripten SDK](https://emscripten.org). +Then: + +```sh +cd +./source/wasm/build.sh +``` + +The resulting node package, with JavaScript and TypeScript bindings, is +written to `/out/web`. + +Note: This builds the package locally. It does *not* publish it to [npm](https://npmjs.org). + +To test the result: + +```sh +node ./test/wasm/test.js +``` ### Tools you'll need @@ -378,15 +401,17 @@ suite. - [Bazel](https://bazel.build/) (optional): if building the source with Bazel, you need to install Bazel Version 0.29.1 on your machine. Other versions may also work, but are not verified. +- [Emscripten SDK](https://emscripten.org) (optional): if building the + WebAssembly module. SPIRV-Tools is regularly tested with the following compilers: On Linux -- GCC version 4.8.5 -- Clang version 3.8 +- GCC version 9.3 +- Clang version 10.0 On MacOS -- AppleClang 10.0 +- AppleClang 11.0 On Windows - Visual Studio 2015 @@ -421,7 +446,7 @@ via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to `/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and iterator debugging. -### Android +### Android ndk-build SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and `libSPIRV-Tools-opt.a` for Android: @@ -442,11 +467,12 @@ $ANDROID_NDK/ndk-build -C ../android_test \ ``` ### Updating DEPS -Occasionally the entries in DEPS will need to be updated. This is done on demand -when there is a request to do this, often due to downstream breakages. There is -a script `utils/roll_deps.sh` provided, which will generate a patch with the -updated DEPS values. This will still need to be tested in your checkout to -confirm that there are no integration issues that need to be resolved. + +Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on +demand when there is a request to do this, often due to downstream breakages. +To update `DEPS`, run `utils/roll_deps.sh` and confirm that tests pass. +The script requires Chromium's +[`depot_tools`](https://chromium.googlesource.com/chromium/tools/depot_tools). ## Library @@ -643,8 +669,32 @@ This is experimental. ### Tests -Tests are only built when googletest is found. Use `ctest` to run all the -tests. +Tests are only built when googletest is found. + +#### Running test with CMake + +Use `ctest -j ` to run all the tests. To run tests using all threads: +```shell +ctest -j$(nproc) +``` + +To run a single test target, use `ctest [-j ] -R `. For example, +you can run all `opt` tests with: +```shell +ctest -R 'spirv-tools-test_opt' +``` + +#### Running test with Bazel + +Use `bazel test :all` to run all tests. This will run tests in parallel by default. + +To run a single test target, specify `:my_test_target` instead of `:all`. Test target +names get printed when you run `bazel test :all`. For example, you can run +`opt_def_use_test` with: +```shell +bazel test :opt_def_use_test +``` + ## Future Work @@ -705,4 +755,3 @@ limitations under the License. [CMake]: https://cmake.org/ [cpp-style-guide]: https://google.github.io/styleguide/cppguide.html [clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation -[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot diff --git a/third_party/spirv-tools/build_defs.bzl b/third_party/spirv-tools/build_defs.bzl index 30af3bd605..b2cd41b9a2 100644 --- a/third_party/spirv-tools/build_defs.bzl +++ b/third_party/spirv-tools/build_defs.bzl @@ -41,6 +41,7 @@ TEST_COPTS = COMMON_COPTS + select({ DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1" CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1" +SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1" def generate_core_tables(version = None): if not version: @@ -201,6 +202,23 @@ def base_test(name, srcs, deps = []): ] + deps, ) +def lint_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "lint_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":spirv_tools_lint", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + def link_test(name, srcs, deps = []): if name[-5:] != "_test": name = name + "_test" diff --git a/third_party/spirv-tools/docker-compose.yml b/third_party/spirv-tools/docker-compose.yml new file mode 100644 index 0000000000..fb6d114ffb --- /dev/null +++ b/third_party/spirv-tools/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3" +services: + build: + image: emscripten/emsdk:2.0.2 + environment: + GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-} + working_dir: /app + command: ./source/wasm/build.sh + volumes: + - ./:/app diff --git a/third_party/spirv-tools/docs/downloads.md b/third_party/spirv-tools/docs/downloads.md index 9c7d85671d..168937a705 100644 --- a/third_party/spirv-tools/docs/downloads.md +++ b/third_party/spirv-tools/docs/downloads.md @@ -1,14 +1,28 @@ # Downloads -Download the latest builds. -## Release +## Latest builds + +Download the latest builds of the [master](https://github.com/KhronosGroup/SPIRV-Tools/tree/master) branch. + +### Release build | Windows | Linux | MacOS | | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | | -## Debug +### Debug build | Windows | Linux | MacOS | | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | | + + +## Vulkan SDK + +SPIRV-Tools is published as part of the [LunarG Vulkan SDK](https://www.lunarg.com/vulkan-sdk/). +The Vulkan SDK is updated approximately every six weeks. + +## Android NDK + +SPIRV-Tools host executables, and library sources are published as +part of the [Android NDK](https://developer.android.com/ndk/downloads). diff --git a/third_party/spirv-tools/external/spirv-headers/.github/workflows/presubmit.yml b/third_party/spirv-tools/external/spirv-headers/.github/workflows/presubmit.yml new file mode 100644 index 0000000000..d9c25faef5 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/.github/workflows/presubmit.yml @@ -0,0 +1,37 @@ +name: Presubmit +on: [push, pull_request] + +jobs: + build: + name: Build ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v2 + - name: Install Ubuntu packages + if: matrix.os == 'ubuntu-latest' + run: sudo apt install -y dos2unix + - name: Install macOS packages + if: matrix.os == 'macos-latest' + run: brew install dos2unix + - name: Build + run: | + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX=install .. + cmake --build . --target install + - name: Build spec tools + run: | + cd tools/buildHeaders + mkdir build + cd build + cmake .. + cmake --build . --target install + - name: Build headers + run: | + cd tools/buildHeaders + ./bin/makeHeaders + - name: Check generated headers + run: git diff --exit-code diff --git a/third_party/spirv-tools/external/spirv-headers/BUILD.bazel b/third_party/spirv-tools/external/spirv-headers/BUILD.bazel index 9cb46bfc8f..c898ade80a 100644 --- a/third_party/spirv-tools/external/spirv-headers/BUILD.bazel +++ b/third_party/spirv-tools/external/spirv-headers/BUILD.bazel @@ -91,6 +91,11 @@ filegroup( srcs = ["include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"], ) +filegroup( + name = "spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json"], +) + filegroup( name = "spirv_ext_inst_spv_amd_gcn_shader_grammar_unified1", srcs = ["include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json"], diff --git a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt index ef2e566067..5c746ff60c 100644 --- a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt +++ b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt @@ -29,7 +29,7 @@ # https://www.khronos.org/registry/spir-v/ # cmake_minimum_required(VERSION 3.0) -project(SPIRV-Headers VERSION 1.5.1) +project(SPIRV-Headers VERSION 1.5.5) # There are two ways to use this project. # @@ -124,4 +124,10 @@ if (SPIRV_HEADERS_ENABLE_INSTALL) NAMESPACE "${namespace}" DESTINATION "${config_install_dir}" ) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/SPIRV-Headers.pc.in ${CMAKE_BINARY_DIR}/SPIRV-Headers.pc @ONLY) + install( + FILES "${CMAKE_BINARY_DIR}/SPIRV-Headers.pc" + DESTINATION ${CMAKE_INSTALL_DATADIR}/pkgconfig + ) endif() diff --git a/third_party/spirv-tools/external/spirv-headers/SPIRV-Headers.pc.in b/third_party/spirv-tools/external/spirv-headers/SPIRV-Headers.pc.in new file mode 100644 index 0000000000..345f5f8040 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/SPIRV-Headers.pc.in @@ -0,0 +1,9 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: SPIRV-Headers +Description: Header files from the SPIR-V registry +Version: @CMAKE_PROJECT_VERSION@ +Requires: +Libs: +Cflags: -I${includedir} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml b/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml index 75cba89a74..34453f83a9 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml @@ -69,7 +69,7 @@ - + @@ -81,7 +81,8 @@ - + + @@ -133,13 +134,14 @@ + - + @@ -161,13 +163,14 @@ + - + @@ -251,4 +254,24 @@ + + + + + + + + + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h index fa7061d845..380dc21b80 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h @@ -33,7 +33,7 @@ extern "C" { #endif enum { - NonSemanticClspvReflectionRevision = 1, + NonSemanticClspvReflectionRevision = 2, NonSemanticClspvReflectionRevision_BitWidthPadding = 0x7fffffff }; @@ -62,6 +62,7 @@ enum NonSemanticClspvReflectionInstructions { NonSemanticClspvReflectionConstantDataUniform = 22, NonSemanticClspvReflectionLiteralSampler = 23, NonSemanticClspvReflectionPropertyRequiredWorkgroupSize = 24, + NonSemanticClspvReflectionSpecConstantSubgroupMaxSize = 25, NonSemanticClspvReflectionInstructionsMax = 0x7fffffff }; diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticShaderDebugInfo100.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticShaderDebugInfo100.h new file mode 100644 index 0000000000..c52f32f809 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticShaderDebugInfo100.h @@ -0,0 +1,171 @@ +// 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. + +#ifndef SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ +#define SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticShaderDebugInfo100Version = 100, + NonSemanticShaderDebugInfo100Version_BitWidthPadding = 0x7fffffff +}; +enum { + NonSemanticShaderDebugInfo100Revision = 6, + NonSemanticShaderDebugInfo100Revision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100Instructions { + NonSemanticShaderDebugInfo100DebugInfoNone = 0, + NonSemanticShaderDebugInfo100DebugCompilationUnit = 1, + NonSemanticShaderDebugInfo100DebugTypeBasic = 2, + NonSemanticShaderDebugInfo100DebugTypePointer = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifier = 4, + NonSemanticShaderDebugInfo100DebugTypeArray = 5, + NonSemanticShaderDebugInfo100DebugTypeVector = 6, + NonSemanticShaderDebugInfo100DebugTypedef = 7, + NonSemanticShaderDebugInfo100DebugTypeFunction = 8, + NonSemanticShaderDebugInfo100DebugTypeEnum = 9, + NonSemanticShaderDebugInfo100DebugTypeComposite = 10, + NonSemanticShaderDebugInfo100DebugTypeMember = 11, + NonSemanticShaderDebugInfo100DebugTypeInheritance = 12, + NonSemanticShaderDebugInfo100DebugTypePtrToMember = 13, + NonSemanticShaderDebugInfo100DebugTypeTemplate = 14, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameter = 15, + NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter = 16, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack = 17, + NonSemanticShaderDebugInfo100DebugGlobalVariable = 18, + NonSemanticShaderDebugInfo100DebugFunctionDeclaration = 19, + NonSemanticShaderDebugInfo100DebugFunction = 20, + NonSemanticShaderDebugInfo100DebugLexicalBlock = 21, + NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator = 22, + NonSemanticShaderDebugInfo100DebugScope = 23, + NonSemanticShaderDebugInfo100DebugNoScope = 24, + NonSemanticShaderDebugInfo100DebugInlinedAt = 25, + NonSemanticShaderDebugInfo100DebugLocalVariable = 26, + NonSemanticShaderDebugInfo100DebugInlinedVariable = 27, + NonSemanticShaderDebugInfo100DebugDeclare = 28, + NonSemanticShaderDebugInfo100DebugValue = 29, + NonSemanticShaderDebugInfo100DebugOperation = 30, + NonSemanticShaderDebugInfo100DebugExpression = 31, + NonSemanticShaderDebugInfo100DebugMacroDef = 32, + NonSemanticShaderDebugInfo100DebugMacroUndef = 33, + NonSemanticShaderDebugInfo100DebugImportedEntity = 34, + NonSemanticShaderDebugInfo100DebugSource = 35, + NonSemanticShaderDebugInfo100DebugFunctionDefinition = 101, + NonSemanticShaderDebugInfo100DebugSourceContinued = 102, + NonSemanticShaderDebugInfo100DebugLine = 103, + NonSemanticShaderDebugInfo100DebugNoLine = 104, + NonSemanticShaderDebugInfo100DebugBuildIdentifier = 105, + NonSemanticShaderDebugInfo100DebugStoragePath = 106, + NonSemanticShaderDebugInfo100DebugEntryPoint = 107, + NonSemanticShaderDebugInfo100DebugTypeMatrix = 108, + NonSemanticShaderDebugInfo100InstructionsMax = 0x7fffffff +}; + + +enum NonSemanticShaderDebugInfo100DebugInfoFlags { + NonSemanticShaderDebugInfo100None = 0x0000, + NonSemanticShaderDebugInfo100FlagIsProtected = 0x01, + NonSemanticShaderDebugInfo100FlagIsPrivate = 0x02, + NonSemanticShaderDebugInfo100FlagIsPublic = 0x03, + NonSemanticShaderDebugInfo100FlagIsLocal = 0x04, + NonSemanticShaderDebugInfo100FlagIsDefinition = 0x08, + NonSemanticShaderDebugInfo100FlagFwdDecl = 0x10, + NonSemanticShaderDebugInfo100FlagArtificial = 0x20, + NonSemanticShaderDebugInfo100FlagExplicit = 0x40, + NonSemanticShaderDebugInfo100FlagPrototyped = 0x80, + NonSemanticShaderDebugInfo100FlagObjectPointer = 0x100, + NonSemanticShaderDebugInfo100FlagStaticMember = 0x200, + NonSemanticShaderDebugInfo100FlagIndirectVariable = 0x400, + NonSemanticShaderDebugInfo100FlagLValueReference = 0x800, + NonSemanticShaderDebugInfo100FlagRValueReference = 0x1000, + NonSemanticShaderDebugInfo100FlagIsOptimized = 0x2000, + NonSemanticShaderDebugInfo100FlagIsEnumClass = 0x4000, + NonSemanticShaderDebugInfo100FlagTypePassByValue = 0x8000, + NonSemanticShaderDebugInfo100FlagTypePassByReference = 0x10000, + NonSemanticShaderDebugInfo100FlagUnknownPhysicalLayout = 0x20000, + NonSemanticShaderDebugInfo100DebugInfoFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100BuildIdentifierFlags { + NonSemanticShaderDebugInfo100IdentifierPossibleDuplicates = 0x01, + NonSemanticShaderDebugInfo100BuildIdentifierFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncoding { + NonSemanticShaderDebugInfo100Unspecified = 0, + NonSemanticShaderDebugInfo100Address = 1, + NonSemanticShaderDebugInfo100Boolean = 2, + NonSemanticShaderDebugInfo100Float = 3, + NonSemanticShaderDebugInfo100Signed = 4, + NonSemanticShaderDebugInfo100SignedChar = 5, + NonSemanticShaderDebugInfo100Unsigned = 6, + NonSemanticShaderDebugInfo100UnsignedChar = 7, + NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugCompositeType { + NonSemanticShaderDebugInfo100Class = 0, + NonSemanticShaderDebugInfo100Structure = 1, + NonSemanticShaderDebugInfo100Union = 2, + NonSemanticShaderDebugInfo100DebugCompositeTypeMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugTypeQualifier { + NonSemanticShaderDebugInfo100ConstType = 0, + NonSemanticShaderDebugInfo100VolatileType = 1, + NonSemanticShaderDebugInfo100RestrictType = 2, + NonSemanticShaderDebugInfo100AtomicType = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifierMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugOperation { + NonSemanticShaderDebugInfo100Deref = 0, + NonSemanticShaderDebugInfo100Plus = 1, + NonSemanticShaderDebugInfo100Minus = 2, + NonSemanticShaderDebugInfo100PlusUconst = 3, + NonSemanticShaderDebugInfo100BitPiece = 4, + NonSemanticShaderDebugInfo100Swap = 5, + NonSemanticShaderDebugInfo100Xderef = 6, + NonSemanticShaderDebugInfo100StackValue = 7, + NonSemanticShaderDebugInfo100Constu = 8, + NonSemanticShaderDebugInfo100Fragment = 9, + NonSemanticShaderDebugInfo100DebugOperationMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugImportedEntity { + NonSemanticShaderDebugInfo100ImportedModule = 0, + NonSemanticShaderDebugInfo100ImportedDeclaration = 1, + NonSemanticShaderDebugInfo100DebugImportedEntityMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json index 15e56990a1..3d153e58f0 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json @@ -1,5 +1,5 @@ { - "revision" : 1, + "revision" : 2, "instructions" : [ { "opname" : "Kernel", @@ -232,6 +232,13 @@ { "kind" : "IdRef", "name" : "Y" }, { "kind" : "IdRef", "name" : "Z" } ] + }, + { + "opname" : "SpecConstantSubgroupMaxSize", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "Size" } + ] } ] } diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json new file mode 100644 index 0000000000..f3621b0b4f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json @@ -0,0 +1,713 @@ +{ + "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" : 100, + "revision" : 6, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Version'" }, + { "kind" : "IdRef", "name" : "'DWARF Version'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Language'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Encoding'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Storage Class'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "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" : "IdRef", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "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" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "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" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "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" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "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" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "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" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "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" : "IdRef", "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" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "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'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "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" : "IdRef", "name" : "'OpCode'" }, + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + }, + { + "opname" : "DebugImportedEntity", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Entity'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugSource", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDefinition", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Definition'" } + ] + }, + { + "opname" : "DebugSourceContinued", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'Text'" } + ] + }, + { + "opname" : "DebugLine", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line Start'" }, + { "kind" : "IdRef", "name" : "'Line End'" }, + { "kind" : "IdRef", "name" : "'Column Start'" }, + { "kind" : "IdRef", "name" : "'Column End'" } + ] + }, + { + "opname" : "DebugNoLine", + "opcode" : 104 + }, + { + "opname" : "DebugBuildIdentifier", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'Identifier'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugStoragePath", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'Path'" } + ] + }, + { + "opname" : "DebugEntryPoint", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "IdRef", "name" : "'Compilation Unit'" }, + { "kind" : "IdRef", "name" : "'Compiler Signature'" }, + { "kind" : "IdRef", "name" : "'Command-line Arguments'" } + ] + }, + { + "opname" : "DebugTypeMatrix", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'Vector Type'" }, + { "kind" : "IdRef", "name" : "'Vector Count'" }, + { "kind" : "IdRef", "name" : "'Column Major'" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "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" + }, + { + "enumerant" : "FlagUnknownPhysicalLayout", + "value" : "0x20000" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "BuildIdentifierFlags", + "enumerants" : [ + { + "enumerant" : "IdentifierPossibleDuplicates", + "value" : "0x01" + } + ] + }, + { + "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" : "IdRef" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Fragment", + "value" : "9", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugImportedEntity", + "enumerants" : [ + { + "enumerant" : "ImportedModule", + "value" : "0" + }, + { + "enumerant" : "ImportedDeclaration", + "value" : "1" + } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json index 2852c09bc0..05784973d0 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json @@ -4753,6 +4753,52 @@ "extensions" : [ "SPV_NV_ray_tracing" ], "version" : "None" }, + { + "opname" : "OpTraceMotionNV", + "class" : "Reserved", + "opcode" : 5338, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Time'" }, + { "kind" : "IdRef", "name" : "'PayloadId'" } + ], + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, + { + "opname" : "OpTraceRayMotionNV", + "class" : "Reserved", + "opcode" : 5339, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Time'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, { "opname" : "OpTypeAccelerationStructureNV", "class" : "Reserved", @@ -4898,6 +4944,88 @@ "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], "version" : "None" }, + { + "opname" : "OpConvertUToImageNV", + "class" : "Reserved", + "opcode" : 5391, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToSamplerNV", + "class" : "Reserved", + "opcode" : 5392, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertImageToUNV", + "class" : "Reserved", + "opcode" : 5393, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertSamplerToUNV", + "class" : "Reserved", + "opcode" : 5394, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToSampledImageNV", + "class" : "Reserved", + "opcode" : 5395, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertSampledImageToUNV", + "class" : "Reserved", + "opcode" : 5396, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpSamplerImageAddressingModeNV", + "class" : "Reserved", + "opcode" : 5397, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Bit Width'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, { "opname" : "OpSubgroupShuffleINTEL", "class" : "Group", @@ -5210,7 +5338,7 @@ "version" : "None" }, { - "opname" : "OpConstFunctionPointerINTEL", + "opname" : "OpConstantFunctionPointerINTEL", "class" : "@exclude", "opcode" : 5600, "operands" : [ @@ -7630,24 +7758,6 @@ "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], "version" : "None" }, - { - "opname" : "OpArbitraryFloatPowNINTEL", - "class" : "@exclude", - "opcode" : 5882, - "operands" : [ - { "kind" : "IdResultType" }, - { "kind" : "IdResult" }, - { "kind" : "IdRef", "name" : "'A'" }, - { "kind" : "LiteralInteger", "name" : "'M1'" }, - { "kind" : "IdRef", "name" : "'B'" }, - { "kind" : "LiteralInteger", "name" : "'Mout'" }, - { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, - { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, - { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } - ], - "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], - "version" : "None" - }, { "opname" : "OpLoopControlINTEL", "class" : "Reserved", @@ -8447,6 +8557,13 @@ "enumerant" : "ZeroExtend", "value" : "0x2000", "version" : "1.4" + }, + { + "enumerant" : "Offsets", + "value" : "0x10000", + "parameters" : [ + { "kind" : "IdRef" } + ] } ] }, @@ -8460,40 +8577,35 @@ }, { "enumerant" : "NotNaN", - "value" : "0x0001", - "capabilities" : [ "Kernel" ] + "value" : "0x0001" }, { "enumerant" : "NotInf", - "value" : "0x0002", - "capabilities" : [ "Kernel" ] + "value" : "0x0002" }, { "enumerant" : "NSZ", - "value" : "0x0004", - "capabilities" : [ "Kernel" ] + "value" : "0x0004" }, { "enumerant" : "AllowRecip", - "value" : "0x0008", - "capabilities" : [ "Kernel" ] + "value" : "0x0008" }, { "enumerant" : "Fast", - "value" : "0x0010", - "capabilities" : [ "Kernel" ] + "value" : "0x0010" }, { "enumerant" : "AllowContractFastINTEL", "value" : "0x10000", "capabilities" : [ "FPFastMathModeINTEL" ], - "version" : "None" + "version" : "None" }, { "enumerant" : "AllowReassocINTEL", "value" : "0x20000", "capabilities" : [ "FPFastMathModeINTEL" ], - "version" : "None" + "version" : "None" } ] }, @@ -8689,6 +8801,12 @@ { "enumerant" : "Const", "value" : "0x0008" + }, + { + "enumerant" : "OptNoneINTEL", + "value" : "0x10000", + "capabilities" : [ "OptNoneINTEL" ], + "version" : "None" } ] }, @@ -9445,7 +9563,9 @@ "value" : 39, "capabilities" : [ "Kernel" ], "parameters" : [ - { "kind" : "IdRef", "name" : "'Local Size Hint'" } + { "kind" : "IdRef", "name" : "'x size hint'" }, + { "kind" : "IdRef", "name" : "'y size hint'" }, + { "kind" : "IdRef", "name" : "'z size hint'" } ], "version" : "1.2" }, @@ -10433,35 +10553,51 @@ "enumerants" : [ { "enumerant" : "TRN", - "value" : 0 + "value" : 0, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "TRN_ZERO", - "value" : 1 + "value" : 1, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "RND", - "value" : 2 + "value" : 2, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "RND_ZERO", - "value" : 3 + "value" : 3, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "RND_INF", - "value" : 4 + "value" : 4, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "RND_MIN_INF", - "value" : 5 + "value" : 5, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "RND_CONV", - "value" : 6 + "value" : 6, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "RND_CONV_ODD", - "value" : 7 + "value" : 7, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" } ] }, @@ -10489,19 +10625,27 @@ "enumerants" : [ { "enumerant" : "WRAP", - "value" : 0 + "value" : 0, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "SAT", - "value" : 1 + "value" : 1, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "SAT_ZERO", - "value" : 2 + "value" : 2, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" }, { "enumerant" : "SAT_SYM", - "value" : 3 + "value" : 3, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" } ] }, @@ -11018,6 +11162,30 @@ "extensions" : [ "SPV_EXT_physical_storage_buffer" ], "version" : "1.5" }, + { + "enumerant" : "BindlessSamplerNV", + "value" : 5398, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BindlessImageNV", + "value" : 5399, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BoundSamplerNV", + "value" : 5400, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BoundImageNV", + "value" : 5401, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, { "enumerant" : "SIMTCallINTEL", "value" : 5599, @@ -12022,6 +12190,13 @@ "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], "version" : "None" }, + { + "enumerant" : "CurrentRayTimeNV", + "value" : 5334, + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, { "enumerant" : "IncomingRayFlagsNV", "value" : 5351, @@ -13054,6 +13229,13 @@ "extensions" : [ "SPV_NV_ray_tracing" ], "version" : "None" }, + { + "enumerant" : "RayTracingMotionBlurNV", + "value" : 5341, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, { "enumerant" : "VulkanMemoryModel", "value" : 5345, @@ -13145,6 +13327,12 @@ "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], "version" : "None" }, + { + "enumerant" : "BindlessTextureNV", + "value" : 5390, + "extensions" : [ "SPV_NV_bindless_texture" ], + "version" : "None" + }, { "enumerant" : "SubgroupShuffleINTEL", "value" : 5568, @@ -13427,6 +13615,12 @@ "extensions" : [ "SPV_INTEL_long_constant_composite" ], "version" : "None" }, + { + "enumerant" : "OptNoneINTEL", + "value" : 6094, + "extensions" : [ "SPV_INTEL_optnone" ], + "version" : "None" + }, { "enumerant" : "AtomicFloat16AddEXT", "value" : 6095, @@ -13509,7 +13703,7 @@ { "enumerant" : "PackedVectorFormat4x8BitKHR", "value" : 0, - "capabilities" : [ "DotProductInput4x8BitPackedKHR" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], "version" : "None" } ] diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs index 859f0e7c48..fd007ee5b7 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs @@ -349,6 +349,7 @@ namespace Spv VolatileTexelKHR = 11, SignExtend = 12, ZeroExtend = 13, + Offsets = 16, } public enum ImageOperandsMask @@ -372,6 +373,7 @@ namespace Spv VolatileTexelKHR = 0x00000800, SignExtend = 0x00001000, ZeroExtend = 0x00002000, + Offsets = 0x00010000, } public enum FPFastMathModeShift @@ -497,6 +499,10 @@ namespace Spv RestrictPointerEXT = 5355, AliasedPointer = 5356, AliasedPointerEXT = 5356, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, SIMTCallINTEL = 5599, ReferencedIndirectlyINTEL = 5602, ClobberINTEL = 5607, @@ -651,6 +657,7 @@ namespace Spv HitTNV = 5332, HitKindKHR = 5333, HitKindNV = 5333, + CurrentRayTimeNV = 5334, IncomingRayFlagsKHR = 5351, IncomingRayFlagsNV = 5351, RayGeometryIndexKHR = 5352, @@ -722,6 +729,7 @@ namespace Spv DontInline = 1, Pure = 2, Const = 3, + OptNoneINTEL = 16, } public enum FunctionControlMask @@ -731,6 +739,7 @@ namespace Spv DontInline = 0x00000002, Pure = 0x00000004, Const = 0x00000008, + OptNoneINTEL = 0x00010000, } public enum MemorySemanticsShift @@ -993,6 +1002,7 @@ namespace Spv StorageTexelBufferArrayNonUniformIndexing = 5312, StorageTexelBufferArrayNonUniformIndexingEXT = 5312, RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, VulkanMemoryModel = 5345, VulkanMemoryModelKHR = 5345, VulkanMemoryModelDeviceScope = 5346, @@ -1007,6 +1017,7 @@ namespace Spv ShaderSMBuiltinsNV = 5373, FragmentShaderPixelInterlockEXT = 5378, DemoteToHelperInvocationEXT = 5379, + BindlessTextureNV = 5390, SubgroupShuffleINTEL = 5568, SubgroupBufferBlockIOINTEL = 5569, SubgroupImageBlockIOINTEL = 5570, @@ -1053,6 +1064,7 @@ namespace Spv AtomicFloat32AddEXT = 6033, AtomicFloat64AddEXT = 6034, LongConstantCompositeINTEL = 6089, + OptNoneINTEL = 6094, AtomicFloat16AddEXT = 6095, DebugInfoModuleINTEL = 6114, } @@ -1549,6 +1561,8 @@ namespace Spv OpIgnoreIntersectionNV = 5335, OpTerminateRayNV = 5336, OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, OpTypeAccelerationStructureKHR = 5341, OpTypeAccelerationStructureNV = 5341, OpExecuteCallableNV = 5344, @@ -1561,6 +1575,13 @@ namespace Spv OpEndInvocationInterlockEXT = 5365, OpDemoteToHelperInvocationEXT = 5380, OpIsHelperInvocationEXT = 5381, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, OpSubgroupShuffleINTEL = 5571, OpSubgroupShuffleDownINTEL = 5572, OpSubgroupShuffleUpINTEL = 5573, @@ -1585,7 +1606,7 @@ namespace Spv OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpConstFunctionPointerINTEL = 5600, + OpConstantFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, OpAsmTargetINTEL = 5609, OpAsmINTEL = 5610, diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h index 34f90d3183..32e5b73efb 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h @@ -357,6 +357,7 @@ typedef enum SpvImageOperandsShift_ { SpvImageOperandsVolatileTexelKHRShift = 11, SpvImageOperandsSignExtendShift = 12, SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsOffsetsShift = 16, SpvImageOperandsMax = 0x7fffffff, } SpvImageOperandsShift; @@ -380,6 +381,7 @@ typedef enum SpvImageOperandsMask_ { SpvImageOperandsVolatileTexelKHRMask = 0x00000800, SpvImageOperandsSignExtendMask = 0x00001000, SpvImageOperandsZeroExtendMask = 0x00002000, + SpvImageOperandsOffsetsMask = 0x00010000, } SpvImageOperandsMask; typedef enum SpvFPFastMathModeShift_ { @@ -503,6 +505,10 @@ typedef enum SpvDecoration_ { SpvDecorationRestrictPointerEXT = 5355, SpvDecorationAliasedPointer = 5356, SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationBindlessSamplerNV = 5398, + SpvDecorationBindlessImageNV = 5399, + SpvDecorationBoundSamplerNV = 5400, + SpvDecorationBoundImageNV = 5401, SpvDecorationSIMTCallINTEL = 5599, SpvDecorationReferencedIndirectlyINTEL = 5602, SpvDecorationClobberINTEL = 5607, @@ -657,6 +663,7 @@ typedef enum SpvBuiltIn_ { SpvBuiltInHitTNV = 5332, SpvBuiltInHitKindKHR = 5333, SpvBuiltInHitKindNV = 5333, + SpvBuiltInCurrentRayTimeNV = 5334, SpvBuiltInIncomingRayFlagsKHR = 5351, SpvBuiltInIncomingRayFlagsNV = 5351, SpvBuiltInRayGeometryIndexKHR = 5352, @@ -726,6 +733,7 @@ typedef enum SpvFunctionControlShift_ { SpvFunctionControlDontInlineShift = 1, SpvFunctionControlPureShift = 2, SpvFunctionControlConstShift = 3, + SpvFunctionControlOptNoneINTELShift = 16, SpvFunctionControlMax = 0x7fffffff, } SpvFunctionControlShift; @@ -735,6 +743,7 @@ typedef enum SpvFunctionControlMask_ { SpvFunctionControlDontInlineMask = 0x00000002, SpvFunctionControlPureMask = 0x00000004, SpvFunctionControlConstMask = 0x00000008, + SpvFunctionControlOptNoneINTELMask = 0x00010000, } SpvFunctionControlMask; typedef enum SpvMemorySemanticsShift_ { @@ -993,6 +1002,7 @@ typedef enum SpvCapability_ { SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, SpvCapabilityRayTracingNV = 5340, + SpvCapabilityRayTracingMotionBlurNV = 5341, SpvCapabilityVulkanMemoryModel = 5345, SpvCapabilityVulkanMemoryModelKHR = 5345, SpvCapabilityVulkanMemoryModelDeviceScope = 5346, @@ -1007,6 +1017,7 @@ typedef enum SpvCapability_ { SpvCapabilityShaderSMBuiltinsNV = 5373, SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilityBindlessTextureNV = 5390, SpvCapabilitySubgroupShuffleINTEL = 5568, SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, SpvCapabilitySubgroupImageBlockIOINTEL = 5570, @@ -1053,6 +1064,7 @@ typedef enum SpvCapability_ { SpvCapabilityAtomicFloat32AddEXT = 6033, SpvCapabilityAtomicFloat64AddEXT = 6034, SpvCapabilityLongConstantCompositeINTEL = 6089, + SpvCapabilityOptNoneINTEL = 6094, SpvCapabilityAtomicFloat16AddEXT = 6095, SpvCapabilityDebugInfoModuleINTEL = 6114, SpvCapabilityMax = 0x7fffffff, @@ -1547,6 +1559,8 @@ typedef enum SpvOp_ { SpvOpIgnoreIntersectionNV = 5335, SpvOpTerminateRayNV = 5336, SpvOpTraceNV = 5337, + SpvOpTraceMotionNV = 5338, + SpvOpTraceRayMotionNV = 5339, SpvOpTypeAccelerationStructureKHR = 5341, SpvOpTypeAccelerationStructureNV = 5341, SpvOpExecuteCallableNV = 5344, @@ -1559,6 +1573,13 @@ typedef enum SpvOp_ { SpvOpEndInvocationInterlockEXT = 5365, SpvOpDemoteToHelperInvocationEXT = 5380, SpvOpIsHelperInvocationEXT = 5381, + SpvOpConvertUToImageNV = 5391, + SpvOpConvertUToSamplerNV = 5392, + SpvOpConvertImageToUNV = 5393, + SpvOpConvertSamplerToUNV = 5394, + SpvOpConvertUToSampledImageNV = 5395, + SpvOpConvertSampledImageToUNV = 5396, + SpvOpSamplerImageAddressingModeNV = 5397, SpvOpSubgroupShuffleINTEL = 5571, SpvOpSubgroupShuffleDownINTEL = 5572, SpvOpSubgroupShuffleUpINTEL = 5573, @@ -1583,7 +1604,7 @@ typedef enum SpvOp_ { SpvOpUSubSatINTEL = 5596, SpvOpIMul32x16INTEL = 5597, SpvOpUMul32x16INTEL = 5598, - SpvOpConstFunctionPointerINTEL = 5600, + SpvOpConstantFunctionPointerINTEL = 5600, SpvOpFunctionPointerCallINTEL = 5601, SpvOpAsmTargetINTEL = 5609, SpvOpAsmINTEL = 5610, @@ -2192,6 +2213,8 @@ inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultTy case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; @@ -2203,6 +2226,13 @@ inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultTy case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; case SpvOpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; @@ -2227,7 +2257,7 @@ inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultTy case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; - case SpvOpConstFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; case SpvOpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break; case SpvOpAsmINTEL: *hasResult = true; *hasResultType = true; break; diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp index a0b35b3f15..d66576d9b1 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp @@ -353,6 +353,7 @@ enum ImageOperandsShift { ImageOperandsVolatileTexelKHRShift = 11, ImageOperandsSignExtendShift = 12, ImageOperandsZeroExtendShift = 13, + ImageOperandsOffsetsShift = 16, ImageOperandsMax = 0x7fffffff, }; @@ -376,6 +377,7 @@ enum ImageOperandsMask { ImageOperandsVolatileTexelKHRMask = 0x00000800, ImageOperandsSignExtendMask = 0x00001000, ImageOperandsZeroExtendMask = 0x00002000, + ImageOperandsOffsetsMask = 0x00010000, }; enum FPFastMathModeShift { @@ -499,6 +501,10 @@ enum Decoration { DecorationRestrictPointerEXT = 5355, DecorationAliasedPointer = 5356, DecorationAliasedPointerEXT = 5356, + DecorationBindlessSamplerNV = 5398, + DecorationBindlessImageNV = 5399, + DecorationBoundSamplerNV = 5400, + DecorationBoundImageNV = 5401, DecorationSIMTCallINTEL = 5599, DecorationReferencedIndirectlyINTEL = 5602, DecorationClobberINTEL = 5607, @@ -653,6 +659,7 @@ enum BuiltIn { BuiltInHitTNV = 5332, BuiltInHitKindKHR = 5333, BuiltInHitKindNV = 5333, + BuiltInCurrentRayTimeNV = 5334, BuiltInIncomingRayFlagsKHR = 5351, BuiltInIncomingRayFlagsNV = 5351, BuiltInRayGeometryIndexKHR = 5352, @@ -722,6 +729,7 @@ enum FunctionControlShift { FunctionControlDontInlineShift = 1, FunctionControlPureShift = 2, FunctionControlConstShift = 3, + FunctionControlOptNoneINTELShift = 16, FunctionControlMax = 0x7fffffff, }; @@ -731,6 +739,7 @@ enum FunctionControlMask { FunctionControlDontInlineMask = 0x00000002, FunctionControlPureMask = 0x00000004, FunctionControlConstMask = 0x00000008, + FunctionControlOptNoneINTELMask = 0x00010000, }; enum MemorySemanticsShift { @@ -989,6 +998,7 @@ enum Capability { CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, CapabilityRayTracingNV = 5340, + CapabilityRayTracingMotionBlurNV = 5341, CapabilityVulkanMemoryModel = 5345, CapabilityVulkanMemoryModelKHR = 5345, CapabilityVulkanMemoryModelDeviceScope = 5346, @@ -1003,6 +1013,7 @@ enum Capability { CapabilityShaderSMBuiltinsNV = 5373, CapabilityFragmentShaderPixelInterlockEXT = 5378, CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilityBindlessTextureNV = 5390, CapabilitySubgroupShuffleINTEL = 5568, CapabilitySubgroupBufferBlockIOINTEL = 5569, CapabilitySubgroupImageBlockIOINTEL = 5570, @@ -1049,6 +1060,7 @@ enum Capability { CapabilityAtomicFloat32AddEXT = 6033, CapabilityAtomicFloat64AddEXT = 6034, CapabilityLongConstantCompositeINTEL = 6089, + CapabilityOptNoneINTEL = 6094, CapabilityAtomicFloat16AddEXT = 6095, CapabilityDebugInfoModuleINTEL = 6114, CapabilityMax = 0x7fffffff, @@ -1543,6 +1555,8 @@ enum Op { OpIgnoreIntersectionNV = 5335, OpTerminateRayNV = 5336, OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, OpTypeAccelerationStructureKHR = 5341, OpTypeAccelerationStructureNV = 5341, OpExecuteCallableNV = 5344, @@ -1555,6 +1569,13 @@ enum Op { OpEndInvocationInterlockEXT = 5365, OpDemoteToHelperInvocationEXT = 5380, OpIsHelperInvocationEXT = 5381, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, OpSubgroupShuffleINTEL = 5571, OpSubgroupShuffleDownINTEL = 5572, OpSubgroupShuffleUpINTEL = 5573, @@ -1579,7 +1600,7 @@ enum Op { OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpConstFunctionPointerINTEL = 5600, + OpConstantFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, OpAsmTargetINTEL = 5609, OpAsmINTEL = 5610, @@ -2188,6 +2209,8 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case OpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; case OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; @@ -2199,6 +2222,13 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; case OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case OpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; @@ -2223,7 +2253,7 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; - case OpConstFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case OpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; case OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; case OpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break; case OpAsmINTEL: *hasResult = true; *hasResultType = true; break; diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 index e17c6c6d26..677daf73bc 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 @@ -353,6 +353,7 @@ enum class ImageOperandsShift : unsigned { VolatileTexelKHR = 11, SignExtend = 12, ZeroExtend = 13, + Offsets = 16, Max = 0x7fffffff, }; @@ -376,6 +377,7 @@ enum class ImageOperandsMask : unsigned { VolatileTexelKHR = 0x00000800, SignExtend = 0x00001000, ZeroExtend = 0x00002000, + Offsets = 0x00010000, }; enum class FPFastMathModeShift : unsigned { @@ -499,6 +501,10 @@ enum class Decoration : unsigned { RestrictPointerEXT = 5355, AliasedPointer = 5356, AliasedPointerEXT = 5356, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, SIMTCallINTEL = 5599, ReferencedIndirectlyINTEL = 5602, ClobberINTEL = 5607, @@ -653,6 +659,7 @@ enum class BuiltIn : unsigned { HitTNV = 5332, HitKindKHR = 5333, HitKindNV = 5333, + CurrentRayTimeNV = 5334, IncomingRayFlagsKHR = 5351, IncomingRayFlagsNV = 5351, RayGeometryIndexKHR = 5352, @@ -722,6 +729,7 @@ enum class FunctionControlShift : unsigned { DontInline = 1, Pure = 2, Const = 3, + OptNoneINTEL = 16, Max = 0x7fffffff, }; @@ -731,6 +739,7 @@ enum class FunctionControlMask : unsigned { DontInline = 0x00000002, Pure = 0x00000004, Const = 0x00000008, + OptNoneINTEL = 0x00010000, }; enum class MemorySemanticsShift : unsigned { @@ -989,6 +998,7 @@ enum class Capability : unsigned { StorageTexelBufferArrayNonUniformIndexing = 5312, StorageTexelBufferArrayNonUniformIndexingEXT = 5312, RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, VulkanMemoryModel = 5345, VulkanMemoryModelKHR = 5345, VulkanMemoryModelDeviceScope = 5346, @@ -1003,6 +1013,7 @@ enum class Capability : unsigned { ShaderSMBuiltinsNV = 5373, FragmentShaderPixelInterlockEXT = 5378, DemoteToHelperInvocationEXT = 5379, + BindlessTextureNV = 5390, SubgroupShuffleINTEL = 5568, SubgroupBufferBlockIOINTEL = 5569, SubgroupImageBlockIOINTEL = 5570, @@ -1049,6 +1060,7 @@ enum class Capability : unsigned { AtomicFloat32AddEXT = 6033, AtomicFloat64AddEXT = 6034, LongConstantCompositeINTEL = 6089, + OptNoneINTEL = 6094, AtomicFloat16AddEXT = 6095, DebugInfoModuleINTEL = 6114, Max = 0x7fffffff, @@ -1543,6 +1555,8 @@ enum class Op : unsigned { OpIgnoreIntersectionNV = 5335, OpTerminateRayNV = 5336, OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, OpTypeAccelerationStructureKHR = 5341, OpTypeAccelerationStructureNV = 5341, OpExecuteCallableNV = 5344, @@ -1555,6 +1569,13 @@ enum class Op : unsigned { OpEndInvocationInterlockEXT = 5365, OpDemoteToHelperInvocationEXT = 5380, OpIsHelperInvocationEXT = 5381, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, OpSubgroupShuffleINTEL = 5571, OpSubgroupShuffleDownINTEL = 5572, OpSubgroupShuffleUpINTEL = 5573, @@ -1579,7 +1600,7 @@ enum class Op : unsigned { OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpConstFunctionPointerINTEL = 5600, + OpConstantFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, OpAsmTargetINTEL = 5609, OpAsmINTEL = 5610, @@ -2188,6 +2209,8 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case Op::OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; case Op::OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; case Op::OpTraceNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; case Op::OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; case Op::OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; case Op::OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; @@ -2199,6 +2222,13 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case Op::OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; case Op::OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; case Op::OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case Op::OpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; case Op::OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; case Op::OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; case Op::OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; @@ -2223,7 +2253,7 @@ inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { case Op::OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; case Op::OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; case Op::OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; - case Op::OpConstFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; case Op::OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; case Op::OpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break; case Op::OpAsmINTEL: *hasResult = true; *hasResultType = true; break; diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json index 1c63036f36..e925ab91c7 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json @@ -395,7 +395,8 @@ "VolatileTexel": 11, "VolatileTexelKHR": 11, "SignExtend": 12, - "ZeroExtend": 13 + "ZeroExtend": 13, + "Offsets": 16 } }, { @@ -527,6 +528,10 @@ "RestrictPointerEXT": 5355, "AliasedPointer": 5356, "AliasedPointerEXT": 5356, + "BindlessSamplerNV": 5398, + "BindlessImageNV": 5399, + "BoundSamplerNV": 5400, + "BoundImageNV": 5401, "SIMTCallINTEL": 5599, "ReferencedIndirectlyINTEL": 5602, "ClobberINTEL": 5607, @@ -684,6 +689,7 @@ "HitTNV": 5332, "HitKindKHR": 5333, "HitKindNV": 5333, + "CurrentRayTimeNV": 5334, "IncomingRayFlagsKHR": 5351, "IncomingRayFlagsNV": 5351, "RayGeometryIndexKHR": 5352, @@ -734,7 +740,8 @@ "Inline": 0, "DontInline": 1, "Pure": 2, - "Const": 3 + "Const": 3, + "OptNoneINTEL": 16 } }, { @@ -976,6 +983,7 @@ "StorageTexelBufferArrayNonUniformIndexing": 5312, "StorageTexelBufferArrayNonUniformIndexingEXT": 5312, "RayTracingNV": 5340, + "RayTracingMotionBlurNV": 5341, "VulkanMemoryModel": 5345, "VulkanMemoryModelKHR": 5345, "VulkanMemoryModelDeviceScope": 5346, @@ -990,6 +998,7 @@ "ShaderSMBuiltinsNV": 5373, "FragmentShaderPixelInterlockEXT": 5378, "DemoteToHelperInvocationEXT": 5379, + "BindlessTextureNV": 5390, "SubgroupShuffleINTEL": 5568, "SubgroupBufferBlockIOINTEL": 5569, "SubgroupImageBlockIOINTEL": 5570, @@ -1036,6 +1045,7 @@ "AtomicFloat32AddEXT": 6033, "AtomicFloat64AddEXT": 6034, "LongConstantCompositeINTEL": 6089, + "OptNoneINTEL": 6094, "AtomicFloat16AddEXT": 6095, "DebugInfoModuleINTEL": 6114 } @@ -1541,6 +1551,8 @@ "OpIgnoreIntersectionNV": 5335, "OpTerminateRayNV": 5336, "OpTraceNV": 5337, + "OpTraceMotionNV": 5338, + "OpTraceRayMotionNV": 5339, "OpTypeAccelerationStructureKHR": 5341, "OpTypeAccelerationStructureNV": 5341, "OpExecuteCallableNV": 5344, @@ -1553,6 +1565,13 @@ "OpEndInvocationInterlockEXT": 5365, "OpDemoteToHelperInvocationEXT": 5380, "OpIsHelperInvocationEXT": 5381, + "OpConvertUToImageNV": 5391, + "OpConvertUToSamplerNV": 5392, + "OpConvertImageToUNV": 5393, + "OpConvertSamplerToUNV": 5394, + "OpConvertUToSampledImageNV": 5395, + "OpConvertSampledImageToUNV": 5396, + "OpSamplerImageAddressingModeNV": 5397, "OpSubgroupShuffleINTEL": 5571, "OpSubgroupShuffleDownINTEL": 5572, "OpSubgroupShuffleUpINTEL": 5573, @@ -1577,7 +1596,7 @@ "OpUSubSatINTEL": 5596, "OpIMul32x16INTEL": 5597, "OpUMul32x16INTEL": 5598, - "OpConstFunctionPointerINTEL": 5600, + "OpConstantFunctionPointerINTEL": 5600, "OpFunctionPointerCallINTEL": 5601, "OpAsmTargetINTEL": 5609, "OpAsmINTEL": 5610, diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua index afdaec9e25..0806dd78de 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua @@ -332,6 +332,7 @@ spv = { VolatileTexelKHR = 11, SignExtend = 12, ZeroExtend = 13, + Offsets = 16, }, ImageOperandsMask = { @@ -354,6 +355,7 @@ spv = { VolatileTexelKHR = 0x00000800, SignExtend = 0x00001000, ZeroExtend = 0x00002000, + Offsets = 0x00010000, }, FPFastMathModeShift = { @@ -472,6 +474,10 @@ spv = { RestrictPointerEXT = 5355, AliasedPointer = 5356, AliasedPointerEXT = 5356, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, SIMTCallINTEL = 5599, ReferencedIndirectlyINTEL = 5602, ClobberINTEL = 5607, @@ -625,6 +631,7 @@ spv = { HitTNV = 5332, HitKindKHR = 5333, HitKindNV = 5333, + CurrentRayTimeNV = 5334, IncomingRayFlagsKHR = 5351, IncomingRayFlagsNV = 5351, RayGeometryIndexKHR = 5352, @@ -691,6 +698,7 @@ spv = { DontInline = 1, Pure = 2, Const = 3, + OptNoneINTEL = 16, }, FunctionControlMask = { @@ -699,6 +707,7 @@ spv = { DontInline = 0x00000002, Pure = 0x00000004, Const = 0x00000008, + OptNoneINTEL = 0x00010000, }, MemorySemanticsShift = { @@ -951,6 +960,7 @@ spv = { StorageTexelBufferArrayNonUniformIndexing = 5312, StorageTexelBufferArrayNonUniformIndexingEXT = 5312, RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, VulkanMemoryModel = 5345, VulkanMemoryModelKHR = 5345, VulkanMemoryModelDeviceScope = 5346, @@ -965,6 +975,7 @@ spv = { ShaderSMBuiltinsNV = 5373, FragmentShaderPixelInterlockEXT = 5378, DemoteToHelperInvocationEXT = 5379, + BindlessTextureNV = 5390, SubgroupShuffleINTEL = 5568, SubgroupBufferBlockIOINTEL = 5569, SubgroupImageBlockIOINTEL = 5570, @@ -1011,6 +1022,7 @@ spv = { AtomicFloat32AddEXT = 6033, AtomicFloat64AddEXT = 6034, LongConstantCompositeINTEL = 6089, + OptNoneINTEL = 6094, AtomicFloat16AddEXT = 6095, DebugInfoModuleINTEL = 6114, }, @@ -1494,6 +1506,8 @@ spv = { OpIgnoreIntersectionNV = 5335, OpTerminateRayNV = 5336, OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, OpTypeAccelerationStructureKHR = 5341, OpTypeAccelerationStructureNV = 5341, OpExecuteCallableNV = 5344, @@ -1506,6 +1520,13 @@ spv = { OpEndInvocationInterlockEXT = 5365, OpDemoteToHelperInvocationEXT = 5380, OpIsHelperInvocationEXT = 5381, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, OpSubgroupShuffleINTEL = 5571, OpSubgroupShuffleDownINTEL = 5572, OpSubgroupShuffleUpINTEL = 5573, @@ -1530,7 +1551,7 @@ spv = { OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpConstFunctionPointerINTEL = 5600, + OpConstantFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, OpAsmTargetINTEL = 5609, OpAsmINTEL = 5610, diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py index 09755a2039..ad41708063 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py @@ -332,6 +332,7 @@ spv = { 'VolatileTexelKHR' : 11, 'SignExtend' : 12, 'ZeroExtend' : 13, + 'Offsets' : 16, }, 'ImageOperandsMask' : { @@ -354,6 +355,7 @@ spv = { 'VolatileTexelKHR' : 0x00000800, 'SignExtend' : 0x00001000, 'ZeroExtend' : 0x00002000, + 'Offsets' : 0x00010000, }, 'FPFastMathModeShift' : { @@ -472,6 +474,10 @@ spv = { 'RestrictPointerEXT' : 5355, 'AliasedPointer' : 5356, 'AliasedPointerEXT' : 5356, + 'BindlessSamplerNV' : 5398, + 'BindlessImageNV' : 5399, + 'BoundSamplerNV' : 5400, + 'BoundImageNV' : 5401, 'SIMTCallINTEL' : 5599, 'ReferencedIndirectlyINTEL' : 5602, 'ClobberINTEL' : 5607, @@ -625,6 +631,7 @@ spv = { 'HitTNV' : 5332, 'HitKindKHR' : 5333, 'HitKindNV' : 5333, + 'CurrentRayTimeNV' : 5334, 'IncomingRayFlagsKHR' : 5351, 'IncomingRayFlagsNV' : 5351, 'RayGeometryIndexKHR' : 5352, @@ -691,6 +698,7 @@ spv = { 'DontInline' : 1, 'Pure' : 2, 'Const' : 3, + 'OptNoneINTEL' : 16, }, 'FunctionControlMask' : { @@ -699,6 +707,7 @@ spv = { 'DontInline' : 0x00000002, 'Pure' : 0x00000004, 'Const' : 0x00000008, + 'OptNoneINTEL' : 0x00010000, }, 'MemorySemanticsShift' : { @@ -951,6 +960,7 @@ spv = { 'StorageTexelBufferArrayNonUniformIndexing' : 5312, 'StorageTexelBufferArrayNonUniformIndexingEXT' : 5312, 'RayTracingNV' : 5340, + 'RayTracingMotionBlurNV' : 5341, 'VulkanMemoryModel' : 5345, 'VulkanMemoryModelKHR' : 5345, 'VulkanMemoryModelDeviceScope' : 5346, @@ -965,6 +975,7 @@ spv = { 'ShaderSMBuiltinsNV' : 5373, 'FragmentShaderPixelInterlockEXT' : 5378, 'DemoteToHelperInvocationEXT' : 5379, + 'BindlessTextureNV' : 5390, 'SubgroupShuffleINTEL' : 5568, 'SubgroupBufferBlockIOINTEL' : 5569, 'SubgroupImageBlockIOINTEL' : 5570, @@ -1011,6 +1022,7 @@ spv = { 'AtomicFloat32AddEXT' : 6033, 'AtomicFloat64AddEXT' : 6034, 'LongConstantCompositeINTEL' : 6089, + 'OptNoneINTEL' : 6094, 'AtomicFloat16AddEXT' : 6095, 'DebugInfoModuleINTEL' : 6114, }, @@ -1494,6 +1506,8 @@ spv = { 'OpIgnoreIntersectionNV' : 5335, 'OpTerminateRayNV' : 5336, 'OpTraceNV' : 5337, + 'OpTraceMotionNV' : 5338, + 'OpTraceRayMotionNV' : 5339, 'OpTypeAccelerationStructureKHR' : 5341, 'OpTypeAccelerationStructureNV' : 5341, 'OpExecuteCallableNV' : 5344, @@ -1506,6 +1520,13 @@ spv = { 'OpEndInvocationInterlockEXT' : 5365, 'OpDemoteToHelperInvocationEXT' : 5380, 'OpIsHelperInvocationEXT' : 5381, + 'OpConvertUToImageNV' : 5391, + 'OpConvertUToSamplerNV' : 5392, + 'OpConvertImageToUNV' : 5393, + 'OpConvertSamplerToUNV' : 5394, + 'OpConvertUToSampledImageNV' : 5395, + 'OpConvertSampledImageToUNV' : 5396, + 'OpSamplerImageAddressingModeNV' : 5397, 'OpSubgroupShuffleINTEL' : 5571, 'OpSubgroupShuffleDownINTEL' : 5572, 'OpSubgroupShuffleUpINTEL' : 5573, @@ -1530,7 +1551,7 @@ spv = { 'OpUSubSatINTEL' : 5596, 'OpIMul32x16INTEL' : 5597, 'OpUMul32x16INTEL' : 5598, - 'OpConstFunctionPointerINTEL' : 5600, + 'OpConstantFunctionPointerINTEL' : 5600, 'OpFunctionPointerCallINTEL' : 5601, 'OpAsmTargetINTEL' : 5609, 'OpAsmINTEL' : 5610, diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d index 179a6b1604..afff6f4b01 100644 --- a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d @@ -352,6 +352,7 @@ enum ImageOperandsShift : uint VolatileTexelKHR = 11, SignExtend = 12, ZeroExtend = 13, + Offsets = 16, } enum ImageOperandsMask : uint @@ -375,6 +376,7 @@ enum ImageOperandsMask : uint VolatileTexelKHR = 0x00000800, SignExtend = 0x00001000, ZeroExtend = 0x00002000, + Offsets = 0x00010000, } enum FPFastMathModeShift : uint @@ -500,6 +502,10 @@ enum Decoration : uint RestrictPointerEXT = 5355, AliasedPointer = 5356, AliasedPointerEXT = 5356, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, SIMTCallINTEL = 5599, ReferencedIndirectlyINTEL = 5602, ClobberINTEL = 5607, @@ -654,6 +660,7 @@ enum BuiltIn : uint HitTNV = 5332, HitKindKHR = 5333, HitKindNV = 5333, + CurrentRayTimeNV = 5334, IncomingRayFlagsKHR = 5351, IncomingRayFlagsNV = 5351, RayGeometryIndexKHR = 5352, @@ -725,6 +732,7 @@ enum FunctionControlShift : uint DontInline = 1, Pure = 2, Const = 3, + OptNoneINTEL = 16, } enum FunctionControlMask : uint @@ -734,6 +742,7 @@ enum FunctionControlMask : uint DontInline = 0x00000002, Pure = 0x00000004, Const = 0x00000008, + OptNoneINTEL = 0x00010000, } enum MemorySemanticsShift : uint @@ -996,6 +1005,7 @@ enum Capability : uint StorageTexelBufferArrayNonUniformIndexing = 5312, StorageTexelBufferArrayNonUniformIndexingEXT = 5312, RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, VulkanMemoryModel = 5345, VulkanMemoryModelKHR = 5345, VulkanMemoryModelDeviceScope = 5346, @@ -1010,6 +1020,7 @@ enum Capability : uint ShaderSMBuiltinsNV = 5373, FragmentShaderPixelInterlockEXT = 5378, DemoteToHelperInvocationEXT = 5379, + BindlessTextureNV = 5390, SubgroupShuffleINTEL = 5568, SubgroupBufferBlockIOINTEL = 5569, SubgroupImageBlockIOINTEL = 5570, @@ -1056,6 +1067,7 @@ enum Capability : uint AtomicFloat32AddEXT = 6033, AtomicFloat64AddEXT = 6034, LongConstantCompositeINTEL = 6089, + OptNoneINTEL = 6094, AtomicFloat16AddEXT = 6095, DebugInfoModuleINTEL = 6114, } @@ -1552,6 +1564,8 @@ enum Op : uint OpIgnoreIntersectionNV = 5335, OpTerminateRayNV = 5336, OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, OpTypeAccelerationStructureKHR = 5341, OpTypeAccelerationStructureNV = 5341, OpExecuteCallableNV = 5344, @@ -1564,6 +1578,13 @@ enum Op : uint OpEndInvocationInterlockEXT = 5365, OpDemoteToHelperInvocationEXT = 5380, OpIsHelperInvocationEXT = 5381, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, OpSubgroupShuffleINTEL = 5571, OpSubgroupShuffleDownINTEL = 5572, OpSubgroupShuffleUpINTEL = 5573, @@ -1588,7 +1609,7 @@ enum Op : uint OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpConstFunctionPointerINTEL = 5600, + OpConstantFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, OpAsmTargetINTEL = 5609, OpAsmINTEL = 5610, diff --git a/third_party/spirv-tools/filament-specific-changes.patch b/third_party/spirv-tools/filament-specific-changes.patch index 55e95faff2..39578c398a 100644 --- a/third_party/spirv-tools/filament-specific-changes.patch +++ b/third_party/spirv-tools/filament-specific-changes.patch @@ -1,17 +1,17 @@ -From f2c7730fd3246c70c6cb23f1637aa05a3a8b517a Mon Sep 17 00:00:00 2001 +From 468f1935352773b5f9975cae8800ed148b0c8bf3 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty -Date: Tue, 1 Jun 2021 23:31:16 -0700 +Date: Tue, 2 Nov 2021 14:48:14 -0700 Subject: [PATCH] Filament specific changes --- - third_party/spirv-tools/CMakeLists.txt | 86 ++++++++++++------- + third_party/spirv-tools/CMakeLists.txt | 94 ++++++++++++------- .../spirv-tools/external/CMakeLists.txt | 6 +- .../external/spirv-headers/CMakeLists.txt | 8 +- - third_party/spirv-tools/source/CMakeLists.txt | 26 ++++-- - 4 files changed, 81 insertions(+), 45 deletions(-) + third_party/spirv-tools/source/CMakeLists.txt | 26 +++-- + 4 files changed, 86 insertions(+), 48 deletions(-) diff --git a/third_party/spirv-tools/CMakeLists.txt b/third_party/spirv-tools/CMakeLists.txt -index 55f84e6d8..41ae7897c 100755 +index 70caf857a..3991bf660 100755 --- a/third_party/spirv-tools/CMakeLists.txt +++ b/third_party/spirv-tools/CMakeLists.txt @@ -24,8 +24,19 @@ if (POLICY CMP0054) @@ -35,7 +35,7 @@ index 55f84e6d8..41ae7897c 100755 set(SPIRV_TOOLS "SPIRV-Tools") include(GNUInstallDirs) -@@ -68,7 +79,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "") +@@ -71,7 +82,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "") set(CMAKE_BUILD_TYPE "Debug") endif() @@ -46,9 +46,9 @@ index 55f84e6d8..41ae7897c 100755 if(NOT ${SKIP_SPIRV_TOOLS_INSTALL}) set(ENABLE_SPIRV_TOOLS_INSTALL ON) endif() -@@ -81,7 +94,10 @@ endif(SPIRV_BUILD_COMPRESSION) +@@ -88,7 +101,10 @@ set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to c - option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) + option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF) -option(SPIRV_WERROR "Enable error on warning" ON) +# Filament specific changes @@ -58,36 +58,41 @@ index 55f84e6d8..41ae7897c 100755 if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))) set(COMPILER_IS_LIKE_GNU TRUE) endif() -@@ -230,7 +246,11 @@ if(NOT COMMAND find_host_program) +@@ -243,7 +259,13 @@ if(NOT COMMAND find_host_program) endif() # Tests require Python3 -find_host_package(PythonInterp 3 REQUIRED) ++# Filament specific changes +if(CMAKE_VERSION VERSION_LESS "3.12" OR ${CMAKE_SYSTEM_NAME} MATCHES "Windows") + find_host_package(PythonInterp 3 REQUIRED) +else() + find_package(Python3 COMPONENTS Interpreter) +endif() ++# End Filament specific changes # Check for symbol exports on Linux. # At the moment, this check will fail on the OSX build machines for the Android NDK. -@@ -273,11 +293,13 @@ if(ENABLE_SPIRV_TOOLS_INSTALL) +@@ -286,11 +308,13 @@ if(ENABLE_SPIRV_TOOLS_INSTALL) endif() # Defaults to OFF if the user didn't set it. -+# Filament specific changes - option(SPIRV_SKIP_EXECUTABLES - "Skip building the executable and tests along with the library" +-option(SPIRV_SKIP_EXECUTABLES +- "Skip building the executable and tests along with the library" - ${SPIRV_SKIP_EXECUTABLES}) -+ ${SPIRV_SKIP_EXECUTABLES_OPTION}) - option(SPIRV_SKIP_TESTS +-option(SPIRV_SKIP_TESTS - "Skip building tests along with the library" ${SPIRV_SKIP_TESTS}) ++# Filament specific changes ++ option(SPIRV_SKIP_EXECUTABLES ++ "Skip building the executable and tests along with the library" ++ ${SPIRV_SKIP_EXECUTABLES_OPTION}) ++ option(SPIRV_SKIP_TESTS + "Skip building tests along with the library" ${SPIRV_SKIP_TESTS_OPTION}) +# End Filament specific changes if ("${SPIRV_SKIP_EXECUTABLES}") set(SPIRV_SKIP_TESTS ON) endif() -@@ -321,8 +343,10 @@ endif() +@@ -334,8 +358,10 @@ endif() add_subdirectory(source) add_subdirectory(tools) @@ -100,7 +105,7 @@ index 55f84e6d8..41ae7897c 100755 if(ENABLE_SPIRV_TOOLS_INSTALL) install( -@@ -347,28 +371,30 @@ set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared") +@@ -360,28 +386,30 @@ set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared") # Build pkg-config file # Use a first-class target so it's regenerated when relevant files are updated. @@ -171,7 +176,7 @@ index 179a4012f..152cfc421 100644 endif() else() diff --git a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt -index 6f01ef098..ef2e56606 100644 +index c11150008..1a1fe9548 100644 --- a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt +++ b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt @@ -49,11 +49,11 @@ add_custom_target(install-headers @@ -191,10 +196,10 @@ index 6f01ef098..ef2e56606 100644 if(NOT ${SPIRV_HEADERS_SKIP_EXAMPLES}) set(SPIRV_HEADERS_ENABLE_EXAMPLES ON) diff --git a/third_party/spirv-tools/source/CMakeLists.txt b/third_party/spirv-tools/source/CMakeLists.txt -index 6633bc916..5f5904567 100644 +index 331ff6755..ef6eb8e77 100644 --- a/third_party/spirv-tools/source/CMakeLists.txt +++ b/third_party/spirv-tools/source/CMakeLists.txt -@@ -364,13 +364,15 @@ endfunction() +@@ -371,13 +371,15 @@ endfunction() # Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and # unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility. @@ -217,7 +222,7 @@ index 6633bc916..5f5904567 100644 if(SPIRV_TOOLS_BUILD_STATIC) add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES}) -@@ -386,11 +388,17 @@ if(SPIRV_TOOLS_BUILD_STATIC) +@@ -393,11 +395,17 @@ if(SPIRV_TOOLS_BUILD_STATIC) add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static) endif() @@ -238,5 +243,5 @@ index 6633bc916..5f5904567 100644 if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") -- -2.32.0.rc0.204.g9fa02ecfa5-goog +2.33.1.1089.g2158813163f-goog diff --git a/third_party/spirv-tools/include/spirv-tools/libspirv.h b/third_party/spirv-tools/include/spirv-tools/libspirv.h index 039dab18ae..8df14f5fb4 100644 --- a/third_party/spirv-tools/include/spirv-tools/libspirv.h +++ b/third_party/spirv-tools/include/spirv-tools/libspirv.h @@ -309,6 +309,7 @@ typedef enum spv_ext_inst_type_t { SPV_EXT_INST_TYPE_DEBUGINFO, SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise @@ -515,6 +516,7 @@ typedef enum { SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. + SPV_ENV_MAX // Keep this as the last enum value. } spv_target_env; // SPIR-V Validator can be parameterized with the following Universal Limits. @@ -658,6 +660,11 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout( SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( spv_validator_options options, bool val); +// Records whether or not the validator should allow the LocalSizeId +// decoration where the environment otherwise would not allow it. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId( + spv_validator_options options, bool val); + // Creates an optimizer options object with default options. Returns a valid // options object. The object remains valid until it is passed into // |spvOptimizerOptionsDestroy|. diff --git a/third_party/spirv-tools/include/spirv-tools/libspirv.hpp b/third_party/spirv-tools/include/spirv-tools/libspirv.hpp index 0c31a18279..8dfb46b774 100644 --- a/third_party/spirv-tools/include/spirv-tools/libspirv.hpp +++ b/third_party/spirv-tools/include/spirv-tools/libspirv.hpp @@ -115,6 +115,12 @@ class ValidatorOptions { spvValidatorOptionsSetSkipBlockLayout(options_, val); } + // Enables LocalSizeId decorations where the environment would not otherwise + // allow them. + void SetAllowLocalSizeId(bool val) { + spvValidatorOptionsSetAllowLocalSizeId(options_, val); + } + // Records whether or not the validator should relax the rules on pointer // usage in logical addressing mode. // diff --git a/third_party/spirv-tools/include/spirv-tools/linter.hpp b/third_party/spirv-tools/include/spirv-tools/linter.hpp new file mode 100644 index 0000000000..52ed5a4672 --- /dev/null +++ b/third_party/spirv-tools/include/spirv-tools/linter.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LINTER_HPP_ +#define INCLUDE_SPIRV_TOOLS_LINTER_HPP_ + +#include "libspirv.hpp" + +namespace spvtools { + +// C++ interface for SPIR-V linting functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides a method for linting. +// +// Instances of this class provides basic thread-safety guarantee. +class Linter { + public: + explicit Linter(spv_target_env env); + + ~Linter(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns a reference to the registered message consumer. + const MessageConsumer& Consumer() const; + + bool Run(const uint32_t* binary, size_t binary_size); + + private: + struct Impl; + std::unique_ptr impl_; +}; +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LINTER_HPP_ diff --git a/third_party/spirv-tools/include/spirv-tools/optimizer.hpp b/third_party/spirv-tools/include/spirv-tools/optimizer.hpp index 700f59f70f..21059cbe35 100644 --- a/third_party/spirv-tools/include/spirv-tools/optimizer.hpp +++ b/third_party/spirv-tools/include/spirv-tools/optimizer.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "libspirv.hpp" @@ -27,7 +28,8 @@ namespace spvtools { namespace opt { class Pass; -} +struct DescriptorSetAndBinding; +} // namespace opt // C++ interface for SPIR-V optimization functionalities. It wraps the context // (including target environment and the corresponding SPIR-V grammar) and @@ -512,7 +514,12 @@ Optimizer::PassToken CreateDeadInsertElimPass(); // Conversion, which tends to cause cycles of dead code to be left after // Store/Load elimination passes are completed. These cycles cannot be // eliminated with standard dead code elimination. -Optimizer::PassToken CreateAggressiveDCEPass(); +// +// If |preserve_interface| is true, all non-io variables in the entry point +// interface are considered live and are not eliminated. This mode is needed +// by GPU-Assisted validation instrumentation, where a change in the interface +// is not allowed. +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface = false); // Creates a remove-unused-interface-variables pass. // Removes variables referenced on the |OpEntryPoint| instruction that are not @@ -699,8 +706,11 @@ Optimizer::PassToken CreateVectorDCEPass(); // Create a pass to reduce the size of loads. // This pass looks for loads of structures where only a few of its members are // used. It replaces the loads feeding an OpExtract with an OpAccessChain and -// a load of the specific elements. -Optimizer::PassToken CreateReduceLoadSizePass(); +// a load of the specific elements. The parameter is a threshold to determine +// whether we have to replace the load or not. If the ratio of the used +// components of the load is less than the threshold, we replace the load. +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold = 0.9); // Create a pass to combine chained access chains. // This pass looks for access chains fed by other access chains and combines @@ -823,6 +833,13 @@ Optimizer::PassToken CreateFixStorageClassPass(); // inclusive. Optimizer::PassToken CreateGraphicsRobustAccessPass(); +// Create a pass to replace a descriptor access using variable index. +// This pass replaces every access using a variable index to array variable +// |desc| that has a DescriptorSet and Binding decorations with a constant +// element of the array. In order to replace the access using a variable index +// with the constant element, it uses a switch statement. +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass(); + // Create descriptor scalar replacement pass. // This pass replaces every array variable |desc| that has a DescriptorSet and // Binding decorations with a new variable for each element of the array. @@ -854,6 +871,16 @@ Optimizer::PassToken CreateAmdExtToKhrPass(); // propagated into their final positions. Optimizer::PassToken CreateInterpolateFixupPass(); +// Creates a convert-to-sampled-image pass to convert images and/or +// samplers with given pairs of descriptor set and binding to sampled image. +// If a pair of an image and a sampler have the same pair of descriptor set and +// binding that is one of the given pairs, they will be converted to a sampled +// image. In addition, if only an image has the descriptor set and binding that +// is one of the given pairs, it will be converted to a sampled image as well. +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/third_party/spirv-tools/kokoro/linux-clang-ubsan/build.sh b/third_party/spirv-tools/kokoro/linux-clang-ubsan/build.sh new file mode 100755 index 0000000000..b5941e3400 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-ubsan/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh UBSAN clang cmake diff --git a/third_party/spirv-tools/kokoro/linux-clang-ubsan/continuous.cfg b/third_party/spirv-tools/kokoro/linux-clang-ubsan/continuous.cfg new file mode 100644 index 0000000000..cb5535e14d --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-ubsan/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-clang-ubsan/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-clang-ubsan/presubmit.cfg new file mode 100644 index 0000000000..029c74a52e --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-ubsan/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh" diff --git a/third_party/spirv-tools/kokoro/scripts/linux/build-docker.sh b/third_party/spirv-tools/kokoro/scripts/linux/build-docker.sh index ba21698745..8f76803c78 100755 --- a/third_party/spirv-tools/kokoro/scripts/linux/build-docker.sh +++ b/third_party/spirv-tools/kokoro/scripts/linux/build-docker.sh @@ -51,14 +51,14 @@ clone_if_missing https://github.com/google/googletest external/googlete pushd external/googletest; git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7; popd clone_if_missing https://github.com/google/effcee external/effcee --depth=1 clone_if_missing https://github.com/google/re2 external/re2 --depth=1 -clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0 +clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0.1 if [ $TOOL = "cmake" ]; then using cmake-3.17.2 using ninja-1.10.0 # Possible configurations are: - # ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW + # ASAN, UBSAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW BUILD_TYPE="Debug" if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ]; then BUILD_TYPE="RelWithDebInfo" @@ -69,6 +69,13 @@ if [ $TOOL = "cmake" ]; then if [ $CONFIG = "ASAN" ]; then ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null" [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } + elif [ $CONFIG = "UBSAN" ]; then + # UBSan requires RTTI, and by default UBSan does not exit when errors are + # encountered - additional compiler options are required to force this. + # The -DSPIRV_USE_SANITIZER=undefined option instructs SPIR-V Tools to be + # built with UBSan enabled. + ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=undefined -DENABLE_RTTI=ON -DCMAKE_C_FLAGS=-fno-sanitize-recover=all -DCMAKE_CXX_FLAGS=-fno-sanitize-recover=all" + [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } elif [ $CONFIG = "COVERAGE" ]; then ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" SKIP_TESTS="True" @@ -79,6 +86,10 @@ if [ $TOOL = "cmake" ]; then SKIP_TESTS="True" fi + if [ $COMPILER = "clang" ]; then + ADDITIONAL_CMAKE_FLAGS="$ADDITIONAL_CMAKE_FLAGS -DSPIRV_BUILD_LIBFUZZER_TARGETS=ON" + fi + clean_dir "$ROOT_DIR/build" cd "$ROOT_DIR/build" diff --git a/third_party/spirv-tools/kokoro/scripts/macos/build.sh b/third_party/spirv-tools/kokoro/scripts/macos/build.sh index 44c9a41247..46128238a6 100644 --- a/third_party/spirv-tools/kokoro/scripts/macos/build.sh +++ b/third_party/spirv-tools/kokoro/scripts/macos/build.sh @@ -36,7 +36,7 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf mkdir build && cd $SRC/build diff --git a/third_party/spirv-tools/kokoro/scripts/windows/build.bat b/third_party/spirv-tools/kokoro/scripts/windows/build.bat index c964320d7d..24e29ccf08 100644 --- a/third_party/spirv-tools/kokoro/scripts/windows/build.bat +++ b/third_party/spirv-tools/kokoro/scripts/windows/build.bat @@ -30,7 +30,7 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf :: ######################################### :: set up msvc build env @@ -93,4 +93,4 @@ zip -r install.zip install rm -rf %SRC%\build rm -rf %SRC%\external -exit /b 0 \ No newline at end of file +exit /b 0 diff --git a/third_party/spirv-tools/source/CMakeLists.txt b/third_party/spirv-tools/source/CMakeLists.txt index 5f59045671..ef6eb8e770 100644 --- a/third_party/spirv-tools/source/CMakeLists.txt +++ b/third_party/spirv-tools/source/CMakeLists.txt @@ -17,10 +17,10 @@ set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_sy set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py") -# For now, assume the DebugInfo grammar file is in the current directory. -# It might migrate to SPIRV-Headers. +# Pull in grammar files that have migrated to SPIRV-Headers set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") +set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.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 @@ -113,6 +113,9 @@ endmacro(spvtools_opencl_tables) macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json") + if(NOT EXISTS ${GRAMMAR_FILE}) + set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") + endif() add_custom_command(OUTPUT ${INSTS_FILE} COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} --extinst-vendor-grammar=${GRAMMAR_FILE} @@ -148,9 +151,11 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") spvtools_vendor_tables("debuginfo" "debuginfo" "") spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") +spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_") spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) spvtools_vimsyntax("unified1" "1.0") add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) @@ -211,6 +216,7 @@ add_subdirectory(opt) add_subdirectory(reduce) add_subdirectory(fuzz) add_subdirectory(link) +add_subdirectory(lint) set(SPIRV_SOURCES ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h @@ -226,6 +232,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h ${CMAKE_CURRENT_SOURCE_DIR}/binary.h ${CMAKE_CURRENT_SOURCE_DIR}/cfa.h + ${CMAKE_CURRENT_SOURCE_DIR}/common_debug_info.h ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h @@ -429,8 +436,10 @@ if(ENABLE_SPIRV_TOOLS_INSTALL) # Special config file for root library compared to other libs. file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n" - "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" - "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n") + "if(TARGET ${SPIRV_TOOLS})\n" + " set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" + " get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n" + "endif()\n") install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/third_party/spirv-tools/source/binary.cpp b/third_party/spirv-tools/source/binary.cpp index 090cccfecd..93d5da7aba 100644 --- a/third_party/spirv-tools/source/binary.cpp +++ b/third_party/spirv-tools/source/binary.cpp @@ -507,7 +507,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { assert(SpvOpSpecConstantOp == opcode); - if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) { + if (word > static_cast(SpvOp::SpvOpMax) || + grammar_.lookupSpecConstantOpcode(SpvOp(word))) { return diagnostic() << "Invalid " << spvOperandTypeStr(type) << ": " << word; } diff --git a/third_party/spirv-tools/source/common_debug_info.h b/third_party/spirv-tools/source/common_debug_info.h new file mode 100644 index 0000000000..ffa5d340c0 --- /dev/null +++ b/third_party/spirv-tools/source/common_debug_info.h @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_COMMON_DEBUG_INFO_HEADER_H_ +#define SOURCE_COMMON_DEBUG_INFO_HEADER_H_ + +// This enum defines the known common set of instructions that are the same +// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100. +// Note that NonSemantic.Shader.* instructions can still have slightly +// different encoding, as it does not use literals anywhere and only constants. +enum CommonDebugInfoInstructions { + CommonDebugInfoDebugInfoNone = 0, + CommonDebugInfoDebugCompilationUnit = 1, + CommonDebugInfoDebugTypeBasic = 2, + CommonDebugInfoDebugTypePointer = 3, + CommonDebugInfoDebugTypeQualifier = 4, + CommonDebugInfoDebugTypeArray = 5, + CommonDebugInfoDebugTypeVector = 6, + CommonDebugInfoDebugTypedef = 7, + CommonDebugInfoDebugTypeFunction = 8, + CommonDebugInfoDebugTypeEnum = 9, + CommonDebugInfoDebugTypeComposite = 10, + CommonDebugInfoDebugTypeMember = 11, + CommonDebugInfoDebugTypeInheritance = 12, + CommonDebugInfoDebugTypePtrToMember = 13, + CommonDebugInfoDebugTypeTemplate = 14, + CommonDebugInfoDebugTypeTemplateParameter = 15, + CommonDebugInfoDebugTypeTemplateTemplateParameter = 16, + CommonDebugInfoDebugTypeTemplateParameterPack = 17, + CommonDebugInfoDebugGlobalVariable = 18, + CommonDebugInfoDebugFunctionDeclaration = 19, + CommonDebugInfoDebugFunction = 20, + CommonDebugInfoDebugLexicalBlock = 21, + CommonDebugInfoDebugLexicalBlockDiscriminator = 22, + CommonDebugInfoDebugScope = 23, + CommonDebugInfoDebugNoScope = 24, + CommonDebugInfoDebugInlinedAt = 25, + CommonDebugInfoDebugLocalVariable = 26, + CommonDebugInfoDebugInlinedVariable = 27, + CommonDebugInfoDebugDeclare = 28, + CommonDebugInfoDebugValue = 29, + CommonDebugInfoDebugOperation = 30, + CommonDebugInfoDebugExpression = 31, + CommonDebugInfoDebugMacroDef = 32, + CommonDebugInfoDebugMacroUndef = 33, + CommonDebugInfoDebugImportedEntity = 34, + CommonDebugInfoDebugSource = 35, + CommonDebugInfoInstructionsMax = 0x7ffffff +}; + +#endif // SOURCE_COMMON_DEBUG_INFO_HEADER_H_ diff --git a/third_party/spirv-tools/source/ext_inst.cpp b/third_party/spirv-tools/source/ext_inst.cpp index 795cb0f3f7..812053ec0a 100644 --- a/third_party/spirv-tools/source/ext_inst.cpp +++ b/third_party/spirv-tools/source/ext_inst.cpp @@ -29,6 +29,7 @@ #include "debuginfo.insts.inc" #include "glsl.std.450.insts.inc" #include "nonsemantic.clspvreflection.insts.inc" +#include "nonsemantic.shader.debuginfo.100.insts.inc" #include "opencl.debuginfo.100.insts.inc" #include "opencl.std.insts.inc" @@ -55,6 +56,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = { debuginfo_entries}, {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + ARRAY_SIZE(nonsemantic_shader_debuginfo_100_entries), + nonsemantic_shader_debuginfo_100_entries}, {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, ARRAY_SIZE(nonsemantic_clspvreflection_entries), nonsemantic_clspvreflection_entries}, @@ -126,6 +130,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { if (!strcmp("OpenCL.DebugInfo.100", name)) { return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; } + if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + } if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; } @@ -139,6 +146,7 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { return true; } @@ -147,6 +155,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || type == SPV_EXT_INST_TYPE_DEBUGINFO) { return true; } diff --git a/third_party/spirv-tools/source/fuzz/CMakeLists.txt b/third_party/spirv-tools/source/fuzz/CMakeLists.txt index a80616623a..dd674dd04c 100644 --- a/third_party/spirv-tools/source/fuzz/CMakeLists.txt +++ b/third_party/spirv-tools/source/fuzz/CMakeLists.txt @@ -124,6 +124,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_swap_functions.h fuzzer_pass_toggle_access_chain_instruction.h fuzzer_pass_wrap_regions_in_selections.h + fuzzer_pass_wrap_vector_synonym.h fuzzer_util.h id_use_descriptor.h instruction_descriptor.h @@ -230,6 +231,7 @@ if(SPIRV_BUILD_FUZZER) transformation_vector_shuffle.h transformation_wrap_early_terminator_in_function.h transformation_wrap_region_in_selection.h + transformation_wrap_vector_synonym.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -319,6 +321,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_swap_functions.cpp fuzzer_pass_toggle_access_chain_instruction.cpp fuzzer_pass_wrap_regions_in_selections.cpp + fuzzer_pass_wrap_vector_synonym.cpp fuzzer_util.cpp id_use_descriptor.cpp instruction_descriptor.cpp @@ -423,6 +426,7 @@ if(SPIRV_BUILD_FUZZER) transformation_vector_shuffle.cpp transformation_wrap_early_terminator_in_function.cpp transformation_wrap_region_in_selection.cpp + transformation_wrap_vector_synonym.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc ) diff --git a/third_party/spirv-tools/source/fuzz/fuzzer.cpp b/third_party/spirv-tools/source/fuzz/fuzzer.cpp index fe88a55b02..9838e64fc8 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer.cpp @@ -90,6 +90,7 @@ #include "source/fuzz/fuzzer_pass_swap_functions.h" #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" #include "source/fuzz/pass_management/repeated_pass_manager.h" #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" @@ -109,7 +110,8 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, - spv_validator_options validator_options) + spv_validator_options validator_options, + bool ignore_inapplicable_transformations /* = true */) : consumer_(std::move(consumer)), enable_all_passes_(enable_all_passes), validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), @@ -123,7 +125,9 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, pass_instances_(), repeated_pass_recommender_(nullptr), repeated_pass_manager_(nullptr), - final_passes_() { + final_passes_(), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) { assert(ir_context_ && "IRContext is not initialized"); assert(fuzzer_context_ && "FuzzerContext is not initialized"); assert(transformation_context_ && "TransformationContext is not initialized"); @@ -214,6 +218,7 @@ Fuzzer::Fuzzer(std::unique_ptr ir_context, MaybeAddRepeatedPass( &pass_instances_); MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); // There is a theoretical possibility that no pass instances were created // until now; loop again if so. } while (pass_instances_.GetPasses().empty()); @@ -255,7 +260,8 @@ void Fuzzer::MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, fuzzer_context_->ChoosePercentage(percentage_chance_of_adding_pass)) { pass_instances->SetPass(MakeUnique( ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), - &transformation_sequence_out_, std::forward(extra_args)...)); + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); } } @@ -265,7 +271,8 @@ void Fuzzer::MaybeAddFinalPass(std::vector>* passes, if (enable_all_passes_ || fuzzer_context_->ChooseEven()) { passes->push_back(MakeUnique( ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), - &transformation_sequence_out_, std::forward(extra_args)...)); + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); } } diff --git a/third_party/spirv-tools/source/fuzz/fuzzer.h b/third_party/spirv-tools/source/fuzz/fuzzer.h index 1e7d6860c2..4c38977fb9 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer.h @@ -62,7 +62,8 @@ class Fuzzer { const std::vector& donor_suppliers, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, - spv_validator_options validator_options); + spv_validator_options validator_options, + bool ignore_inapplicable_transformations = true); // Disables copy/move constructor/assignment operations. Fuzzer(const Fuzzer&) = delete; @@ -187,6 +188,12 @@ class Fuzzer { // Some passes that it does not make sense to apply repeatedly, as they do not // unlock other passes. std::vector> final_passes_; + + // When set, this flag causes inapplicable transformations that should be + // applicable by construction to be ignored. This is useful when the fuzzer + // is being deployed at scale to test a SPIR-V processing tool, and where it + // is desirable to ignore bugs in the fuzzer itself. + const bool ignore_inapplicable_transformations_; }; } // namespace fuzz diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp index 9e9650f646..c2175425b2 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -42,6 +42,8 @@ const std::pair kChanceOfAddingAnotherPassToPassLoop = {50, const std::pair kChanceOfAddingAnotherStructField = {20, 90}; const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; +const std::pair KChanceOfAddingAtomicLoad = {30, 90}; +const std::pair KChanceOfAddingAtomicStore = {20, 90}; const std::pair kChanceOfAddingBitInstructionSynonym = {5, 20}; const std::pair @@ -162,6 +164,7 @@ const std::pair kChanceOfTogglingAccessChainInstruction = { 20, 90}; const std::pair kChanceOfWrappingRegionInSelection = {70, 90}; +const std::pair kChanceOfWrappingVectorSynonym = {10, 90}; // Default limits for various quantities that are chosen during fuzzing. // Keep them in alphabetical order. @@ -215,6 +218,10 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); chance_of_adding_array_or_struct_type_ = ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); + chance_of_adding_atomic_load_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicLoad); + chance_of_adding_atomic_store_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicStore); chance_of_adding_bit_instruction_synonym_ = ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); chance_of_adding_both_branches_when_replacing_opselect_ = @@ -369,6 +376,8 @@ FuzzerContext::FuzzerContext(std::unique_ptr random_generator, ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); chance_of_wrapping_region_in_selection_ = ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection); + chance_of_wrapping_vector_synonym_ = + ChooseBetweenMinAndMax(kChanceOfWrappingVectorSynonym); } FuzzerContext::~FuzzerContext() = default; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_context.h b/third_party/spirv-tools/source/fuzz/fuzzer_context.h index ef2cc2c548..77a5d40045 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_context.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_context.h @@ -142,6 +142,12 @@ class FuzzerContext { uint32_t GetChanceOfAddingArrayOrStructType() const { return chance_of_adding_array_or_struct_type_; } + uint32_t GetChanceOfAddingAtomicLoad() const { + return chance_of_adding_atomic_load_; + } + uint32_t GetChanceOfAddingAtomicStore() const { + return chance_of_adding_atomic_store_; + } uint32_t GetChanceOfAddingBitInstructionSynonym() const { return chance_of_adding_bit_instruction_synonym_; } @@ -381,6 +387,10 @@ class FuzzerContext { return chance_of_wrapping_region_in_selection_; } + uint32_t GetChanceOfWrappingVectorSynonym() const { + return chance_of_wrapping_vector_synonym_; + } + // Other functions to control transformations. Keep them in alphabetical // order. uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const { @@ -425,6 +435,9 @@ class FuzzerContext { uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) { return random_generator_->RandomUint32(number_of_components); } + uint32_t GetRandomIndexForWrappingVector(uint32_t vector_width) { + return random_generator_->RandomUint32(vector_width); + } int64_t GetRandomValueForStepConstantInLoop() { return random_generator_->RandomUint64(UINT64_MAX); } @@ -462,6 +475,9 @@ class FuzzerContext { // Ensure that the number of unused components is non-zero. return random_generator_->RandomUint32(max_unused_component_count) + 1; } + uint32_t GetWidthOfWrappingVector() { + return 2 + random_generator_->RandomUint32(3); + } bool GoDeeperInConstantObfuscation(uint32_t depth) { return go_deeper_in_constant_obfuscation_(depth, random_generator_.get()); } @@ -482,6 +498,8 @@ class FuzzerContext { uint32_t chance_of_adding_another_pass_to_pass_loop_; uint32_t chance_of_adding_another_struct_field_; uint32_t chance_of_adding_array_or_struct_type_; + uint32_t chance_of_adding_atomic_load_; + uint32_t chance_of_adding_atomic_store_; uint32_t chance_of_adding_bit_instruction_synonym_; uint32_t chance_of_adding_both_branches_when_replacing_opselect_; uint32_t chance_of_adding_composite_extract_; @@ -562,6 +580,7 @@ class FuzzerContext { uint32_t chance_of_swapping_functions_; uint32_t chance_of_toggling_access_chain_instruction_; uint32_t chance_of_wrapping_region_in_selection_; + uint32_t chance_of_wrapping_vector_synonym_; // Limits associated with various quantities for which random values are // chosen during fuzzing. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp index 9e95190c1d..d91482cc7e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -43,11 +43,14 @@ namespace fuzz { FuzzerPass::FuzzerPass(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : ir_context_(ir_context), transformation_context_(transformation_context), fuzzer_context_(fuzzer_context), - transformations_(transformations) {} + transformations_(transformations), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) {} FuzzerPass::~FuzzerPass() = default; @@ -183,9 +186,23 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( } void FuzzerPass::ApplyTransformation(const Transformation& transformation) { - assert(transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - "Transformation should be applicable by construction."); + if (ignore_inapplicable_transformations_) { + // If an applicable-by-construction transformation turns out to be + // inapplicable, this is a bug in the fuzzer. However, when deploying the + // fuzzer at scale for finding bugs in SPIR-V processing tools it is + // desirable to silently ignore such bugs. This code path caters for that + // scenario. + if (!transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + return; + } + } else { + // This code path caters for debugging bugs in the fuzzer, where an + // applicable-by-construction transformation turns out to be inapplicable. + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && + "Transformation should be applicable by construction."); + } transformation.Apply(GetIRContext(), GetTransformationContext()); auto transformation_message = transformation.ToMessage(); assert(transformation_message.transformation_case() != diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass.h index ec254855a2..2655b5406c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass.h @@ -33,7 +33,8 @@ class FuzzerPass { FuzzerPass(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); virtual ~FuzzerPass(); @@ -321,6 +322,10 @@ class FuzzerPass { TransformationContext* transformation_context_; FuzzerContext* fuzzer_context_; protobufs::TransformationSequence* transformations_; + // If set, then transformations that should be applicable by construction are + // still tested for applicability, and ignored if they turn out to be + // inapplicable. Otherwise, applicability by construction is asserted. + const bool ignore_inapplicable_transformations_; }; } // namespace fuzz diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp index c498642c06..39f193d940 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddAccessChains::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h index e80c2c6c71..5e209cd29f 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h @@ -28,7 +28,8 @@ class FuzzerPassAddAccessChains : public FuzzerPass { FuzzerPassAddAccessChains(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp index 78abf5be07..1b0d7b107f 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddBitInstructionSynonyms::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h index 28f95779cc..38d81aa7d4 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass { FuzzerPassAddBitInstructionSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp index 19f9902435..dbbec0ca0c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeExtract::Apply() { std::vector composite_synonyms; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h index 32ac190a53..e7ed18af31 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeExtract : public FuzzerPass { FuzzerPassAddCompositeExtract( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp index cf314d30bc..2ac12de42d 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeInserts::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h index 4d511f6c75..d9f42d588a 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeInserts : public FuzzerPass { FuzzerPassAddCompositeInserts( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp index 3dfbd690f7..af36ad06eb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCompositeTypes::Apply() { MaybeAddMissingVectorTypes(); diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h index 89d48f8ab0..f16c79e353 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h @@ -27,7 +27,8 @@ class FuzzerPassAddCompositeTypes : public FuzzerPass { FuzzerPassAddCompositeTypes( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp index b654927f6e..6551f49f38 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddCopyMemory::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h index 0f7db0c7e5..e258752ccc 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -27,7 +27,8 @@ class FuzzerPassAddCopyMemory : public FuzzerPass { FuzzerPassAddCopyMemory(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 8c166a2033..82d53ebba9 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -31,9 +31,10 @@ const size_t kMaxTransformationsInOnePass = 100U; FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadBlocks::Apply() { // We iterate over all blocks in the module collecting up those at which we diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h index a87c05c385..4567e87d0c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassAddDeadBlocks : public FuzzerPass { FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index 0c18da9099..c3664c87cd 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadBreaks::Apply() { // We first collect up lots of possibly-applicable transformations. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h index d1086fc4fb..361c3464e7 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -26,7 +26,8 @@ class FuzzerPassAddDeadBreaks : public FuzzerPass { FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 1ab40b7371..3881481137 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddDeadContinues::Apply() { // Consider every block in every function. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h index bf0009e444..4f1bd60144 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -23,10 +23,11 @@ namespace fuzz { // A fuzzer pass for adding dead continue edges to the module. class FuzzerPassAddDeadContinues : public FuzzerPass { public: - FuzzerPassAddDeadContinues( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddDeadContinues(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 15554b7a63..4bbded8e18 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -45,9 +45,10 @@ bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) { FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddEquationInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h index dbec5bacd1..dc9a27b3a6 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -29,7 +29,8 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass { FuzzerPassAddEquationInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp index 2240696b94..033f4a276e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddFunctionCalls::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h index 081510c1ad..80b03d7b94 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h @@ -24,10 +24,11 @@ namespace fuzz { // anywhere, and (b) any functions, from dead blocks. class FuzzerPassAddFunctionCalls : public FuzzerPass { public: - FuzzerPassAddFunctionCalls( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddFunctionCalls(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp index 067974134e..061f44d069 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddGlobalVariables::Apply() { SpvStorageClass variable_storage_class = SpvStorageClassPrivate; @@ -47,6 +48,10 @@ void FuzzerPassAddGlobalVariables::Apply() { // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // There are no basic types, so there is nothing this fuzzer pass can do. + return; + } // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h index 3745c5c387..1496646ee5 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h @@ -27,7 +27,8 @@ class FuzzerPassAddGlobalVariables : public FuzzerPass { FuzzerPassAddGlobalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp index c10db720e5..19661d103b 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -27,9 +27,10 @@ FuzzerPassAddImageSampleUnusedComponents:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddImageSampleUnusedComponents::Apply() { // SPIR-V module to help understand the transformation. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h index f5dea8bfbc..1a278931ca 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -28,7 +28,8 @@ class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { FuzzerPassAddImageSampleUnusedComponents( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp index 2e50da2628..ab91543b09 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddLoads::FuzzerPassAddLoads( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoads::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -38,18 +39,22 @@ void FuzzerPassAddLoads::Apply() { "The opcode of the instruction we might insert before must be " "the same as the opcode in the descriptor for the instruction"); - // Check whether it is legitimate to insert a load before this - // instruction. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { - return; - } - // Randomly decide whether to try inserting a load here. if (!GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingLoad())) { return; } + // Check whether it is legitimate to insert a load or atomic load before + // this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { + return; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicLoad, + inst_it)) { + return; + } + std::vector relevant_instructions = FindAvailableInstructions( function, block, inst_it, @@ -79,13 +84,53 @@ void FuzzerPassAddLoads::Apply() { return; } - // Choose a pointer at random, and create and apply a loading - // transformation based on it. - ApplyTransformation(TransformationLoad( - GetFuzzerContext()->GetFreshId(), + auto chosen_instruction = relevant_instructions[GetFuzzerContext()->RandomIndex( - relevant_instructions)] - ->result_id(), + relevant_instructions)]; + + bool is_atomic_load = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = static_cast( + GetIRContext() + ->get_def_use_mgr() + ->GetDef(chosen_instruction->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicLoad())) { + is_atomic_load = true; + + memory_scope_id = FindOrCreateConstant( + {SpvScopeInvocation}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. + ApplyTransformation(TransformationLoad( + GetFuzzerContext()->GetFreshId(), chosen_instruction->result_id(), + is_atomic_load, memory_scope_id, memory_semantics_id, instruction_descriptor)); }); } diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h index 3913c6246c..14460e7f95 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h @@ -26,7 +26,8 @@ class FuzzerPassAddLoads : public FuzzerPass { FuzzerPassAddLoads(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp index a50b0b072d..a4e739f8e0 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLocalVariables::Apply() { auto basic_type_ids_and_pointers = @@ -34,6 +35,10 @@ void FuzzerPassAddLocalVariables::Apply() { // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // The pass cannot do anything if there are no basic types. + return; + } // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h index d73dae29ce..c969ba06c7 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h @@ -27,7 +27,8 @@ class FuzzerPassAddLocalVariables : public FuzzerPass { FuzzerPassAddLocalVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp index 1cfed866ca..e8c9e964fb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoopPreheaders::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h index 8ac2dac8c2..2a13dbacea 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h @@ -30,7 +30,8 @@ class FuzzerPassAddLoopPreheaders : public FuzzerPass { FuzzerPassAddLoopPreheaders( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp index 69e0697f87..1a65ef6171 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp @@ -29,9 +29,10 @@ FuzzerPassAddLoopsToCreateIntConstantSynonyms:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() { std::vector constants; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h index 2eacef5fc7..14e1754236 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass { FuzzerPassAddLoopsToCreateIntConstantSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp index d0753100ca..8ae6e90aff 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddNoContractionDecorations::Apply() { // Consider every instruction in every block in every function. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h index 74212d8719..7aeb26deac 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -26,7 +26,8 @@ class FuzzerPassAddNoContractionDecorations : public FuzzerPass { FuzzerPassAddNoContractionDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp index be6e7ea1aa..73b6b0ac74 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddOpPhiSynonyms::Apply() { // Get a list of synonymous ids with the same type that can be used in the diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h index 9077118e25..c45c9a8a12 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h @@ -25,10 +25,11 @@ namespace fuzz { // synonymous with the others. class FuzzerPassAddOpPhiSynonyms : public FuzzerPass { public: - FuzzerPassAddOpPhiSynonyms( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassAddOpPhiSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp index 784653d8ec..1cb6a79d11 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddParameters::FuzzerPassAddParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddParameters::Apply() { // Compute type candidates for the new parameter. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h index 47dde390e3..c79f0e0969 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h @@ -30,7 +30,8 @@ class FuzzerPassAddParameters : public FuzzerPass { FuzzerPassAddParameters(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp index 58c6d1b2a0..b8c2b559eb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddRelaxedDecorations::Apply() { // Consider every instruction in every block in every function. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h index 723c4a0afe..51eb59424d 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -26,7 +26,8 @@ class FuzzerPassAddRelaxedDecorations : public FuzzerPass { FuzzerPassAddRelaxedDecorations( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp index f89428d3d4..606e4a634f 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAddStores::FuzzerPassAddStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddStores::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -38,16 +39,20 @@ void FuzzerPassAddStores::Apply() { "The opcode of the instruction we might insert before must be " "the same as the opcode in the descriptor for the instruction"); + // Randomly decide whether to try inserting a store here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingStore())) { + return; + } + // Check whether it is legitimate to insert a store before this // instruction. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst_it)) { return; } - - // Randomly decide whether to try inserting a store here. - if (!GetFuzzerContext()->ChoosePercentage( - GetFuzzerContext()->GetChanceOfAddingStore())) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicStore, + inst_it)) { return; } @@ -116,10 +121,49 @@ void FuzzerPassAddStores::Apply() { return; } - // Choose a value at random, and create and apply a storing - // transformation based on it and the pointer. + bool is_atomic_store = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = + static_cast(GetIRContext() + ->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicStore())) { + is_atomic_store = true; + + memory_scope_id = FindOrCreateConstant( + {SpvScopeInvocation}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. ApplyTransformation(TransformationStore( - pointer->result_id(), + pointer->result_id(), is_atomic_store, memory_scope_id, + memory_semantics_id, relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] ->result_id(), instruction_descriptor)); diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h index 9519c385bb..b18dde1fe7 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h @@ -28,7 +28,8 @@ class FuzzerPassAddStores : public FuzzerPass { FuzzerPassAddStores(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp index fd866f985b..1d188deb85 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddSynonyms::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h index ccf4a88671..b0c174bc4b 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h @@ -27,7 +27,8 @@ class FuzzerPassAddSynonyms : public FuzzerPass { FuzzerPassAddSynonyms(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp index e2eaaa0a48..a29d1d34d2 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAddVectorShuffleInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -75,7 +76,7 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() { ->IdIsIrrelevant(instruction->result_id()) && !fuzzerutil::CanMakeSynonymOf(ir_context, *GetTransformationContext(), - instruction)) { + *instruction)) { // If the id is irrelevant, we can use it since it will not // participate in DataSynonym fact. Otherwise, we should be // able to produce a synonym out of the id. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h index c5af374cd3..64d6608a13 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h @@ -26,7 +26,8 @@ class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass { FuzzerPassAddVectorShuffleInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp index 3c4f3803d7..94428f72cc 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustBranchWeights::Apply() { // For all OpBranchConditional instructions, diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h index de2f33d3b2..ae1ea3458b 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h @@ -28,7 +28,8 @@ class FuzzerPassAdjustBranchWeights : public FuzzerPass { FuzzerPassAdjustBranchWeights( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp index b38bd212c7..1c2bc8cf76 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustFunctionControls::Apply() { // Consider every function in the module. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h index 5ef32a180c..7a8c492793 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustFunctionControls : public FuzzerPass { FuzzerPassAdjustFunctionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp index 0f7cf037ec..fe855cade2 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustLoopControls::Apply() { // Consider every merge instruction in the module (via looking through all diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h index 4ca670b780..25e8304701 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustLoopControls : public FuzzerPass { FuzzerPassAdjustLoopControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp index 778d43ff0e..d2ff40e6fc 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustMemoryOperandsMasks::Apply() { // Consider every block in every function. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h index a068b8dd94..3197f91b34 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -27,7 +27,8 @@ class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { FuzzerPassAdjustMemoryOperandsMasks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp index d9b4e2939d..7d8e6b5782 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassAdjustSelectionControls::Apply() { // Consider every merge instruction in the module (via looking through all diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h index 6931f942c7..ac55de7925 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -26,7 +26,8 @@ class FuzzerPassAdjustSelectionControls : public FuzzerPass { FuzzerPassAdjustSelectionControls( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 712083132c..5c3b86b954 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassApplyIdSynonyms::Apply() { // Compute a closure of data synonym facts, to enrich the pool of synonyms @@ -197,7 +198,7 @@ bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes( GetIRContext(), base_object_type_id_2, dd2.index()); assert(type_id_1 && type_id_2 && "Data descriptors have invalid types"); - return TransformationReplaceIdWithSynonym::TypesAreCompatible( + return fuzzerutil::TypesAreCompatible( GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2); } diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h index b402b50924..3da9c5d69a 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -28,7 +28,8 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass { FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; 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 1a174cf16b..ff022fcca3 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 @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassConstructComposites::FuzzerPassConstructComposites( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassConstructComposites::Apply() { // Gather up the ids of all composite types, but skip block-/buffer @@ -61,7 +62,7 @@ void FuzzerPassConstructComposites::Apply() { return GetTransformationContext()->GetFactManager()->IdIsIrrelevant( inst->result_id()) || fuzzerutil::CanMakeSynonymOf(ir_context, - *GetTransformationContext(), inst); + *GetTransformationContext(), *inst); }); ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h index 333ac93160..8141398095 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h @@ -29,7 +29,8 @@ class FuzzerPassConstructComposites : public FuzzerPass { FuzzerPassConstructComposites( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp index 09b5368a4c..80cc2a5725 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassCopyObjects::FuzzerPassCopyObjects( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassCopyObjects::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h index 461cd97927..2fb5a53f98 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h @@ -26,7 +26,8 @@ class FuzzerPassCopyObjects : public FuzzerPass { FuzzerPassCopyObjects(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp index 2f2ed50a72..5bdf6973eb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -45,10 +45,11 @@ FuzzerPassDonateModules::FuzzerPassDonateModules( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, - const std::vector& donor_suppliers) + bool ignore_inapplicable_transformations, + std::vector donor_suppliers) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations), - donor_suppliers_(donor_suppliers) {} + transformations, ignore_inapplicable_transformations), + donor_suppliers_(std::move(donor_suppliers)) {} void FuzzerPassDonateModules::Apply() { // If there are no donor suppliers, this fuzzer pass is a no-op. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h index 1581a8a4a7..924dd358cd 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h @@ -31,7 +31,8 @@ class FuzzerPassDonateModules : public FuzzerPass { opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, - const std::vector& donor_suppliers); + bool ignore_inapplicable_transformations, + std::vector donor_suppliers); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp index e08d65b121..3a9a7e6648 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp @@ -25,9 +25,10 @@ FuzzerPassDuplicateRegionsWithSelections:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassDuplicateRegionsWithSelections::Apply() { // Iterate over all of the functions in the module. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h index 7cb1197db0..74343a668e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h @@ -29,7 +29,8 @@ class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass { FuzzerPassDuplicateRegionsWithSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp index e25dcbc3eb..5bf0461d31 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassExpandVectorReductions::Apply() { for (auto& function : *GetIRContext()->module()) { @@ -46,7 +47,7 @@ void FuzzerPassExpandVectorReductions::Apply() { // It must be able to make a synonym of |instruction|. if (!fuzzerutil::CanMakeSynonymOf( - GetIRContext(), *GetTransformationContext(), &instruction)) { + GetIRContext(), *GetTransformationContext(), instruction)) { continue; } diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h index ed0225df1d..c0673e12fc 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h @@ -28,7 +28,8 @@ class FuzzerPassExpandVectorReductions : public FuzzerPass { FuzzerPassExpandVectorReductions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp index 84da7a749e..70fa6a12ca 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassFlattenConditionalBranches::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h index e7d7dc36ab..abfbc3c1b0 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h @@ -25,7 +25,8 @@ class FuzzerPassFlattenConditionalBranches : public FuzzerPass { FuzzerPassFlattenConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp index 405afd8b16..4024096f46 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassInlineFunctions::FuzzerPassInlineFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInlineFunctions::Apply() { // |function_call_instructions| are the instructions that will be inlined. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h index a6ed8cad55..c4e0b83186 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h @@ -28,7 +28,8 @@ class FuzzerPassInlineFunctions : public FuzzerPass { FuzzerPassInlineFunctions(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp index 675cae9ade..d8780ff747 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -27,9 +27,10 @@ FuzzerPassInterchangeSignednessOfIntegerOperands:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() { assert(!GetFuzzerContext()->IsWgslCompatible() && diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h index 11b8fb6e8b..6a10e89720 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -32,7 +32,8 @@ class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass { FuzzerPassInterchangeSignednessOfIntegerOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp index 5d0a222377..2c16cd5bb7 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( opt::Instruction* declaration) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h index 012f03d3b5..bb4a4dc759 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -33,7 +33,8 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { FuzzerPassInterchangeZeroLikeConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp index 542748e95d..130f75029e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassInvertComparisonOperators::Apply() { GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h index 5f015c28dc..d0d09edb6b 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -27,7 +27,8 @@ class FuzzerPassInvertComparisonOperators : public FuzzerPass { FuzzerPassInvertComparisonOperators( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp index 7bf07a4952..b755d2357e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMakeVectorOperationsDynamic::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h index da27825a41..b6a46c7887 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h @@ -27,7 +27,8 @@ class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass { FuzzerPassMakeVectorOperationsDynamic( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; 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 index a8ec784c9c..702006292c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMergeBlocks::Apply() { // First we populate a sequence of transformations that we might consider 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 index 66cf4c64c1..46e257f127 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h @@ -26,7 +26,8 @@ class FuzzerPassMergeBlocks : public FuzzerPass { FuzzerPassMergeBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp index ee82eca5e3..220f707b5c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp @@ -26,9 +26,10 @@ namespace fuzz { FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMergeFunctionReturns::Apply() { // The pass might add new functions to the module (due to wrapping early diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h index ddd2c9dc38..a799b8be07 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h @@ -31,7 +31,8 @@ class FuzzerPassMergeFunctionReturns : public FuzzerPass { FuzzerPassMergeFunctionReturns( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp index f021a978ac..bbe05409b0 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassMutatePointers::FuzzerPassMutatePointers( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassMutatePointers::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h index 5ef6a2ab4a..45c6a7cc5c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h @@ -26,7 +26,8 @@ class FuzzerPassMutatePointers : public FuzzerPass { FuzzerPassMutatePointers(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 32318e8965..f60c1b4cdf 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -30,9 +30,10 @@ namespace fuzz { FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h index 82b1092c70..30e64d28cb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -30,7 +30,8 @@ class FuzzerPassObfuscateConstants : public FuzzerPass { FuzzerPassObfuscateConstants( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; 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 index bfde61f7c3..b90c12dc34 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassOutlineFunctions::Apply() { std::vector original_functions; 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 index 45e52ff44e..d80dc4a74e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h @@ -24,10 +24,11 @@ namespace fuzz { // flow graph into their own functions. class FuzzerPassOutlineFunctions : public FuzzerPass { public: - FuzzerPassOutlineFunctions( - opt::IRContext* ir_context, TransformationContext* transformation_context, - FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + FuzzerPassOutlineFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp index 769c49f069..e55fae32ff 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -22,9 +22,10 @@ namespace fuzz { FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteBlocks::Apply() { // For now we do something very simple: we randomly decide whether to move a diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h index e40178e656..39326c2b40 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassPermuteBlocks : public FuzzerPass { FuzzerPassPermuteBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index 9a61bea0f9..a8035b90cf 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteFunctionParameters::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h index a4bf2ca52d..c5b6ad437a 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -32,7 +32,8 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass { FuzzerPassPermuteFunctionParameters( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp index a4e19e3b19..f8b9b45034 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp @@ -29,9 +29,11 @@ namespace fuzz { FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} // Here we call parent constructor. + transformations, ignore_inapplicable_transformations) { +} // Here we call parent constructor. void FuzzerPassPermuteFunctionVariables::Apply() { // Permuting OpVariable instructions in each function. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h index 47f1de281f..2ebc15f237 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h @@ -26,7 +26,8 @@ class FuzzerPassPermuteFunctionVariables : public FuzzerPass { FuzzerPassPermuteFunctionVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp index f17e018778..3ef76be476 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermuteInstructions::Apply() { // We are iterating over all instructions in all basic blocks. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h index 027101dd44..b7ccbccb84 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h @@ -27,7 +27,8 @@ class FuzzerPassPermuteInstructions : public FuzzerPass { FuzzerPassPermuteInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp index f2cc52314e..5fac98161b 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp @@ -28,9 +28,10 @@ namespace fuzz { FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPermutePhiOperands::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h index 7999956288..30a9d4f5fa 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h @@ -27,7 +27,8 @@ class FuzzerPassPermutePhiOperands : public FuzzerPass { FuzzerPassPermutePhiOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp index 89e14370f5..4c46dcd34e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPropagateInstructionsDown::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h index a2a0aac828..18f0165495 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h @@ -26,7 +26,8 @@ class FuzzerPassPropagateInstructionsDown : public FuzzerPass { FuzzerPassPropagateInstructionsDown( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp index 8cd7437bdb..5e45da83fd 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPropagateInstructionsUp::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h index b89be48dd5..0cb8396be4 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h @@ -27,7 +27,8 @@ class FuzzerPassPropagateInstructionsUp : public FuzzerPass { FuzzerPassPropagateInstructionsUp( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 6aebac6687..a6c07b4b34 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassPushIdsThroughVariables::Apply() { ForEachInstructionWithInstructionDescriptor( @@ -102,7 +103,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { ->IdIsIrrelevant(instruction->result_id()) && !fuzzerutil::CanMakeSynonymOf(ir_context, *GetTransformationContext(), - instruction)) { + *instruction)) { return false; } diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h index 53008ee223..71b6949251 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h @@ -28,7 +28,8 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass { FuzzerPassPushIdsThroughVariables( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp index 8a83d3bc51..467e613493 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp @@ -29,9 +29,10 @@ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() { std::vector instructions_for_transformation; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h index 0e29a6c6b0..268655f97a 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h @@ -29,7 +29,8 @@ class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass { FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp index a516f3d45b..995657cc13 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp @@ -28,9 +28,10 @@ FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() { // OpKill can only be used as a terminator in a function that is guaranteed diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h index ab7e00e5c5..cdbb66f266 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass { FuzzerPassReplaceBranchesFromDeadBlocksWithExits( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp index f17339a9c8..af1aacee34 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceCopyMemoriesWithLoadsStores:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h index cffe1cb92c..7d954abebc 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { FuzzerPassReplaceCopyMemoriesWithLoadsStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp index 24f2255b74..d0992a3ffb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceCopyObjectsWithStoresLoads:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h index e7b11ce107..2ffc00b651 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { FuzzerPassReplaceCopyObjectsWithStoresLoads( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp index 7e9d7baaa1..4d55ae840d 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceIrrelevantIds::Apply() { // Keep track of the irrelevant ids. This includes all the ids that are diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h index 1dc6b5d386..80f8eb8626 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceIrrelevantIds : public FuzzerPass { FuzzerPassReplaceIrrelevantIds( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index 0890c2fe70..445dbfe1a0 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -26,9 +26,10 @@ FuzzerPassReplaceLinearAlgebraInstructions:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { // For each instruction, checks whether it is a linear algebra instruction. In diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h index 5d2f204287..5734bf1f2c 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass { FuzzerPassReplaceLinearAlgebraInstructions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp index f2cf80fae4..38ac048bf3 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -27,9 +27,10 @@ FuzzerPassReplaceLoadsStoresWithCopyMemories:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() { // We look for matching pairs of instructions OpLoad and diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h index f30fc2b77f..f6209fc058 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -28,7 +28,8 @@ class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass { FuzzerPassReplaceLoadsStoresWithCopyMemories( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp index b0a3d57c60..ea90a7ac73 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp @@ -24,9 +24,10 @@ FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() { // Keep a vector of the transformations to apply. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h index a2bc1886a9..b01e242f64 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass { FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp index 10bb90adbd..72ed0936a5 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp @@ -27,9 +27,10 @@ FuzzerPassReplaceOpSelectsWithConditionalBranches:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() { // Keep track of the instructions that we want to replace. We need to collect diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h index ec743890eb..174962ee0d 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass { FuzzerPassReplaceOpSelectsWithConditionalBranches( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp index 5c256bb057..7fb7b0d27f 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceParameterWithGlobal::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h index 2ae494696b..4eb508660a 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { FuzzerPassReplaceParameterWithGlobal( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp index c045e19f7c..f029316ec1 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -27,9 +27,10 @@ namespace fuzz { FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassReplaceParamsWithStruct::Apply() { for (const auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h index f17f520713..3af7367aed 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h @@ -27,7 +27,8 @@ class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { FuzzerPassReplaceParamsWithStruct( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp index 7b493559bc..40a4151d32 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSplitBlocks::Apply() { // Gather up pointers to all the blocks in the module. We are then able to diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h index 58f10ddbc0..b1b9424002 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h @@ -27,7 +27,8 @@ class FuzzerPassSplitBlocks : public FuzzerPass { FuzzerPassSplitBlocks(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp index 27fadd173a..dce65f087e 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapCommutableOperands::Apply() { auto context = GetIRContext(); diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h index 93de1728d8..13d8fb6ed4 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -28,7 +28,8 @@ class FuzzerPassSwapCommutableOperands : public FuzzerPass { FuzzerPassSwapCommutableOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp index b145b3bcad..f8bf111dcf 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp @@ -27,9 +27,10 @@ FuzzerPassSwapBranchConditionalOperands:: opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapBranchConditionalOperands::Apply() { ForEachInstructionWithInstructionDescriptor( diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h index 0137f38b97..7f71f9b1f5 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h @@ -27,7 +27,8 @@ class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass { FuzzerPassSwapBranchConditionalOperands( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp index 171f6cb82c..8eeec850eb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp @@ -23,9 +23,10 @@ namespace fuzz { FuzzerPassSwapFunctions::FuzzerPassSwapFunctions( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassSwapFunctions::Apply() { // Collect all function ids in a module. diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h index ac551f6970..7af527f0d5 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h @@ -26,7 +26,8 @@ class FuzzerPassSwapFunctions : public FuzzerPass { FuzzerPassSwapFunctions(opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp index e5afd9ee7e..ac2b1565dc 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -24,9 +24,10 @@ namespace fuzz { FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassToggleAccessChainInstruction::Apply() { auto context = GetIRContext(); diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h index ff2f5d457a..f0b6166b89 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -27,7 +27,8 @@ class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { FuzzerPassToggleAccessChainInstruction( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; }; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp index 66bbcd81ac..3a3b12c151 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp @@ -25,9 +25,10 @@ namespace fuzz { FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations) + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, ignore_inapplicable_transformations) {} void FuzzerPassWrapRegionsInSelections::Apply() { for (auto& function : *GetIRContext()->module()) { diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h index 822c308f43..fc3d7df9b3 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h @@ -27,7 +27,8 @@ class FuzzerPassWrapRegionsInSelections : public FuzzerPass { FuzzerPassWrapRegionsInSelections( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, - protobufs::TransformationSequence* transformations); + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); void Apply() override; diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp new file mode 100644 index 0000000000..35adcfec00 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_composite_construct.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassWrapVectorSynonym::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + + // Randomly decide whether to wrap it to a vector operation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) { + return; + } + + // The transformation is not applicable if the instruction has missing + // result id, type id, or is not supported type. + if (!TransformationWrapVectorSynonym::IsInstructionSupported( + GetIRContext(), *instruction_iterator)) { + return; + } + + // It must be valid to insert an OpCompositeConstruct instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeConstruct, instruction_iterator)) { + return; + } + + // Get the scalar operands from the original instruction. + opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(0)); + opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(1)); + + // We need to be able to make a synonym of the scalar operation's result + // id, as well as the operand ids (for example, they cannot be + // irrelevant). + if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(), + *GetTransformationContext(), + *instruction_iterator)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), *operand1)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), *operand2)) { + return; + } + + // Get a random vector size from 2 to 4. + uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); + + // Randomly choose a position that target ids should be placed at. + // The position is in range [0, n - 1], where n is the size of the + // vector. + uint32_t position = + GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); + + // Stores the ids of scalar constants. + std::vector vec1_components; + std::vector vec2_components; + + // Populate components based on vector type and size. + for (uint32_t i = 0; i < vector_size; ++i) { + if (i == position) { + vec1_components.emplace_back(operand1->result_id()); + vec2_components.emplace_back(operand2->result_id()); + } else { + vec1_components.emplace_back( + FindOrCreateZeroConstant(operand1->type_id(), true)); + vec2_components.emplace_back( + FindOrCreateZeroConstant(operand2->type_id(), true)); + } + } + + // Add two OpCompositeConstruct to the module with result id returned. + // The added vectors may have different types, for instance if the + // scalar instruction operates on integers with differing sign. + + // Add the first OpCompositeConstruct that wraps the id of the first + // operand. + uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + FindOrCreateVectorType(operand1->type_id(), vector_size), + vec1_components, instruction_descriptor, result_id1)); + + // Add the second OpCompositeConstruct that wraps the id of the second + // operand. + uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + FindOrCreateVectorType(operand2->type_id(), vector_size), + vec2_components, instruction_descriptor, result_id2)); + + // The result of the vector instruction that + // TransformationWrapVectorSynonym will create should be a vector of the + // right size, with the scalar instruction's result type as its element + // type. This can be distinct from the types of the operands, if the + // scalar instruction adds two signed integers and stores the result in + // an unsigned id, for example. A transformation is applied to add the + // right type to the module. + FindOrCreateVectorType(instruction_iterator->type_id(), vector_size); + + // Apply transformation to do vector operation and add synonym between + // the result vector id and the id of the original instruction. + ApplyTransformation(TransformationWrapVectorSynonym( + instruction_iterator->result_id(), result_id1, result_id2, + GetFuzzerContext()->GetFreshId(), position)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.h new file mode 100644 index 0000000000..514583832e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly wrap a scalar operation into a vector operation. +class FuzzerPassWrapVectorSynonym : public FuzzerPass { + public: + FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp index 4beb67fde6..1d368a9fa6 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -305,34 +305,34 @@ bool CanInsertOpcodeBeforeInstruction( bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - opt::Instruction* inst) { - if (inst->opcode() == SpvOpSampledImage) { + const opt::Instruction& inst) { + if (inst.opcode() == SpvOpSampledImage) { // The SPIR-V data rules say that only very specific instructions may // may consume the result id of an OpSampledImage, and this excludes the // instructions that are used for making synonyms. return false; } - if (!inst->HasResultId()) { + if (!inst.HasResultId()) { // We can only make a synonym of an instruction that generates an id. return false; } if (transformation_context.GetFactManager()->IdIsIrrelevant( - inst->result_id())) { + inst.result_id())) { // An irrelevant id can't be a synonym of anything. return false; } - if (!inst->type_id()) { + if (!inst.type_id()) { // We can only make a synonym of an instruction that has a type. return false; } - auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id()); + auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst.type_id()); if (type_inst->opcode() == SpvOpTypeVoid) { // We only make synonyms of instructions that define objects, and an object // cannot have void type. return false; } if (type_inst->opcode() == SpvOpTypePointer) { - switch (inst->opcode()) { + switch (inst.opcode()) { case SpvOpConstantNull: case SpvOpUndef: // We disallow making synonyms of null or undefined pointers. This is @@ -348,7 +348,7 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, // not decorated analogously, using the original object vs. its synonymous // form may not be equivalent. return ir_context->get_decoration_mgr() - ->GetDecorationsFor(inst->result_id(), true) + ->GetDecorationsFor(inst.result_id(), true) .empty(); } @@ -455,6 +455,30 @@ uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, } } +SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class) { + switch (storage_class) { + case SpvStorageClassWorkgroup: + return SpvMemorySemanticsWorkgroupMemoryMask; + + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + return SpvMemorySemanticsUniformMemoryMask; + + case SpvStorageClassCrossWorkgroup: + return SpvMemorySemanticsCrossWorkgroupMemoryMask; + + case SpvStorageClassAtomicCounter: + return SpvMemorySemanticsAtomicCounterMemoryMask; + + case SpvStorageClassImage: + return SpvMemorySemanticsImageMemoryMask; + + default: + return SpvMemorySemanticsMaskNone; + } +} + bool IsValid(const opt::IRContext* context, spv_validator_options validator_options, MessageConsumer consumer) { @@ -502,8 +526,12 @@ bool IsValidAndWellFormed(const opt::IRContext* ir_context, // this is a useful aid to debugging. std::unordered_map unique_ids; bool found_duplicate = false; - ir_context->module()->ForEachInst([&consumer, &found_duplicate, + ir_context->module()->ForEachInst([&consumer, &found_duplicate, ir_context, &unique_ids](opt::Instruction* inst) { + (void)ir_context; // Only used in an assertion; keep release-mode compilers + // happy. + assert(inst->context() == ir_context && + "Instruction has wrong IR context."); if (unique_ids.count(inst->unique_id()) != 0) { consumer(SPV_MSG_INFO, nullptr, {}, "Two instructions have the same unique id (set a breakpoint to " @@ -787,11 +815,38 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, return absolute_index - inst.NumOperands() + inst.NumInOperands(); } -bool IsNullConstantSupported(const opt::analysis::Type& type) { - return type.AsBool() || type.AsInteger() || type.AsFloat() || - type.AsMatrix() || type.AsVector() || type.AsArray() || - type.AsStruct() || type.AsPointer() || type.AsEvent() || - type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue(); +bool IsNullConstantSupported(opt::IRContext* ir_context, + const opt::Instruction& type_inst) { + switch (type_inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeDeviceEvent: + case SpvOpTypeEvent: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeQueue: + case SpvOpTypeReserveId: + case SpvOpTypeVector: + case SpvOpTypeStruct: + return true; + case SpvOpTypePointer: + // Null pointers are allowed if the VariablePointers capability is + // enabled, or if the VariablePointersStorageBuffer capability is enabled + // and the pointer type has StorageBuffer as its storage class. + if (ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + return true; + } + if (ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)) { + return type_inst.GetSingleWordInOperand(0) == + SpvStorageClassStorageBuffer; + } + return false; + default: + return false; + } } bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( @@ -1616,6 +1671,44 @@ bool IdUseCanBeReplaced(opt::IRContext* ir_context, return false; } + if (ir_context->get_feature_mgr()->HasCapability(SpvCapabilityShader)) { + // With the Shader capability, memory scope and memory semantics operands + // are required to be constants, so they cannot be replaced arbitrarily. + switch (use_instruction->opcode()) { + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + if (use_in_operand_index == 1 || use_in_operand_index == 2) { + return false; + } + break; + case SpvOpAtomicCompareExchange: + if (use_in_operand_index == 1 || use_in_operand_index == 2 || + use_in_operand_index == 3) { + return false; + } + break; + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: + case SpvOpAtomicFAddEXT: + assert(false && "Not allowed with the Shader capability."); + default: + break; + } + } + return true; } @@ -1925,6 +2018,93 @@ opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, }); } +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all +// opcodes that are agnostic to signedness of operands to function. +// This is not exhaustive yet. +bool IsAgnosticToSignednessOfOperand(SpvOp opcode, + uint32_t use_in_operand_index) { + switch (opcode) { + case SpvOpSNegate: + case SpvOpNot: + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpSDiv: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + return true; + + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT, + // AtomicFloat64AddEXT. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index == 1 || use_in_operand_index == 2; + + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1 && use_in_operand_index <= 3; + + case SpvOpAtomicLoad: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicFlagTestAndSet: // Capability Kernel. + case SpvOpAtomicFlagClear: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1; + + case SpvOpAccessChain: + // The signedness of indices does not matter. + return use_in_operand_index > 0; + + default: + // Conservatively assume that the id cannot be swapped in other + // instructions. + return false; + } +} + +bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2) { + assert(ir_context->get_type_mgr()->GetType(type_id_1) && + ir_context->get_type_mgr()->GetType(type_id_2) && + "Type ids are invalid"); + + return type_id_1 == type_id_2 || + (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && + fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); +} + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_util.h b/third_party/spirv-tools/source/fuzz/fuzzer_util.h index b956507a91..54aa14a2fb 100644 --- a/third_party/spirv-tools/source/fuzz/fuzzer_util.h +++ b/third_party/spirv-tools/source/fuzz/fuzzer_util.h @@ -118,7 +118,7 @@ bool CanInsertOpcodeBeforeInstruction( // does not participate in IdIsIrrelevant fact. bool CanMakeSynonymOf(opt::IRContext* ir_context, const TransformationContext& transformation_context, - opt::Instruction* inst); + const opt::Instruction& inst); // Determines whether the given type is a composite; that is: an array, matrix, // struct or vector. @@ -169,6 +169,10 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction, uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, opt::IRContext* ir_context); +// Returns memory semantics mask for specific storage class. +SpvMemorySemanticsMask GetMemorySemanticsForStorageClass( + SpvStorageClass storage_class); + // Returns true if and only if |context| is valid, according to the validator // instantiated with |validator_options|. |consumer| is used for error // reporting. @@ -273,8 +277,10 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, uint32_t absolute_index); // Returns true if and only if |type| is one of the types for which it is legal -// to have an OpConstantNull value. -bool IsNullConstantSupported(const opt::analysis::Type& type); +// to have an OpConstantNull value. This may depend on the capabilities declared +// in |context|. +bool IsNullConstantSupported(opt::IRContext* context, + const opt::Instruction& type); // Returns true if and only if the SPIR-V version being used requires that // global variables accessed in the static call graph of an entry point need @@ -598,6 +604,21 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, uint32_t function_id); +// Returns true if the instruction with opcode |opcode| does not change its +// behaviour depending on the signedness of the operand at +// |use_in_operand_index|. +// Assumes that the operand must be the id of an integer scalar or vector. +bool IsAgnosticToSignednessOfOperand(SpvOp opcode, + uint32_t use_in_operand_index); + +// Returns true if |type_id_1| and |type_id_2| represent compatible types +// given the context of the instruction with |opcode| (i.e. we can replace +// an operand of |opcode| of the first type with an id of the second type +// and vice-versa). +bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2); + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h index 80ac0875e5..da61fdabc7 100644 --- a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h @@ -73,6 +73,7 @@ #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" namespace spvtools { namespace fuzz { @@ -168,6 +169,7 @@ class RepeatedPassInstances { REPEATED_PASS_INSTANCE(SplitBlocks); REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands); REPEATED_PASS_INSTANCE(WrapRegionsInSelections); + REPEATED_PASS_INSTANCE(WrapVectorSynonym); #undef REPEATED_PASS_INSTANCE public: diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp index a933848d4c..6c61c0d4e4 100644 --- a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp @@ -351,6 +351,12 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations( pass_instances_->GetReplaceIrrelevantIds(), pass_instances_->GetFlattenConditionalBranches()}); } + if (&pass == pass_instances_->GetWrapVectorSynonym()) { + // This transformation introduces synonym facts and irrelevant ids. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + assert(false && "Unreachable: every fuzzer pass should be dealt with."); return {}; } diff --git a/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h b/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h index eb8cb14514..46c218814e 100644 --- a/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h +++ b/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -23,8 +23,11 @@ #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wunknown-warning-option" // Must come first +#pragma clang diagnostic ignored "-Wreserved-identifier" #pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsuggest-destructor-override" +#pragma clang diagnostic ignored "-Wunused-parameter" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" diff --git a/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index 657e807616..cba9397b54 100644 --- a/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -559,6 +559,7 @@ message Transformation { TransformationExpandVectorReduction expand_vector_reduction = 85; TransformationSwapFunctionVariables swap_function_variables = 86; TransformationSwapTwoFunctions swap_two_functions = 87; + TransformationWrapVectorSynonym wrap_vector_synonym = 88; // Add additional option using the next available number. } } @@ -1568,17 +1569,26 @@ message TransformationInvertComparisonOperator { message TransformationLoad { - // Transformation that adds an OpLoad instruction from a pointer into an id. + // Transformation that adds an OpLoad or OpAtomicLoad instruction from a pointer into an id. - // The result of the load instruction + // The result of the load instruction. uint32 fresh_id = 1; - // The pointer to be loaded from + // The pointer to be loaded from. uint32 pointer_id = 2; + // True if and only if the load should be atomic. + bool is_atomic = 3; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 4; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 5; + // A descriptor for an instruction in a block before which the new OpLoad - // instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 3; + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; } @@ -2235,17 +2245,26 @@ message TransformationSplitBlock { message TransformationStore { - // Transformation that adds an OpStore instruction of an id to a pointer. + // Transformation that adds an OpStore or OpAtomicStore instruction of an id to a pointer. - // The pointer to be stored to + // The pointer to be stored to. uint32 pointer_id = 1; - // The value to be stored - uint32 value_id = 2; + // True if and only if the load should be atomic. + bool is_atomic = 2; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 3; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 4; + + // The value to be stored. + uint32 value_id = 5; // A descriptor for an instruction in a block before which the new OpStore - // instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 3; + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; } @@ -2371,3 +2390,39 @@ message TransformationWrapRegionInSelection { bool branch_condition = 3; } + +message TransformationWrapVectorSynonym { + // A transformation that wraps an arithmetic operation into a vector operation + // and get the result of the original operation from the corresponding index. + // For instance, for this transformation, an scalar operation between two scalars: + // define op ∈ {+, -, *} + // c = a op b + // + // requires the availability of two vectors: + // + // va = vector(..., a, ...) + // vb = vector(..., b, ...) + // + // where a and b are in the same position i in each of their correponding vector + // and a is synonymous with va[i] and b is synonymous with vb[i]. + // + // The transformation then add an instruction vc = va op vb where c is synonymous + // with vc[i]. + + // The result if of the original scalar operation instruction. + uint32 instruction_id = 1; + + // The result id for the first vector that contains the first value of the scalar operation. + uint32 vector_operand1 = 2; + + // The result id for the second vector that contains the second value of the scalar operation. + uint32 vector_operand2 = 3; + + // A fresh id for the resulted vector from the addition of the first and second vector. + uint32 fresh_id = 4; + + // The position in the vector where the value of original instruction is located. Must be in + // the corresponding vector range. + uint32 scalar_position = 5; + +} diff --git a/third_party/spirv-tools/source/fuzz/transformation.cpp b/third_party/spirv-tools/source/fuzz/transformation.cpp index 4ea0c773c6..70a302b752 100644 --- a/third_party/spirv-tools/source/fuzz/transformation.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation.cpp @@ -104,6 +104,7 @@ #include "source/fuzz/transformation_vector_shuffle.h" #include "source/fuzz/transformation_wrap_early_terminator_in_function.h" #include "source/fuzz/transformation_wrap_region_in_selection.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" #include "source/util/make_unique.h" namespace spvtools { @@ -382,6 +383,9 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kWrapRegionInSelection: return MakeUnique( message.wrap_region_in_selection()); + case protobufs::Transformation::TransformationCase::kWrapVectorSynonym: + return MakeUnique( + message.wrap_vector_synonym()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: assert(false && "An unset transformation was encountered."); return nullptr; diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp index 32544e6deb..c0f736709d 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp @@ -35,14 +35,14 @@ bool TransformationAddConstantNull::IsApplicable( if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { return false; } - auto type = context->get_type_mgr()->GetType(message_.type_id()); + auto type = context->get_def_use_mgr()->GetDef(message_.type_id()); // The type must exist. if (!type) { return false; } // The type must be one of the types for which null constants are allowed, // according to the SPIR-V spec. - return fuzzerutil::IsNullConstantSupported(*type); + return fuzzerutil::IsNullConstantSupported(context, *type); } void TransformationAddConstantNull::Apply( 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 index eb390ea0df..ec0574a44b 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.cpp @@ -15,6 +15,7 @@ #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/fuzzer_util.h" +#include "source/opt/reflect.h" namespace spvtools { namespace fuzz { @@ -35,9 +36,11 @@ bool TransformationAddGlobalUndef::IsApplicable( if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } - auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); - // The type must exist, and must not be a function type. - return type && !type->AsFunction(); + auto type = ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + // The type must exist, and must not be a function or pointer type. + return type != nullptr && opt::IsTypeInst(type->opcode()) && + type->opcode() != SpvOpTypeFunction && + type->opcode() != SpvOpTypePointer; } void TransformationAddGlobalUndef::Apply( 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 index 37542c3fda..fff1ad9d6c 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.h +++ b/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.h @@ -31,7 +31,7 @@ class TransformationAddGlobalUndef : public Transformation { 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 + // - |message_.type_id| must be the id of a non-function, non-pointer type bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp index a1949fbcea..69269e5e34 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp @@ -151,7 +151,8 @@ bool TransformationAddSynonym::IsInstructionValid( return false; } - if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) { + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *inst)) { return false; } diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp b/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp index 0cd2308bc8..2d8e59918a 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp @@ -297,7 +297,7 @@ void TransformationCompositeConstruct::AddDataSynonymFacts( composite_type->AsVector() && component_type->AsVector(); if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(component))) { + *ir_context->get_def_use_mgr()->GetDef(component))) { // We can't make a synonym of this component, so we skip on to the next // component. In the case where we're packing a vector into a vector we // have to skip as many components of the resulting vectors as there are diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp b/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp index 647cd74e6d..0fbd4e1b19 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp @@ -125,7 +125,7 @@ void TransformationCompositeExtract::AddDataSynonymFacts( // or if the result id into which we are extracting is irrelevant. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || + *ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || transformation_context->GetFactManager()->IdIsIrrelevant( message_.fresh_id())) { return; diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp b/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp index 05162bfc9e..60fa562815 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp @@ -219,9 +219,9 @@ void TransformationCompositeInsert::AddDataSynonymFacts( continue; } current_index.push_back(i); - if (fuzzerutil::CanMakeSynonymOf( - ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) { + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef( + message_.composite_id()))) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.fresh_id(), current_index), MakeDataDescriptor(message_.composite_id(), current_index)); @@ -235,7 +235,7 @@ void TransformationCompositeInsert::AddDataSynonymFacts( // synonymous with the result of the insert instruction at the given index. if (fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.object_id(), {}), MakeDataDescriptor(message_.fresh_id(), index)); diff --git a/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp b/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp index dee1207fa6..a80becd97b 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -309,9 +309,9 @@ void TransformationDuplicateRegionWithSelection::Apply( // Construct the merge block. std::unique_ptr merge_block = - MakeUnique(MakeUnique(opt::Instruction( + MakeUnique(MakeUnique( ir_context, SpvOpLabel, 0, message_.merge_label_fresh_id(), - opt::Instruction::OperandList()))); + opt::Instruction::OperandList())); // Get the maps from the protobuf. std::map original_label_to_duplicate_label = diff --git a/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp b/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp index 9938706698..bafcf9292c 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp @@ -129,7 +129,7 @@ void TransformationExpandVectorReduction::Apply( // If it's possible to make a synonym of |instruction|, then add the fact that // the last |logical_instruction| is a synonym of |instruction|. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, - instruction)) { + *instruction)) { transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(logical_instruction.result_id(), {}), MakeDataDescriptor(instruction->result_id(), {})); diff --git a/third_party/spirv-tools/source/fuzz/transformation_load.cpp b/third_party/spirv-tools/source/fuzz/transformation_load.cpp index e22f8dd292..bf48d996bc 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_load.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_load.cpp @@ -24,10 +24,15 @@ TransformationLoad::TransformationLoad(protobufs::TransformationLoad message) : message_(std::move(message)) {} TransformationLoad::TransformationLoad( - uint32_t fresh_id, uint32_t pointer_id, + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, const protobufs::InstructionDescriptor& instruction_to_insert_before) { message_.set_fresh_id(fresh_id); message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); + *message_.mutable_instruction_to_insert_before() = instruction_to_insert_before; } @@ -68,11 +73,98 @@ bool TransformationLoad::IsApplicable( if (!insert_before) { return false; } - // ... and it must be legitimate to insert a store before it. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { + // ... and it must be legitimate to insert a load before it. + if (!message_.is_atomic() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { return false; } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAtomicLoad, insert_before)) { + return false; + } + + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != SpvOpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != SpvOpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of SpvScopeInvocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (memory_scope_const_value != SpvScopeInvocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + // The pointer needs to be available at the insertion point. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.pointer_id()); @@ -80,22 +172,46 @@ bool TransformationLoad::IsApplicable( void TransformationLoad::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( - ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto new_instruction = MakeUnique( - ir_context, SpvOpLoad, result_type, message_.fresh_id(), - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); - auto new_instruction_ptr = new_instruction.get(); - insert_before->InsertBefore(std::move(new_instruction)); - // Inform the def-use manager about the new instruction and record its basic - // block. - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); - ir_context->set_instr_block(new_instruction_ptr, - ir_context->get_instr_block(insert_before)); + if (message_.is_atomic()) { + // OpAtomicLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } else { + // OpLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } } protobufs::Transformation TransformationLoad::ToMessage() const { diff --git a/third_party/spirv-tools/source/fuzz/transformation_load.h b/third_party/spirv-tools/source/fuzz/transformation_load.h index d10b0073d8..57b4a53516 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_load.h +++ b/third_party/spirv-tools/source/fuzz/transformation_load.h @@ -28,11 +28,20 @@ class TransformationLoad : public Transformation { explicit TransformationLoad(protobufs::TransformationLoad message); TransformationLoad( - uint32_t fresh_id, uint32_t pointer_id, + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, const protobufs::InstructionDescriptor& instruction_to_insert_before); // - |message_.fresh_id| must be fresh // - |message_.pointer_id| must be the id of a pointer + // - |message_.is_atomic| must be true if want to work with OpAtomicLoad + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // SpvScopeInvocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. // - The pointer must not be OpConstantNull or OpUndef // - |message_.instruction_to_insert_before| must identify an instruction // before which it is valid to insert an OpLoad, and where diff --git a/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp b/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp index ff52516545..55a57a1518 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp @@ -155,7 +155,7 @@ void TransformationPushIdThroughVariable::Apply( // We should be able to create a synonym of |value_id| if it's not irrelevant. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, - value_instruction) && + *value_instruction) && !transformation_context->GetFactManager()->IdIsIrrelevant( message_.value_synonym_id())) { // Adds the fact that |message_.value_synonym_id| diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp index 92ce751dd5..8d21d233a4 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -65,9 +65,10 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( // If the id of interest and the synonym are scalar or vector integer // constants with different signedness, their use can only be swapped if the // instruction is agnostic to the signedness of the operand. - if (!TypesAreCompatible(ir_context, use_instruction->opcode(), - message_.id_use_descriptor().in_operand_index(), - type_id_of_interest, type_id_synonym)) { + if (!fuzzerutil::TypesAreCompatible( + ir_context, use_instruction->opcode(), + message_.id_use_descriptor().in_operand_index(), type_id_of_interest, + type_id_synonym)) { return false; } @@ -109,59 +110,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() return result; } -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all -// opcodes that are agnostic to signedness of operands to function. -// This is not exhaustive yet. -bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand( - SpvOp opcode, uint32_t use_in_operand_index) { - switch (opcode) { - case SpvOpSNegate: - case SpvOpNot: - case SpvOpIAdd: - case SpvOpISub: - case SpvOpIMul: - case SpvOpSDiv: - case SpvOpSRem: - case SpvOpSMod: - case SpvOpShiftRightLogical: - case SpvOpShiftRightArithmetic: - case SpvOpShiftLeftLogical: - case SpvOpBitwiseOr: - case SpvOpBitwiseXor: - case SpvOpBitwiseAnd: - case SpvOpIEqual: - case SpvOpINotEqual: - case SpvOpULessThan: - case SpvOpSLessThan: - case SpvOpUGreaterThan: - case SpvOpSGreaterThan: - case SpvOpULessThanEqual: - case SpvOpSLessThanEqual: - case SpvOpUGreaterThanEqual: - case SpvOpSGreaterThanEqual: - return true; - case SpvOpAccessChain: - // The signedness of indices does not matter. - return use_in_operand_index > 0; - default: - // Conservatively assume that the id cannot be swapped in other - // instructions. - return false; - } -} - -bool TransformationReplaceIdWithSynonym::TypesAreCompatible( - opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index, - uint32_t type_id_1, uint32_t type_id_2) { - assert(ir_context->get_type_mgr()->GetType(type_id_1) && - ir_context->get_type_mgr()->GetType(type_id_2) && - "Type ids are invalid"); - - return type_id_1 == type_id_2 || - (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && - fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); -} - std::unordered_set TransformationReplaceIdWithSynonym::GetFreshIds() const { return std::unordered_set(); diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h index 1ac636b4b5..4570fcefec 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h @@ -52,22 +52,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::Transformation ToMessage() const override; - // Returns true if |type_id_1| and |type_id_2| represent compatible types - // given the context of the instruction with |opcode| (i.e. we can replace - // an operand of |opcode| of the first type with an id of the second type - // and vice-versa). - static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, - uint32_t use_in_operand_index, - uint32_t type_id_1, uint32_t type_id_2); - private: - // Returns true if the instruction with opcode |opcode| does not change its - // behaviour depending on the signedness of the operand at - // |use_in_operand_index|. - // Assumes that the operand must be the id of an integer scalar or vector. - static bool IsAgnosticToSignednessOfOperand(SpvOp opcode, - uint32_t use_in_operand_index); - protobufs::TransformationReplaceIdWithSynonym message_; }; diff --git a/third_party/spirv-tools/source/fuzz/transformation_store.cpp b/third_party/spirv-tools/source/fuzz/transformation_store.cpp index f8c6d013ba..c00cd345d7 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_store.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_store.cpp @@ -24,9 +24,13 @@ TransformationStore::TransformationStore(protobufs::TransformationStore message) : message_(std::move(message)) {} TransformationStore::TransformationStore( - uint32_t pointer_id, uint32_t value_id, + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, const protobufs::InstructionDescriptor& instruction_to_insert_before) { message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); message_.set_value_id(value_id); *message_.mutable_instruction_to_insert_before() = instruction_to_insert_before; @@ -70,8 +74,12 @@ bool TransformationStore::IsApplicable( return false; } // ... and it must be legitimate to insert a store before it. - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, - insert_before)) { + if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpStore, insert_before)) { + return false; + } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAtomicStore, insert_before)) { return false; } @@ -102,6 +110,87 @@ bool TransformationStore::IsApplicable( return false; } + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != SpvOpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != SpvOpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != SpvOpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of SpvScopeInvocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (memory_scope_const_value != SpvScopeInvocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + // The value needs to be available at the insertion point. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, message_.value_id()); @@ -109,20 +198,43 @@ bool TransformationStore::IsApplicable( void TransformationStore::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto new_instruction = MakeUnique( - ir_context, SpvOpStore, 0, 0, - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); - auto new_instruction_ptr = new_instruction.get(); - insert_before->InsertBefore(std::move(new_instruction)); - // Inform the def-use manager about the new instruction and record its basic - // block. - ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); - ir_context->set_instr_block(new_instruction_ptr, - ir_context->get_instr_block(insert_before)); + if (message_.is_atomic()) { + // OpAtomicStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpAtomicStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + + } else { + // OpStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } } protobufs::Transformation TransformationStore::ToMessage() const { diff --git a/third_party/spirv-tools/source/fuzz/transformation_store.h b/third_party/spirv-tools/source/fuzz/transformation_store.h index 18ba1d7d22..638713bb77 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_store.h +++ b/third_party/spirv-tools/source/fuzz/transformation_store.h @@ -28,12 +28,21 @@ class TransformationStore : public Transformation { explicit TransformationStore(protobufs::TransformationStore message); TransformationStore( - uint32_t pointer_id, uint32_t value_id, + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, const protobufs::InstructionDescriptor& instruction_to_insert_before); // - |message_.pointer_id| must be the id of a pointer // - The pointer type must not have read-only storage class // - The pointer must not be OpConstantNull or OpUndef + // - |message_.is_atomic| must be true if want to work with OpAtomicStore. + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // SpvScopeInvocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. // - |message_.value_id| must be an instruction result id that has the same // type as the pointee type of |message_.pointer_id| // - |message_.instruction_to_insert_before| must identify an instruction diff --git a/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp b/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp index ac0e3ccba2..742a2c8056 100644 --- a/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp +++ b/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp @@ -204,7 +204,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts( // Check that the first vector can participate in data synonym facts. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { continue; } descriptor_for_source_component = @@ -213,7 +213,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts( // Check that the second vector can participate in data synonym facts. if (!fuzzerutil::CanMakeSynonymOf( ir_context, *transformation_context, - ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { + *ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { continue; } auto index_into_vector_2 = diff --git a/third_party/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.cpp new file mode 100644 index 0000000000..490bcd7827 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message) + : message_(std::move(message)) {} + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2, + uint32_t fresh_id, uint32_t pos) { + message_.set_instruction_id(instruction_id); + message_.set_vector_operand1(vector_operand1); + message_.set_vector_operand2(vector_operand2); + message_.set_fresh_id(fresh_id); + message_.set_scalar_position(pos); +} + +bool TransformationWrapVectorSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + const opt::Instruction* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + + // |instruction_id| must refer to an existing instruction. + if (instruction == nullptr) { + return false; + } + + if (!IsInstructionSupported(ir_context, *instruction)) { + return false; + } + + // It must be possible to make a synonym of the result id of the scalar + // operation + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *instruction)) { + return false; + } + + // |vector_operand1| and |vector_operand2| must exist. + auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1()); + auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2()); + + if (vec1 == nullptr || vec2 == nullptr) { + return false; + } + + // The 2 vectors must have compatible vector types. + auto vec1_type_id = vec1->type_id(); + auto vec2_type_id = vec2->type_id(); + + for (auto operand_index : {0, 1}) { + if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(), + operand_index, vec1_type_id, + vec2_type_id)) { + return false; + } + } + + auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id); + if (vec1_type->opcode() != SpvOpTypeVector) { + return false; + } + + // A suitable vector for the result type of the new vector instruction must + // exist in the module. This is a vector of the right length, whose element + // type matches the result type of the scalar instruction. + uint32_t vector_size = vec1_type->GetSingleWordInOperand(1); + if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(), + vector_size)) { + return false; + } + + // |scalar_position| needs to be a non-negative integer less than the vector + // length. + // OpTypeVector instruction has the component count at index 2. + if (message_.scalar_position() >= ir_context->get_def_use_mgr() + ->GetDef(vec1_type_id) + ->GetSingleWordInOperand(1)) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand1(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand2(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) { + return false; + } + + return true; +} + +void TransformationWrapVectorSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Create an instruction descriptor for the original instruction. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + auto destination_block = ir_context->get_instr_block(instruction); + + // Populate input operand list with two vectors for vector operation. + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}}); + + // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1 + // %result_id2. + auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef( + fuzzerutil::GetTypeId(ir_context, message_.vector_operand1())); + uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1); + auto vec_type_id = fuzzerutil::MaybeGetVectorType( + ir_context, instruction->type_id(), vector_size); + auto new_instruction = MakeUnique( + ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(), + std::move(in_operands)); + auto new_instruction_ptr = new_instruction.get(); + instruction->InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, destination_block); + + // Add |fresh_id| to id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Add synonyms between |fresh_id| and |instruction_id|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}), + MakeDataDescriptor(message_.instruction_id(), {})); +} + +protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_wrap_vector_synonym() = message_; + return result; +} + +std::unordered_set TransformationWrapVectorSynonym::GetFreshIds() + const { + return std::unordered_set{message_.fresh_id()}; +} + +bool TransformationWrapVectorSynonym::IsInstructionSupported( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + if (!instruction.result_id() || !instruction.type_id()) { + return false; + } + auto type_instruction = + ir_context->get_def_use_mgr()->GetDef(instruction.type_id()); + + if ((type_instruction->opcode() != SpvOpTypeInt && + type_instruction->opcode() != SpvOpTypeFloat)) { + return false; + } + + switch (instruction.opcode()) { + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpFAdd: + case SpvOpFSub: + case SpvOpFMul: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.h new file mode 100644 index 0000000000..94437fe597 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.h @@ -0,0 +1,80 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationWrapVectorSynonym : public Transformation { + public: + explicit TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message); + + TransformationWrapVectorSynonym(uint32_t instruction_id, + uint32_t vector_operand1, + uint32_t vector_operand2, uint32_t fresh_id, + uint32_t pos); + // - |instruction_id| must be the id of a supported arithmetic operation + // and must be relevant. + // - |vector_operand1| and |vector_operand2| represents the result ids of the + // two vector operands. + // - |fresh_id| is an unused id that will be used as a result id of the + // created instruction. + // - |vector_operand1| and |vector_operand2| must have compatible vector types + // that are supported by this transformation. + // - |pos| is an index of the operands of |instruction_id| in the + // |vector_operand1| and |vector_operand2|. It must be less than the size + // of those vector operands. + // - A vector type with the same width as the types of the vector operands, + // and element type matching the type of |instruction_id|, must exist in the + // module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a new instruction before the |instruction_id| with |fresh_id| + // result id and |instruction_id|'s opcode. The added instruction has + // two operands: |vector_operand1| and |vector_operand2| and its type + // id is equal to the type ids of those operands. A new fact is added + // to the fact manager specifying that |fresh_id[pos]| is synonymous + // to |instruction_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + // Checks whether the instruction given is supported by the transformation. + // A valid instruction must: + // - has both result id and type id. + // - is a supported scalar operation instruction. + // - has a supported type that is either int or float. + static bool IsInstructionSupported(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + private: + protobufs::TransformationWrapVectorSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/link/linker.cpp b/third_party/spirv-tools/source/link/linker.cpp index 8da4a98d88..c5ca5625d6 100644 --- a/third_party/spirv-tools/source/link/linker.cpp +++ b/third_party/spirv-tools/source/link/linker.cpp @@ -34,6 +34,7 @@ #include "source/opt/pass_manager.h" #include "source/opt/remove_duplicates_pass.h" #include "source/opt/type_manager.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/make_unique.h" #include "spirv-tools/libspirv.hpp" @@ -207,7 +208,7 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, header->magic_number = SpvMagicNumber; header->version = version; - header->generator = 17u; + header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); header->bound = max_id_bound; header->reserved = 0u; diff --git a/third_party/spirv-tools/source/lint/CMakeLists.txt b/third_party/spirv-tools/source/lint/CMakeLists.txt new file mode 100644 index 0000000000..1feae3f94d --- /dev/null +++ b/third_party/spirv-tools/source/lint/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (c) 2021 Google LLC. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_LINT_SOURCES + divergence_analysis.h + lints.h + + linter.cpp + divergence_analysis.cpp + lint_divergent_derivatives.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib. + add_definitions(/MP4) +endif() + +add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-lint) +target_include_directories(SPIRV-Tools-lint + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-lint + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) +# We need the internals of spirv-opt. +target_link_libraries(SPIRV-Tools-lint + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-lint PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-lint) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-lint EXPORT SPIRV-Tools-lintTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-lint PACKAGE_DIR) + install(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-lint) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-lintConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/third_party/spirv-tools/source/lint/divergence_analysis.cpp b/third_party/spirv-tools/source/lint/divergence_analysis.cpp new file mode 100644 index 0000000000..b5a72b459d --- /dev/null +++ b/third_party/spirv-tools/source/lint/divergence_analysis.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/lint/divergence_analysis.h" + +#include "source/opt/basic_block.h" +#include "source/opt/control_dependence.h" +#include "source/opt/dataflow.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace lint { + +void DivergenceAnalysis::EnqueueSuccessors(opt::Instruction* inst) { + // Enqueue control dependents of block, if applicable. + // There are two ways for a dependence source to be updated: + // 1. control -> control: source block is marked divergent. + // 2. data -> control: branch condition is marked divergent. + uint32_t block_id; + if (inst->IsBlockTerminator()) { + block_id = context().get_instr_block(inst)->id(); + } else if (inst->opcode() == SpvOpLabel) { + block_id = inst->result_id(); + opt::BasicBlock* bb = context().cfg()->block(block_id); + // Only enqueue phi instructions, as other uses don't affect divergence. + bb->ForEachPhiInst([this](opt::Instruction* phi) { Enqueue(phi); }); + } else { + opt::ForwardDataFlowAnalysis::EnqueueUsers(inst); + return; + } + if (!cd_.HasBlock(block_id)) { + return; + } + for (const spvtools::opt::ControlDependence& dep : + cd_.GetDependenceTargets(block_id)) { + opt::Instruction* target_inst = + context().cfg()->block(dep.target_bb_id())->GetLabelInst(); + Enqueue(target_inst); + } +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::Visit( + opt::Instruction* inst) { + if (inst->opcode() == SpvOpLabel) { + return VisitBlock(inst->result_id()); + } else { + return VisitInstruction(inst); + } +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitBlock(uint32_t id) { + if (!cd_.HasBlock(id)) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel& cur_level = divergence_[id]; + if (cur_level == DivergenceLevel::kDivergent) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel orig = cur_level; + for (const spvtools::opt::ControlDependence& dep : + cd_.GetDependenceSources(id)) { + if (divergence_[dep.source_bb_id()] > cur_level) { + cur_level = divergence_[dep.source_bb_id()]; + divergence_source_[id] = dep.source_bb_id(); + } else if (dep.source_bb_id() != 0) { + uint32_t condition_id = dep.GetConditionID(*context().cfg()); + DivergenceLevel dep_level = divergence_[condition_id]; + // Check if we are along the chain of unconditional branches starting from + // the branch target. + if (follow_unconditional_branches_[dep.branch_target_bb_id()] != + follow_unconditional_branches_[dep.target_bb_id()]) { + // We must have reconverged in order to reach this block. + // Promote partially uniform to divergent. + if (dep_level == DivergenceLevel::kPartiallyUniform) { + dep_level = DivergenceLevel::kDivergent; + } + } + if (dep_level > cur_level) { + cur_level = dep_level; + divergence_source_[id] = condition_id; + divergence_dependence_source_[id] = dep.source_bb_id(); + } + } + } + return cur_level > orig ? VisitResult::kResultChanged + : VisitResult::kResultFixed; +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitInstruction( + opt::Instruction* inst) { + if (inst->IsBlockTerminator()) { + // This is called only when the condition has changed, so return changed. + return VisitResult::kResultChanged; + } + if (!inst->HasResultId()) { + return VisitResult::kResultFixed; + } + uint32_t id = inst->result_id(); + DivergenceLevel& cur_level = divergence_[id]; + if (cur_level == DivergenceLevel::kDivergent) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel orig = cur_level; + cur_level = ComputeInstructionDivergence(inst); + return cur_level > orig ? VisitResult::kResultChanged + : VisitResult::kResultFixed; +} + +DivergenceAnalysis::DivergenceLevel +DivergenceAnalysis::ComputeInstructionDivergence(opt::Instruction* inst) { + // TODO(kuhar): Check to see if inst is decorated with Uniform or UniformId + // and use that to short circuit other checks. Uniform is for subgroups which + // would satisfy derivative groups too. UniformId takes a scope, so if it is + // subgroup or greater it could satisfy derivative group and + // Device/QueueFamily could satisfy fully uniform. + uint32_t id = inst->result_id(); + // Handle divergence roots. + if (inst->opcode() == SpvOpFunctionParameter) { + divergence_source_[id] = 0; + return divergence_[id] = DivergenceLevel::kDivergent; + } else if (inst->IsLoad()) { + spvtools::opt::Instruction* var = inst->GetBaseAddress(); + if (var->opcode() != SpvOpVariable) { + // Assume divergent. + divergence_source_[id] = 0; + return DivergenceLevel::kDivergent; + } + DivergenceLevel ret = ComputeVariableDivergence(var); + if (ret > DivergenceLevel::kUniform) { + divergence_source_[inst->result_id()] = 0; + } + return divergence_[id] = ret; + } + // Get the maximum divergence of the operands. + DivergenceLevel ret = DivergenceLevel::kUniform; + inst->ForEachInId([this, inst, &ret](const uint32_t* op) { + if (!op) return; + if (divergence_[*op] > ret) { + divergence_source_[inst->result_id()] = *op; + ret = divergence_[*op]; + } + }); + divergence_[inst->result_id()] = ret; + return ret; +} + +DivergenceAnalysis::DivergenceLevel +DivergenceAnalysis::ComputeVariableDivergence(opt::Instruction* var) { + uint32_t type_id = var->type_id(); + spvtools::opt::analysis::Pointer* type = + context().get_type_mgr()->GetType(type_id)->AsPointer(); + assert(type != nullptr); + uint32_t def_id = var->result_id(); + DivergenceLevel ret; + switch (type->storage_class()) { + case SpvStorageClassFunction: + case SpvStorageClassGeneric: + case SpvStorageClassAtomicCounter: + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassOutput: + case SpvStorageClassWorkgroup: + case SpvStorageClassImage: // Image atomics probably aren't uniform. + case SpvStorageClassPrivate: + ret = DivergenceLevel::kDivergent; + break; + case SpvStorageClassInput: + ret = DivergenceLevel::kDivergent; + // If this variable has a Flat decoration, it is partially uniform. + // TODO(kuhar): Track access chain indices and also consider Flat members + // of a structure. + context().get_decoration_mgr()->WhileEachDecoration( + def_id, SpvDecorationFlat, [&ret](const opt::Instruction&) { + ret = DivergenceLevel::kPartiallyUniform; + return false; + }); + break; + case SpvStorageClassUniformConstant: + // May be a storage image which is also written to; mark those as + // divergent. + if (!var->IsVulkanStorageImage() || var->IsReadOnlyPointer()) { + ret = DivergenceLevel::kUniform; + } else { + ret = DivergenceLevel::kDivergent; + } + break; + case SpvStorageClassUniform: + case SpvStorageClassPushConstant: + case SpvStorageClassCrossWorkgroup: // Not for shaders; default uniform. + default: + ret = DivergenceLevel::kUniform; + break; + } + return ret; +} + +void DivergenceAnalysis::Setup(opt::Function* function) { + // TODO(kuhar): Run functions called by |function| so we can detect + // reconvergence caused by multiple returns. + cd_.ComputeControlDependenceGraph( + *context().cfg(), *context().GetPostDominatorAnalysis(function)); + context().cfg()->ForEachBlockInPostOrder( + function->entry().get(), [this](const opt::BasicBlock* bb) { + uint32_t id = bb->id(); + if (bb->terminator() == nullptr || + bb->terminator()->opcode() != SpvOpBranch) { + follow_unconditional_branches_[id] = id; + } else { + uint32_t target_id = bb->terminator()->GetSingleWordInOperand(0); + // Target is guaranteed to have been visited before us in postorder. + follow_unconditional_branches_[id] = + follow_unconditional_branches_[target_id]; + } + }); +} + +std::ostream& operator<<(std::ostream& os, + DivergenceAnalysis::DivergenceLevel level) { + switch (level) { + case DivergenceAnalysis::DivergenceLevel::kUniform: + return os << "uniform"; + case DivergenceAnalysis::DivergenceLevel::kPartiallyUniform: + return os << "partially uniform"; + case DivergenceAnalysis::DivergenceLevel::kDivergent: + return os << "divergent"; + default: + return os << ""; + } +} + +} // namespace lint +} // namespace spvtools diff --git a/third_party/spirv-tools/source/lint/divergence_analysis.h b/third_party/spirv-tools/source/lint/divergence_analysis.h new file mode 100644 index 0000000000..4d595ec282 --- /dev/null +++ b/third_party/spirv-tools/source/lint/divergence_analysis.h @@ -0,0 +1,163 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ +#define SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/control_dependence.h" +#include "source/opt/dataflow.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace lint { + +// Computes the static divergence level for blocks (control flow) and values. +// +// A value is uniform if all threads that execute it are guaranteed to have the +// same value. Similarly, a value is partially uniform if this is true only +// within each derivative group. If neither apply, it is divergent. +// +// Control flow through a block is uniform if for any possible execution and +// point in time, all threads are executing it, or no threads are executing it. +// In particular, it is never possible for some threads to be inside the block +// and some threads not executing. +// TODO(kuhar): Clarify the difference between uniform, divergent, and +// partially-uniform execution in this analysis. +// +// Caveat: +// As we use control dependence to determine how divergence is propagated, this +// analysis can be overly permissive when the merge block for a conditional +// branch or switch is later than (strictly postdominates) the expected merge +// block, which is the immediate postdominator. However, this is not expected to +// be a problem in practice, given that SPIR-V is generally output by compilers +// and other automated tools, which would assign the earliest possible merge +// block, rather than written by hand. +// TODO(kuhar): Handle late merges. +class DivergenceAnalysis : public opt::ForwardDataFlowAnalysis { + public: + // The tightest (most uniform) level of divergence that can be determined + // statically for a value or control flow for a block. + // + // The values are ordered such that A > B means that A is potentially more + // divergent than B. + // TODO(kuhar): Rename |PartiallyUniform' to something less confusing. For + // example, the enum could be based on scopes. + enum class DivergenceLevel { + // The value or control flow is uniform across the entire invocation group. + kUniform = 0, + // The value or control flow is uniform across the derivative group, but not + // the invocation group. + kPartiallyUniform = 1, + // The value or control flow is not statically uniform. + kDivergent = 2, + }; + + DivergenceAnalysis(opt::IRContext& context) + : ForwardDataFlowAnalysis(context, LabelPosition::kLabelsAtEnd) {} + + // Returns the divergence level for the given value (non-label instructions), + // or control flow for the given block. + DivergenceLevel GetDivergenceLevel(uint32_t id) { + auto it = divergence_.find(id); + if (it == divergence_.end()) { + return DivergenceLevel::kUniform; + } + return it->second; + } + + // Returns the divergence source for the given id. The following types of + // divergence flows from A to B are possible: + // + // data -> data: A is used as an operand in the definition of B. + // data -> control: B is control-dependent on a branch with condition A. + // control -> data: B is a OpPhi instruction in which A is a block operand. + // control -> control: B is control-dependent on A. + uint32_t GetDivergenceSource(uint32_t id) { + auto it = divergence_source_.find(id); + if (it == divergence_source_.end()) { + return 0; + } + return it->second; + } + + // Returns the dependence source for the control dependence for the given id. + // This only exists for data -> control edges. + // + // In other words, if block 2 is dependent on block 1 due to value 3 (e.g. + // block 1 terminates with OpBranchConditional %3 %2 %4): + // * GetDivergenceSource(2) = 3 + // * GetDivergenceDependenceSource(2) = 1 + // + // Returns 0 if not applicable. + uint32_t GetDivergenceDependenceSource(uint32_t id) { + auto it = divergence_dependence_source_.find(id); + if (it == divergence_dependence_source_.end()) { + return 0; + } + return it->second; + } + + void InitializeWorklist(opt::Function* function, + bool is_first_iteration) override { + // Since |EnqueueSuccessors| is complete, we only need one pass. + if (is_first_iteration) { + Setup(function); + opt::ForwardDataFlowAnalysis::InitializeWorklist(function, true); + } + } + + void EnqueueSuccessors(opt::Instruction* inst) override; + + VisitResult Visit(opt::Instruction* inst) override; + + private: + VisitResult VisitBlock(uint32_t id); + VisitResult VisitInstruction(opt::Instruction* inst); + + // Computes the divergence level for the result of the given instruction + // based on the current state of the analysis. This is always an + // underapproximation, which will be improved as the analysis proceeds. + DivergenceLevel ComputeInstructionDivergence(opt::Instruction* inst); + + // Computes the divergence level for a variable, which is used for loads. + DivergenceLevel ComputeVariableDivergence(opt::Instruction* var); + + // Initializes data structures for performing dataflow on the given function. + void Setup(opt::Function* function); + + std::unordered_map divergence_; + std::unordered_map divergence_source_; + std::unordered_map divergence_dependence_source_; + + // Stores the result of following unconditional branches starting from the + // given block. This is used to detect when reconvergence needs to be + // accounted for. + std::unordered_map follow_unconditional_branches_; + + opt::ControlDependenceAnalysis cd_; +}; + +std::ostream& operator<<(std::ostream& os, + DivergenceAnalysis::DivergenceLevel level); + +} // namespace lint +} // namespace spvtools + +#endif // SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ diff --git a/third_party/spirv-tools/source/lint/lint_divergent_derivatives.cpp b/third_party/spirv-tools/source/lint/lint_divergent_derivatives.cpp new file mode 100644 index 0000000000..512847b0ce --- /dev/null +++ b/third_party/spirv-tools/source/lint/lint_divergent_derivatives.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/lint/divergence_analysis.h" +#include "source/lint/lints.h" +#include "source/opt/basic_block.h" +#include "source/opt/cfg.h" +#include "source/opt/control_dependence.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace lint { +namespace lints { +namespace { +// Returns the %name[id], where `name` is the first name associated with the +// given id, or just %id if one is not found. +std::string GetFriendlyName(opt::IRContext* context, uint32_t id) { + auto names = context->GetNames(id); + std::stringstream ss; + ss << "%"; + if (names.empty()) { + ss << id; + } else { + opt::Instruction* inst_name = names.begin()->second; + if (inst_name->opcode() == SpvOpName) { + ss << names.begin()->second->GetInOperand(0).AsString(); + ss << "[" << id << "]"; + } else { + ss << id; + } + } + return ss.str(); +} + +bool InstructionHasDerivative(const opt::Instruction& inst) { + static const SpvOp derivative_opcodes[] = { + // Implicit derivatives. + SpvOpImageSampleImplicitLod, + SpvOpImageSampleDrefImplicitLod, + SpvOpImageSampleProjImplicitLod, + SpvOpImageSampleProjDrefImplicitLod, + SpvOpImageSparseSampleImplicitLod, + SpvOpImageSparseSampleDrefImplicitLod, + SpvOpImageSparseSampleProjImplicitLod, + SpvOpImageSparseSampleProjDrefImplicitLod, + // Explicit derivatives. + SpvOpDPdx, + SpvOpDPdy, + SpvOpFwidth, + SpvOpDPdxFine, + SpvOpDPdyFine, + SpvOpFwidthFine, + SpvOpDPdxCoarse, + SpvOpDPdyCoarse, + SpvOpFwidthCoarse, + }; + return std::find(std::begin(derivative_opcodes), std::end(derivative_opcodes), + inst.opcode()) != std::end(derivative_opcodes); +} + +spvtools::DiagnosticStream Warn(opt::IRContext* context, + opt::Instruction* inst) { + if (inst == nullptr) { + return DiagnosticStream({0, 0, 0}, context->consumer(), "", SPV_WARNING); + } else { + // TODO(kuhar): Use line numbers based on debug info. + return DiagnosticStream( + {0, 0, 0}, context->consumer(), + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES), + SPV_WARNING); + } +} + +void PrintDivergenceFlow(opt::IRContext* context, DivergenceAnalysis div, + uint32_t id) { + opt::analysis::DefUseManager* def_use = context->get_def_use_mgr(); + opt::CFG* cfg = context->cfg(); + while (id != 0) { + bool is_block = def_use->GetDef(id)->opcode() == SpvOpLabel; + if (is_block) { + Warn(context, nullptr) + << "block " << GetFriendlyName(context, id) << " is divergent"; + uint32_t source = div.GetDivergenceSource(id); + // Skip intermediate blocks. + while (source != 0 && def_use->GetDef(source)->opcode() == SpvOpLabel) { + id = source; + source = div.GetDivergenceSource(id); + } + if (source == 0) break; + spvtools::opt::Instruction* branch = + cfg->block(div.GetDivergenceDependenceSource(id))->terminator(); + Warn(context, branch) + << "because it depends on a conditional branch on divergent value " + << GetFriendlyName(context, source) << ""; + id = source; + } else { + Warn(context, nullptr) + << "value " << GetFriendlyName(context, id) << " is divergent"; + uint32_t source = div.GetDivergenceSource(id); + opt::Instruction* def = def_use->GetDef(id); + opt::Instruction* source_def = + source == 0 ? nullptr : def_use->GetDef(source); + // First print data -> data dependencies. + while (source != 0 && source_def->opcode() != SpvOpLabel) { + Warn(context, def_use->GetDef(id)) + << "because " << GetFriendlyName(context, id) << " uses value " + << GetFriendlyName(context, source) + << "in its definition, which is divergent"; + id = source; + def = source_def; + source = div.GetDivergenceSource(id); + source_def = def_use->GetDef(source); + } + if (source == 0) { + Warn(context, def) << "because it has a divergent definition"; + break; + } + Warn(context, def) << "because it is conditionally set in block " + << GetFriendlyName(context, source); + id = source; + } + } +} +} // namespace + +bool CheckDivergentDerivatives(opt::IRContext* context) { + DivergenceAnalysis div(*context); + for (opt::Function& func : *context->module()) { + div.Run(&func); + for (const opt::BasicBlock& bb : func) { + for (const opt::Instruction& inst : bb) { + if (InstructionHasDerivative(inst) && + div.GetDivergenceLevel(bb.id()) > + DivergenceAnalysis::DivergenceLevel::kPartiallyUniform) { + Warn(context, nullptr) + << "derivative with divergent control flow" + << " located in block " << GetFriendlyName(context, bb.id()); + PrintDivergenceFlow(context, div, bb.id()); + } + } + } + } + return true; +} + +} // namespace lints +} // namespace lint +} // namespace spvtools diff --git a/third_party/spirv-tools/source/lint/linter.cpp b/third_party/spirv-tools/source/lint/linter.cpp new file mode 100644 index 0000000000..e4ed04ea44 --- /dev/null +++ b/third_party/spirv-tools/source/lint/linter.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/linter.hpp" + +#include "source/lint/lints.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv/unified1/spirv.h" + +namespace spvtools { + +struct Linter::Impl { + explicit Impl(spv_target_env env) : target_env(env) { + message_consumer = [](spv_message_level_t /*level*/, const char* /*source*/, + const spv_position_t& /*position*/, + const char* /*message*/) {}; + } + + spv_target_env target_env; // Target environment. + MessageConsumer message_consumer; // Message consumer. +}; + +Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {} + +Linter::~Linter() {} + +void Linter::SetMessageConsumer(MessageConsumer consumer) { + impl_->message_consumer = std::move(consumer); +} + +const MessageConsumer& Linter::Consumer() const { + return impl_->message_consumer; +} + +bool Linter::Run(const uint32_t* binary, size_t binary_size) { + std::unique_ptr context = + BuildModule(SPV_ENV_VULKAN_1_2, Consumer(), binary, binary_size); + if (context == nullptr) return false; + + bool result = true; + result &= lint::lints::CheckDivergentDerivatives(context.get()); + + return result; +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/lint/lints.h b/third_party/spirv-tools/source/lint/lints.h new file mode 100644 index 0000000000..a1995d2fb7 --- /dev/null +++ b/third_party/spirv-tools/source/lint/lints.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LINT_LINTS_H_ +#define SOURCE_LINT_LINTS_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace lint { + +// All of the functions in this namespace output to the error consumer in the +// |context| argument and return |true| if no errors are found. They do not +// modify the IR. +namespace lints { + +bool CheckDivergentDerivatives(opt::IRContext* context); + +} // namespace lints +} // namespace lint +} // namespace spvtools + +#endif // SOURCE_LINT_LINTS_H_ diff --git a/third_party/spirv-tools/source/operand.cpp b/third_party/spirv-tools/source/operand.cpp index c00c9b64ed..6d83e81e2f 100644 --- a/third_party/spirv-tools/source/operand.cpp +++ b/third_party/spirv-tools/source/operand.cpp @@ -578,6 +578,12 @@ std::function spvOperandCanBeForwardDeclaredFunction( std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( spv_ext_inst_type_t ext_type, uint32_t key) { + // The Vulkan debug info extended instruction set is non-semantic so allows no + // forward references ever + if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + return [](unsigned) { return false; }; + } + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward // references for debug info instructions are still in discussion. We must // update the following lines of code when we conclude the spec. diff --git a/third_party/spirv-tools/source/opt/CMakeLists.txt b/third_party/spirv-tools/source/opt/CMakeLists.txt index 0e41b20581..7d522fb5e6 100644 --- a/third_party/spirv-tools/source/opt/CMakeLists.txt +++ b/third_party/spirv-tools/source/opt/CMakeLists.txt @@ -27,8 +27,11 @@ set(SPIRV_TOOLS_OPT_SOURCES composite.h const_folding_rules.h constants.h + control_dependence.h + convert_to_sampled_image_pass.h convert_to_half_pass.h copy_prop_arrays.h + dataflow.h dead_branch_elim_pass.h dead_insert_elim_pass.h dead_variable_elimination.h @@ -36,6 +39,7 @@ set(SPIRV_TOOLS_OPT_SOURCES debug_info_manager.h def_use_manager.h desc_sroa.h + desc_sroa_util.h dominator_analysis.h dominator_tree.h eliminate_dead_constant_pass.h @@ -97,6 +101,7 @@ set(SPIRV_TOOLS_OPT_SOURCES relax_float_ops_pass.h remove_duplicates_pass.h remove_unused_interface_variables_pass.h + replace_desc_array_access_using_var_index.h replace_invalid_opc.h scalar_analysis.h scalar_analysis_nodes.h @@ -133,8 +138,11 @@ set(SPIRV_TOOLS_OPT_SOURCES composite.cpp const_folding_rules.cpp constants.cpp + control_dependence.cpp + convert_to_sampled_image_pass.cpp convert_to_half_pass.cpp copy_prop_arrays.cpp + dataflow.cpp dead_branch_elim_pass.cpp dead_insert_elim_pass.cpp dead_variable_elimination.cpp @@ -142,6 +150,7 @@ set(SPIRV_TOOLS_OPT_SOURCES debug_info_manager.cpp def_use_manager.cpp desc_sroa.cpp + desc_sroa_util.cpp dominator_analysis.cpp dominator_tree.cpp eliminate_dead_constant_pass.cpp @@ -199,6 +208,7 @@ set(SPIRV_TOOLS_OPT_SOURCES relax_float_ops_pass.cpp remove_duplicates_pass.cpp remove_unused_interface_variables_pass.cpp + replace_desc_array_access_using_var_index.cpp replace_invalid_opc.cpp scalar_analysis.cpp scalar_analysis_simplification.cpp diff --git a/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp index 7cffff5700..0b54d5e85a 100644 --- a/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG Inc. -// Copyright (c) 2018 Google LLC +// Copyright (c) 2018-2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ #include "source/cfa.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/eliminate_dead_functions_util.h" +#include "source/opt/ir_builder.h" #include "source/opt/iterator.h" #include "source/opt/reflect.h" #include "source/spirv_constant.h" @@ -35,10 +36,10 @@ namespace { const uint32_t kTypePointerStorageClassInIdx = 0; const uint32_t kEntryPointFunctionIdInIdx = 1; const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; -const uint32_t kLoopMergeMergeBlockIdInIdx = 0; const uint32_t kLoopMergeContinueBlockIdInIdx = 1; const uint32_t kCopyMemoryTargetAddrInIdx = 0; const uint32_t kCopyMemorySourceAddrInIdx = 1; +const uint32_t kLoadSourceAddrInIdx = 0; const uint32_t kDebugDeclareOperandVariableIndex = 5; const uint32_t kGlobalVariableVariableIndex = 12; @@ -96,16 +97,21 @@ bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) { storageClass; } -bool AggressiveDCEPass::IsLocalVar(uint32_t varId) { +bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) { if (IsVarOfStorage(varId, SpvStorageClassFunction)) { return true; } - if (!private_like_local_) { + + if (!IsVarOfStorage(varId, SpvStorageClassPrivate) && + !IsVarOfStorage(varId, SpvStorageClassWorkgroup)) { return false; } - return IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup); + // For a variable in the Private or WorkGroup storage class, the variable will + // get a new instance for every call to an entry point. If the entry point + // does not have a call, then no other function can read or write to that + // instance of the variable. + return IsEntryPointWithNoCalls(func); } void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { @@ -145,15 +151,19 @@ bool AggressiveDCEPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } - return true; -} - -bool AggressiveDCEPass::IsDead(Instruction* inst) { - if (IsLive(inst)) return false; - if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) && - !IsStructuredHeader(context()->get_instr_block(inst), nullptr, nullptr, - nullptr)) - return false; + // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended instruction sets even if they are non-semantic + 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(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -173,12 +183,12 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { }); return dead; } - return IsDead(tInst); + return !IsLive(tInst); } void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { // Only process locals - if (!IsLocalVar(varId)) return; + if (!IsLocalVar(varId, func)) return; // Return if already processed if (live_local_vars_.find(varId) != live_local_vars_.end()) return; // Mark all stores to varId as live @@ -187,66 +197,6 @@ void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { live_local_vars_.insert(varId); } -bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp, - Instruction** mergeInst, - Instruction** branchInst, - uint32_t* mergeBlockId) { - if (!bp) return false; - Instruction* mi = bp->GetMergeInst(); - if (mi == nullptr) return false; - Instruction* bri = &*bp->tail(); - if (branchInst != nullptr) *branchInst = bri; - if (mergeInst != nullptr) *mergeInst = mi; - if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0); - return true; -} - -void AggressiveDCEPass::ComputeBlock2HeaderMaps( - std::list& structuredOrder) { - block2headerBranch_.clear(); - header2nextHeaderBranch_.clear(); - branch2merge_.clear(); - structured_order_index_.clear(); - std::stack currentHeaderBranch; - currentHeaderBranch.push(nullptr); - uint32_t currentMergeBlockId = 0; - uint32_t index = 0; - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); - ++bi, ++index) { - structured_order_index_[*bi] = index; - // If this block is the merge block of the current control construct, - // we are leaving the current construct so we must update state - if ((*bi)->id() == currentMergeBlockId) { - currentHeaderBranch.pop(); - Instruction* chb = currentHeaderBranch.top(); - if (chb != nullptr) - currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0); - } - Instruction* mergeInst; - Instruction* branchInst; - uint32_t mergeBlockId; - bool is_header = - IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId); - // Map header block to next enclosing header. - if (is_header) header2nextHeaderBranch_[*bi] = currentHeaderBranch.top(); - // If this is a loop header, update state first so the block will map to - // itself. - if (is_header && mergeInst->opcode() == SpvOpLoopMerge) { - currentHeaderBranch.push(branchInst); - branch2merge_[branchInst] = mergeInst; - currentMergeBlockId = mergeBlockId; - } - // Map the block to the current construct. - block2headerBranch_[*bi] = currentHeaderBranch.top(); - // If this is an if header, update state so following blocks map to the if. - if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) { - currentHeaderBranch.push(branchInst); - branch2merge_[branchInst] = mergeInst; - currentMergeBlockId = mergeBlockId; - } - } -} - void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) { std::unique_ptr newBranch( new Instruction(context(), SpvOpBranch, 0, 0, @@ -262,23 +212,18 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( mergeInst->opcode() == SpvOpLoopMerge); BasicBlock* header = context()->get_instr_block(mergeInst); - uint32_t headerIndex = structured_order_index_[header]; const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0); - BasicBlock* merge = context()->get_instr_block(mergeId); - uint32_t mergeIndex = structured_order_index_[merge]; - get_def_use_mgr()->ForEachUser( - mergeId, [headerIndex, mergeIndex, this](Instruction* user) { - if (!user->IsBranch()) return; - BasicBlock* block = context()->get_instr_block(user); - uint32_t index = structured_order_index_[block]; - if (headerIndex < index && index < mergeIndex) { - // This is a break from the loop. - AddToWorklist(user); - // Add branch's merge if there is one. - Instruction* userMerge = branch2merge_[user]; - if (userMerge != nullptr) AddToWorklist(userMerge); - } - }); + get_def_use_mgr()->ForEachUser(mergeId, [header, this](Instruction* user) { + if (!user->IsBranch()) return; + BasicBlock* block = context()->get_instr_block(user); + if (BlockIsInConstruct(header, block)) { + // This is a break from the loop. + AddToWorklist(user); + // Add branch's merge if there is one. + Instruction* userMerge = GetMergeInstruction(user); + if (userMerge != nullptr) AddToWorklist(userMerge); + } + }); if (mergeInst->opcode() != SpvOpLoopMerge) { return; @@ -292,7 +237,7 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( if (op == SpvOpBranchConditional || op == SpvOpSwitch) { // A conditional branch or switch can only be a continue if it does not // have a merge instruction or its merge block is not the continue block. - Instruction* hdrMerge = branch2merge_[user]; + Instruction* hdrMerge = GetMergeInstruction(user); if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) { uint32_t hdrMergeId = hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); @@ -304,9 +249,9 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( // An unconditional branch can only be a continue if it is not // branching to its own merge block. BasicBlock* blk = context()->get_instr_block(user); - Instruction* hdrBranch = block2headerBranch_[blk]; + Instruction* hdrBranch = GetHeaderBranch(blk); if (hdrBranch == nullptr) return; - Instruction* hdrMerge = branch2merge_[hdrBranch]; + Instruction* hdrMerge = GetMergeInstruction(hdrBranch); if (hdrMerge->opcode() == SpvOpLoopMerge) return; uint32_t hdrMergeId = hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); @@ -319,254 +264,36 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( } bool AggressiveDCEPass::AggressiveDCE(Function* func) { - // Mark function parameters as live. - AddToWorklist(&func->DefInst()); - func->ForEachParam( - [this](const Instruction* param) { - AddToWorklist(const_cast(param)); - }, - false); - - // Compute map from block to controlling conditional branch - std::list structuredOrder; - cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); - ComputeBlock2HeaderMaps(structuredOrder); - bool modified = false; - // Add instructions with external side effects to worklist. Also add branches - // EXCEPT those immediately contained in an "if" selection construct or a loop - // or continue construct. - // TODO(greg-lunarg): Handle Frexp, Modf more optimally - call_in_func_ = false; - func_is_entry_point_ = false; - private_stores_.clear(); + std::list structured_order; + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order); live_local_vars_.clear(); - // Stacks to keep track of when we are inside an if- or loop-construct. - // When immediately inside an if- or loop-construct, we do not initially - // mark branches live. All other branches must be marked live. - std::stack assume_branches_live; - std::stack currentMergeBlockId; - // Push sentinel values on stack for when outside of any control flow. - assume_branches_live.push(true); - currentMergeBlockId.push(0); - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { - // If exiting if or loop, update stacks - if ((*bi)->id() == currentMergeBlockId.top()) { - assume_branches_live.pop(); - currentMergeBlockId.pop(); - } - for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) { - SpvOp op = ii->opcode(); - switch (op) { - case SpvOpStore: { - uint32_t varId; - (void)GetPtr(&*ii, &varId); - // Mark stores as live if their variable is not function scope - // and is not private scope. Remember private stores for possible - // later inclusion. We cannot call IsLocalVar at this point because - // private_like_local_ has not been set yet. - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) - private_stores_.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) - AddToWorklist(&*ii); - } break; - case SpvOpCopyMemory: - case SpvOpCopyMemorySized: { - uint32_t varId; - (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), - &varId); - if (IsVarOfStorage(varId, SpvStorageClassPrivate) || - IsVarOfStorage(varId, SpvStorageClassWorkgroup)) - private_stores_.push_back(&*ii); - else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) - AddToWorklist(&*ii); - } break; - case SpvOpLoopMerge: { - assume_branches_live.push(false); - currentMergeBlockId.push( - ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx)); - } break; - case SpvOpSelectionMerge: { - assume_branches_live.push(false); - currentMergeBlockId.push( - ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx)); - } break; - case SpvOpSwitch: - case SpvOpBranch: - case SpvOpBranchConditional: - case SpvOpUnreachable: { - if (assume_branches_live.top()) { - AddToWorklist(&*ii); - } - } break; - default: { - // Function calls, atomics, function params, function returns, etc. - // TODO(greg-lunarg): function calls live only if write to non-local - if (!ii->IsOpcodeSafeToDelete()) { - AddToWorklist(&*ii); - } - // Remember function calls - if (op == SpvOpFunctionCall) call_in_func_ = true; - } break; - } - } - } - // See if current function is an entry point - for (auto& ei : get_module()->entry_points()) { - if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) == - func->result_id()) { - func_is_entry_point_ = true; - break; - } - } - // If the current function is an entry point and has no function calls, - // we can optimize private variables as locals - private_like_local_ = func_is_entry_point_ && !call_in_func_; - // If privates are not like local, add their stores to worklist - if (!private_like_local_) - for (auto& ps : private_stores_) AddToWorklist(ps); - // Perform closure on live instruction set. - while (!worklist_.empty()) { - Instruction* liveInst = worklist_.front(); - // Add all operand instructions if not already live - liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) { - Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - // Do not add label if an operand of a branch. This is not needed - // as part of live code discovery and can create false live code, - // for example, the branch to a header of a loop. - if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return; - AddToWorklist(inInst); - }); - if (liveInst->type_id() != 0) { - AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id())); - } - // If in a structured if or loop construct, add the controlling - // conditional branch and its merge. - BasicBlock* blk = context()->get_instr_block(liveInst); - Instruction* branchInst = block2headerBranch_[blk]; - if (branchInst != nullptr) { - AddToWorklist(branchInst); - Instruction* mergeInst = branch2merge_[branchInst]; - AddToWorklist(mergeInst); - } - // If the block is a header, add the next outermost controlling - // conditional branch and its merge. - Instruction* nextBranchInst = header2nextHeaderBranch_[blk]; - if (nextBranchInst != nullptr) { - AddToWorklist(nextBranchInst); - Instruction* mergeInst = branch2merge_[nextBranchInst]; - AddToWorklist(mergeInst); - } - // If local load, add all variable's stores if variable not already live - if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) { - uint32_t varId; - (void)GetPtr(liveInst, &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - // Process memory copies like loads - } else if (liveInst->opcode() == SpvOpCopyMemory || - liveInst->opcode() == SpvOpCopyMemorySized) { - uint32_t varId; - (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx), - &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - // If DebugDeclare, process as load of variable - } else if (liveInst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { - uint32_t varId = - liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); - ProcessLoad(func, varId); - // If DebugValue with Deref, process as load of variable - } else if (liveInst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugValue) { - uint32_t varId = context() - ->get_debug_info_mgr() - ->GetVariableIdOfDebugValueUsedForDeclare(liveInst); - if (varId != 0) ProcessLoad(func, varId); - // If merge, add other branches that are part of its control structure - } else if (liveInst->opcode() == SpvOpLoopMerge || - liveInst->opcode() == SpvOpSelectionMerge) { - AddBreaksAndContinuesToWorklist(liveInst); - // If function call, treat as if it loads from all pointer arguments - } else if (liveInst->opcode() == SpvOpFunctionCall) { - liveInst->ForEachInId([this, func](const uint32_t* iid) { - // Skip non-ptr args - if (!IsPtr(*iid)) return; - uint32_t varId; - (void)GetPtr(*iid, &varId); - ProcessLoad(func, varId); - }); - // If function parameter, treat as if it's result id is loaded from - } else if (liveInst->opcode() == SpvOpFunctionParameter) { - ProcessLoad(func, liveInst->result_id()); - // We treat an OpImageTexelPointer as a load of the pointer, and - // that value is manipulated to get the result. - } else if (liveInst->opcode() == SpvOpImageTexelPointer) { - uint32_t varId; - (void)GetPtr(liveInst, &varId); - if (varId != 0) { - ProcessLoad(func, varId); - } - } + InitializeWorkList(func, structured_order); + ProcessWorkList(func); + return KillDeadInstructions(func, structured_order); +} - // Add OpDecorateId instructions that apply to this instruction to the work - // list. We use the decoration manager to look through the group - // decorations to get to the OpDecorate* instructions themselves. - auto decorations = - get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false); - for (Instruction* dec : decorations) { - // We only care about OpDecorateId instructions because the are the only - // decorations that will reference an id that will have to be kept live - // because of that use. - if (dec->opcode() != SpvOpDecorateId) { - continue; - } - if (dec->GetSingleWordInOperand(1) == - SpvDecorationHlslCounterBufferGOOGLE) { - // These decorations should not force the use id to be live. It will be - // removed if either the target or the in operand are dead. - continue; - } - AddToWorklist(dec); - } - - // Add DebugScope and DebugInlinedAt for |liveInst| to the work list. - if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { - auto* scope = get_def_use_mgr()->GetDef( - liveInst->GetDebugScope().GetLexicalScope()); - AddToWorklist(scope); - } - if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) { - auto* inlined_at = - get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt()); - AddToWorklist(inlined_at); - } - worklist_.pop(); - } - - // Kill dead instructions and remember dead blocks - for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) { - uint32_t mergeBlockId = 0; - (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) { - if (!IsDead(inst)) return; +bool AggressiveDCEPass::KillDeadInstructions( + const Function* func, std::list& structured_order) { + bool modified = false; + for (auto bi = structured_order.begin(); bi != structured_order.end();) { + uint32_t merge_block_id = 0; + (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) { + if (IsLive(inst)) return; if (inst->opcode() == SpvOpLabel) return; // If dead instruction is selection merge, remember merge block // for new branch at end of block if (inst->opcode() == SpvOpSelectionMerge || inst->opcode() == SpvOpLoopMerge) - mergeBlockId = inst->GetSingleWordInOperand(0); + merge_block_id = inst->GetSingleWordInOperand(0); to_kill_.push_back(inst); modified = true; }); // If a structured if or loop was deleted, add a branch to its merge // block, and traverse to the merge block and continue processing there. // We know the block still exists because the label is not deleted. - if (mergeBlockId != 0) { - AddBranch(mergeBlockId, *bi); - for (++bi; (*bi)->id() != mergeBlockId; ++bi) { + if (merge_block_id != 0) { + AddBranch(merge_block_id, *bi); + for (++bi; (*bi)->id() != merge_block_id; ++bi) { } auto merge_terminator = (*bi)->terminator(); @@ -589,13 +316,252 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { live_insts_.Set(merge_terminator->unique_id()); } } else { + Instruction* inst = (*bi)->terminator(); + if (!IsLive(inst)) { + // If the terminator is not live, this block has no live instructions, + // and it will be unreachable. + AddUnreachable(*bi); + } ++bi; } } - return modified; } +void AggressiveDCEPass::ProcessWorkList(Function* func) { + while (!worklist_.empty()) { + Instruction* live_inst = worklist_.front(); + worklist_.pop(); + AddOperandsToWorkList(live_inst); + MarkBlockAsLive(live_inst); + MarkLoadedVariablesAsLive(func, live_inst); + AddDecorationsToWorkList(live_inst); + AddDebugInstructionsToWorkList(live_inst); + } +} + +void AggressiveDCEPass::AddDebugInstructionsToWorkList( + const Instruction* inst) { + for (auto& line_inst : inst->dbg_line_insts()) { + if (line_inst.IsDebugLineInst()) { + AddOperandsToWorkList(&line_inst); + } + } + + if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { + auto* scope = + get_def_use_mgr()->GetDef(inst->GetDebugScope().GetLexicalScope()); + AddToWorklist(scope); + } + if (inst->GetDebugInlinedAt() != kNoInlinedAt) { + auto* inlined_at = get_def_use_mgr()->GetDef(inst->GetDebugInlinedAt()); + AddToWorklist(inlined_at); + } +} + +void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) { + // Add OpDecorateId instructions that apply to this instruction to the work + // list. We use the decoration manager to look through the group + // decorations to get to the OpDecorate* instructions themselves. + auto decorations = + get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false); + for (Instruction* dec : decorations) { + // We only care about OpDecorateId instructions because the are the only + // decorations that will reference an id that will have to be kept live + // because of that use. + if (dec->opcode() != SpvOpDecorateId) { + continue; + } + if (dec->GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + // These decorations should not force the use id to be live. It will be + // removed if either the target or the in operand are dead. + continue; + } + AddToWorklist(dec); + } +} + +void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func, + Instruction* inst) { + std::vector live_variables = GetLoadedVariables(inst); + for (uint32_t var_id : live_variables) { + ProcessLoad(func, var_id); + } +} + +std::vector AggressiveDCEPass::GetLoadedVariables(Instruction* inst) { + if (inst->opcode() == SpvOpFunctionCall) { + return GetLoadedVariablesFromFunctionCall(inst); + } + uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst); + if (var_id == 0) { + return {}; + } + return {var_id}; +} + +uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls( + Instruction* inst) { + std::vector live_variables; + if (inst->IsAtomicWithLoad()) { + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + } + + switch (inst->opcode()) { + case SpvOpLoad: + case SpvOpImageTexelPointer: + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + return GetVariableId( + inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx)); + default: + break; + } + + switch (inst->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: + return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + case CommonDebugInfoDebugValue: { + analysis::DebugInfoManager* debug_info_mgr = + context()->get_debug_info_mgr(); + return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst); + } + default: + break; + } + return 0; +} + +std::vector AggressiveDCEPass::GetLoadedVariablesFromFunctionCall( + const Instruction* inst) { + assert(inst->opcode() == SpvOpFunctionCall); + std::vector live_variables; + inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) { + if (!IsPtr(*operand_id)) return; + uint32_t var_id = GetVariableId(*operand_id); + live_variables.push_back(var_id); + }); + return live_variables; +} + +uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) { + assert(IsPtr(ptr_id) && + "Cannot get the variable when input is not a pointer."); + uint32_t varId = 0; + (void)GetPtr(ptr_id, &varId); + return varId; +} + +void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) { + BasicBlock* basic_block = context()->get_instr_block(inst); + if (basic_block == nullptr) { + return; + } + + // If we intend to keep this instruction, we need the block label and + // block terminator to have a valid block for the instruction. + AddToWorklist(basic_block->GetLabelInst()); + + // We need to mark the successors blocks that follow as live. If this is + // header of the merge construct, the construct may be folded, but we will + // definitely need the merge label. If it is not a construct, the terminator + // must be live, and the successor blocks will be marked as live when + // processing the terminator. + uint32_t merge_id = basic_block->MergeBlockIdIfAny(); + if (merge_id == 0) { + AddToWorklist(basic_block->terminator()); + } else { + AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id)); + } + + // Mark the structured control flow constructs that contains this block as + // live. If |inst| is an instruction in the loop header, then it is part of + // the loop, so the loop construct must be live. We exclude the label because + // it does not matter how many times it is executed. This could be extended + // to more instructions, but we will need it for now. + if (inst->opcode() != SpvOpLabel) + MarkLoopConstructAsLiveIfLoopHeader(basic_block); + + Instruction* next_branch_inst = GetBranchForNextHeader(basic_block); + if (next_branch_inst != nullptr) { + AddToWorklist(next_branch_inst); + Instruction* mergeInst = GetMergeInstruction(next_branch_inst); + AddToWorklist(mergeInst); + } + + if (inst->opcode() == SpvOpLoopMerge || + inst->opcode() == SpvOpSelectionMerge) { + AddBreaksAndContinuesToWorklist(inst); + } +} +void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader( + BasicBlock* basic_block) { + // If this is the header for a loop, then loop structure needs to keep as well + // because the loop header is also part of the loop. + Instruction* merge_inst = basic_block->GetLoopMergeInst(); + if (merge_inst != nullptr) { + AddToWorklist(basic_block->terminator()); + AddToWorklist(merge_inst); + } +} + +void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) { + inst->ForEachInId([this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + AddToWorklist(inInst); + }); + if (inst->type_id() != 0) { + AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id())); + } +} + +void AggressiveDCEPass::InitializeWorkList( + Function* func, std::list& structured_order) { + AddToWorklist(&func->DefInst()); + MarkFunctionParameterAsLive(func); + MarkFirstBlockAsLive(func); + + // Add instructions with external side effects to the worklist. Also add + // branches that are not attached to a structured construct. + // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be + // cleaned up. + for (auto& bi : structured_order) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + SpvOp op = ii->opcode(); + if (ii->IsBranch()) { + continue; + } + switch (op) { + case SpvOpStore: { + uint32_t var_id = 0; + (void)GetPtr(&*ii, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); + } break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: { + uint32_t var_id = 0; + uint32_t target_addr_id = + ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx); + (void)GetPtr(target_addr_id, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); + } break; + case SpvOpLoopMerge: + case SpvOpSelectionMerge: + case SpvOpUnreachable: + break; + default: { + // Function calls, atomics, function params, function returns, etc. + if (!ii->IsOpcodeSafeToDelete()) { + AddToWorklist(&*ii); + } + } break; + } + } + } +} + void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { // Keep all execution modes. for (auto& exec : get_module()->execution_modes()) { @@ -603,7 +569,8 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // Keep all entry points. for (auto& entry : get_module()->entry_points()) { - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && + !preserve_interface_) { // In SPIR-V 1.4 and later, entry points must list all global variables // used. DCE can still remove non-input/output variables and update the // interface list. Mark the entry point as live and inputs and outputs as @@ -650,16 +617,25 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // For each DebugInfo GlobalVariable keep all operands except the Variable. - // Later, if the variable is dead, we will set the operand to DebugInfoNone. + // Later, if the variable is killed with KillInst(), we will set the operand + // to DebugInfoNone. Create and save DebugInfoNone now for this possible + // later use. This is slightly unoptimal, but it avoids generating it during + // instruction killing when the module is not consistent. + bool debug_global_seen = false; for (auto& dbg : get_module()->ext_inst_debuginfo()) { - if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable) + if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; + debug_global_seen = true; dbg.ForEachInId([this](const uint32_t* iid) { - Instruction* inInst = get_def_use_mgr()->GetDef(*iid); - if (inInst->opcode() == SpvOpVariable) return; - AddToWorklist(inInst); + Instruction* in_inst = get_def_use_mgr()->GetDef(*iid); + if (in_inst->opcode() == SpvOpVariable) return; + AddToWorklist(in_inst); }); } + if (debug_global_seen) { + auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone(); + AddToWorklist(dbg_none); + } } Pass::Status AggressiveDCEPass::ProcessImpl() { @@ -691,7 +667,7 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { // Process all entry point functions. ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); }; - modified |= context()->ProcessEntryPointCallTree(pfn); + modified |= context()->ProcessReachableCallTree(pfn); // If the decoration manager is kept live then the context will try to keep it // up to date. ADCE deals with group decorations by changing the operands in @@ -718,21 +694,20 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { // Cleanup all CFG including all unreachable blocks. ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); }; - modified |= context()->ProcessEntryPointCallTree(cleanup); + modified |= context()->ProcessReachableCallTree(cleanup); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } bool AggressiveDCEPass::EliminateDeadFunctions() { // Identify live functions first. Those that are not live - // are dead. ADCE is disabled for non-shaders so we do not check for exported - // functions here. + // are dead. std::unordered_set live_function_set; ProcessFunction mark_live = [&live_function_set](Function* fp) { live_function_set.insert(fp); return false; }; - context()->ProcessEntryPointCallTree(mark_live); + context()->ProcessReachableCallTree(mark_live); bool modified = false; for (auto funcIter = get_module()->begin(); @@ -798,7 +773,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2); Instruction* counter_buffer_inst = get_def_use_mgr()->GetDef(counter_buffer_id); - if (IsDead(counter_buffer_inst)) { + if (!IsLive(counter_buffer_inst)) { context()->KillInst(annotation); modified = true; } @@ -813,7 +788,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { for (uint32_t i = 1; i < annotation->NumOperands();) { Instruction* opInst = get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); - if (IsDead(opInst)) { + if (!IsLive(opInst)) { // Don't increment |i|. annotation->RemoveOperand(i); modified = true; @@ -840,7 +815,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { for (uint32_t i = 1; i < annotation->NumOperands();) { Instruction* opInst = get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); - if (IsDead(opInst)) { + if (!IsLive(opInst)) { // Don't increment |i|. annotation->RemoveOperand(i + 1); annotation->RemoveOperand(i); @@ -874,14 +849,13 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } for (auto& dbg : get_module()->ext_inst_debuginfo()) { - if (!IsDead(&dbg)) continue; + if (IsLive(&dbg)) continue; // Save GlobalVariable if its variable is live, otherwise null out variable // index - if (dbg.GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); - if (!IsDead(var_inst)) continue; + if (IsLive(var_inst)) continue; context()->ForgetUses(&dbg); dbg.SetOperand( kGlobalVariableVariableIndex, @@ -896,7 +870,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { // Since ADCE is disabled for non-shaders, we don't check for export linkage // attributes here. for (auto& val : get_module()->types_values()) { - if (IsDead(&val)) { + if (!IsLive(&val)) { // Save forwarded pointer if pointer is live since closure does not mark // this live as it does not have a result id. This is a little too // conservative since it is not known if the structure type that needed @@ -904,14 +878,15 @@ bool AggressiveDCEPass::ProcessGlobalValues() { if (val.opcode() == SpvOpTypeForwardPointer) { uint32_t ptr_ty_id = val.GetSingleWordInOperand(0); Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); - if (!IsDead(ptr_ty_inst)) continue; + if (IsLive(ptr_ty_inst)) continue; } to_kill_.push_back(&val); modified = true; } } - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && + !preserve_interface_) { // Remove the dead interface variables from the entry point interface list. for (auto& entry : get_module()->entry_points()) { std::vector new_operands; @@ -922,7 +897,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } else { auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); - if (!IsDead(var)) { + if (IsLive(var)) { new_operands.push_back(entry.GetInOperand(i)); } } @@ -937,8 +912,6 @@ bool AggressiveDCEPass::ProcessGlobalValues() { return modified; } -AggressiveDCEPass::AggressiveDCEPass() = default; - Pass::Status AggressiveDCEPass::Process() { // Initialize extensions allowlist InitExtensions(); @@ -999,8 +972,119 @@ void AggressiveDCEPass::InitExtensions() { "SPV_KHR_vulkan_memory_model", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } +Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + BasicBlock* header_block = GetHeaderBlock(blk); + if (header_block == nullptr) { + return nullptr; + } + return header_block->terminator(); +} + +BasicBlock* AggressiveDCEPass::GetHeaderBlock(BasicBlock* blk) const { + if (blk == nullptr) { + return nullptr; + } + + BasicBlock* header_block = nullptr; + if (blk->IsLoopHeader()) { + header_block = blk; + } else { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + header_block = context()->get_instr_block(header); + } + return header_block; +} + +Instruction* AggressiveDCEPass::GetMergeInstruction(Instruction* inst) { + BasicBlock* bb = context()->get_instr_block(inst); + if (bb == nullptr) { + return nullptr; + } + return bb->GetMergeInst(); +} + +Instruction* AggressiveDCEPass::GetBranchForNextHeader(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + + if (blk->IsLoopHeader()) { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + blk = context()->get_instr_block(header); + } + return GetHeaderBranch(blk); +} + +void AggressiveDCEPass::MarkFunctionParameterAsLive(const Function* func) { + func->ForEachParam( + [this](const Instruction* param) { + AddToWorklist(const_cast(param)); + }, + false); +} + +bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block, + BasicBlock* bb) { + if (bb == nullptr || header_block == nullptr) { + return false; + } + + uint32_t current_header = bb->id(); + while (current_header != 0) { + if (current_header == header_block->id()) return true; + current_header = context()->GetStructuredCFGAnalysis()->ContainingConstruct( + current_header); + } + return false; +} + +bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) { + auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id()); + if (cached_result != entry_point_with_no_calls_cache_.end()) { + return cached_result->second; + } + bool result = IsEntryPoint(func) && !HasCall(func); + entry_point_with_no_calls_cache_[func->result_id()] = result; + return result; +} + +bool AggressiveDCEPass::IsEntryPoint(Function* func) { + for (const Instruction& entry_point : get_module()->entry_points()) { + uint32_t entry_point_id = + entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + if (entry_point_id == func->result_id()) { + return true; + } + } + return false; +} + +bool AggressiveDCEPass::HasCall(Function* func) { + return !func->WhileEachInst( + [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; }); +} + +void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) { + BasicBlock* first_block = &*func->begin(); + MarkBlockAsLive(first_block->GetLabelInst()); +} + +void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) { + InstructionBuilder builder( + context(), block, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + builder.AddUnreachable(); +} + } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h index f02e729fd5..1b3fd1e853 100644 --- a/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h +++ b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h @@ -44,7 +44,9 @@ class AggressiveDCEPass : public MemPass { using GetBlocksFunction = std::function*(const BasicBlock*)>; - AggressiveDCEPass(); + AggressiveDCEPass(bool preserve_interface = false) + : preserve_interface_(preserve_interface) {} + const char* name() const override { return "eliminate-dead-code-aggressive"; } Status Process() override; @@ -55,23 +57,26 @@ class AggressiveDCEPass : public MemPass { } private: + // Preserve entry point interface if true. All variables in interface + // will be marked live and will not be eliminated. This mode is needed by + // GPU-Assisted Validation instrumentation where a change in the interface + // is not allowed. + bool preserve_interface_; + // Return true if |varId| is a variable of |storageClass|. |varId| must either // be 0 or the result of an instruction. bool IsVarOfStorage(uint32_t varId, uint32_t storageClass); - // Return true if |varId| is variable of function storage class or is - // private variable and privates can be optimized like locals (see - // privates_like_local_). - bool IsLocalVar(uint32_t varId); + // Return true if the instance of the variable |varId| can only be access in + // |func|. For example, a function scope variable, or a private variable + // where |func| is an entry point with no function calls. + bool IsLocalVar(uint32_t varId, Function* func); // Return true if |inst| is marked live. bool IsLive(const Instruction* inst) const { return live_insts_.Get(inst->unique_id()); } - // Returns true if |inst| is dead. - bool IsDead(Instruction* inst); - // Adds entry points, execution modes and workgroup size decorations to the // worklist for processing with the first function. void InitializeModuleScopeLiveInstructions(); @@ -101,18 +106,6 @@ class AggressiveDCEPass : public MemPass { // If |varId| is local, mark all stores of varId as live. void ProcessLoad(Function* func, uint32_t varId); - // If |bp| is structured header block, returns true and sets |mergeInst| to - // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the - // merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or - // |mergeBlockId| may be a null pointer. Returns false if |bp| is a null - // pointer. - bool IsStructuredHeader(BasicBlock* bp, Instruction** mergeInst, - Instruction** branchInst, uint32_t* mergeBlockId); - - // Initialize block2headerBranch_, header2nextHeaderBranch_, and - // branch2merge_ using |structuredOrder| to order blocks. - void ComputeBlock2HeaderMaps(std::list& structuredOrder); - // Add branch to |labelId| to end of block |bp|. void AddBranch(uint32_t labelId, BasicBlock* bp); @@ -140,14 +133,100 @@ class AggressiveDCEPass : public MemPass { Pass::Status ProcessImpl(); - // True if current function has a call instruction contained in it - bool call_in_func_; + // Adds instructions which must be kept because of they have side-effects + // that ADCE cannot model to the work list. + void InitializeWorkList(Function* func, + std::list& structured_order); - // True if current function is an entry point - bool func_is_entry_point_; + // Process each instruction in the work list by marking any instruction that + // that it depends on as live, and adding it to the work list. The work list + // will be empty at the end. + void ProcessWorkList(Function* func); - // True if current function is entry point and has no function calls. - bool private_like_local_; + // Kills any instructions in |func| that have not been marked as live. + bool KillDeadInstructions(const Function* func, + std::list& structured_order); + + // Adds the instructions that define the operands of |inst| to the work list. + void AddOperandsToWorkList(const Instruction* inst); + + // Marks all of the labels and branch that inst requires as live. + void MarkBlockAsLive(Instruction* inst); + + // Marks any variables from which |inst| may require data as live. + void MarkLoadedVariablesAsLive(Function* func, Instruction* inst); + + // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a + // value whose type is a pointer. + uint32_t GetVariableId(uint32_t ptr_id); + + // Returns all of the ids for the variables from which |inst| will load data. + std::vector GetLoadedVariables(Instruction* inst); + + // Returns all of the ids for the variables from which |inst| will load data. + // The opcode of |inst| must be OpFunctionCall. + std::vector GetLoadedVariablesFromFunctionCall( + const Instruction* inst); + + // Returns the id of the variable from which |inst| will load data. |inst| + // must not be an OpFunctionCall. Returns 0 if no data is read or the + // variable cannot be determined. Note that in logical addressing mode the + // latter is not possible for function and private storage class because there + // cannot be variable pointers pointing to those storage classes. + uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst); + + // Adds all decorations of |inst| to the work list. + void AddDecorationsToWorkList(const Instruction* inst); + + // Adds all debug instruction associated with |inst| to the work list. + void AddDebugInstructionsToWorkList(const Instruction* inst); + + // Marks all of the OpFunctionParameter instructions in |func| as live. + void MarkFunctionParameterAsLive(const Function* func); + + // Returns the terminator instruction in the header for the innermost + // construct that contains |blk|. Returns nullptr if no such header exists. + Instruction* GetHeaderBranch(BasicBlock* blk); + + // Returns the header for the innermost construct that contains |blk|. A loop + // header will be its own header. Returns nullptr if no such header exists. + BasicBlock* GetHeaderBlock(BasicBlock* blk) const; + + // Returns the same as |GetHeaderBlock| except if |blk| is a loop header it + // will return the header of the next enclosing construct. Returns nullptr if + // no such header exists. + Instruction* GetBranchForNextHeader(BasicBlock* blk); + + // Returns the merge instruction in the same basic block as |inst|. Returns + // nullptr if one does not exist. + Instruction* GetMergeInstruction(Instruction* inst); + + // Returns true if |bb| is in the construct with header |header_block|. + bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb); + + // Returns true if |func| is an entry point that does not have any function + // calls. + bool IsEntryPointWithNoCalls(Function* func); + + // Returns true if |func| is an entry point. + bool IsEntryPoint(Function* func); + + // Returns true if |func| contains a function call. + bool HasCall(Function* func); + + // Marks the first block, which is the entry block, in |func| as live. + void MarkFirstBlockAsLive(Function* func); + + // Adds an OpUnreachable instruction at the end of |block|. + void AddUnreachable(BasicBlock*& block); + + // Marks the OpLoopMerge and the terminator in |basic_block| as live if + // |basic_block| is a loop header. + void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block); + + // The cached results for |IsEntryPointWithNoCalls|. It maps the function's + // result id to the return value. + std::unordered_map entry_point_with_no_calls_cache_; // Live Instruction Worklist. An instruction is added to this list // if it might have a side effect, either directly or indirectly. @@ -156,27 +235,6 @@ class AggressiveDCEPass : public MemPass { // building up the live instructions set |live_insts_|. std::queue worklist_; - // Map from block to the branch instruction in the header of the most - // immediate controlling structured if or loop. A loop header block points - // to its own branch instruction. An if-selection block points to the branch - // of an enclosing construct's header, if one exists. - std::unordered_map block2headerBranch_; - - // Map from header block to the branch instruction in the header of the - // structured construct enclosing it. - // The liveness algorithm is designed to iteratively mark as live all - // structured constructs enclosing a live instruction. - std::unordered_map header2nextHeaderBranch_; - - // Maps basic block to their index in the structured order traversal. - std::unordered_map structured_order_index_; - - // Map from branch to its associated merge instruction, if any - std::unordered_map branch2merge_; - - // Store instructions to variables of private storage - std::vector private_stores_; - // Live Instructions utils::BitVector live_insts_; diff --git a/third_party/spirv-tools/source/opt/block_merge_pass.cpp b/third_party/spirv-tools/source/opt/block_merge_pass.cpp index 04e47f1c2e..ef7f31fe0b 100644 --- a/third_party/spirv-tools/source/opt/block_merge_pass.cpp +++ b/third_party/spirv-tools/source/opt/block_merge_pass.cpp @@ -44,7 +44,7 @@ bool BlockMergePass::MergeBlocks(Function* func) { Pass::Status BlockMergePass::Process() { // Process all entry point functions. ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } 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 15e8c6ff9c..8ae8020a58 100644 --- a/third_party/spirv-tools/source/opt/block_merge_util.cpp +++ b/third_party/spirv-tools/source/opt/block_merge_util.cpp @@ -171,10 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func, // and OpBranchConditional. auto terminator = bi->terminator(); auto& vec = terminator->dbg_line_insts(); - auto& new_vec = merge_inst->dbg_line_insts(); - new_vec.insert(new_vec.end(), vec.begin(), vec.end()); - terminator->clear_dbg_line_insts(); - + if (vec.size() > 0) { + merge_inst->ClearDbgLineInsts(); + auto& new_vec = merge_inst->dbg_line_insts(); + new_vec.insert(new_vec.end(), vec.begin(), vec.end()); + terminator->ClearDbgLineInsts(); + for (auto& l_inst : new_vec) + context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); + } + // Clear debug scope of terminator to avoid DebugScope + // emitted between terminator and merge. + terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); // Move the merge instruction to just before the terminator. merge_inst->InsertBefore(terminator); } diff --git a/third_party/spirv-tools/source/opt/ccp_pass.cpp b/third_party/spirv-tools/source/opt/ccp_pass.cpp index d84f13f5d1..8b896d504b 100644 --- a/third_party/spirv-tools/source/opt/ccp_pass.cpp +++ b/third_party/spirv-tools/source/opt/ccp_pass.cpp @@ -291,6 +291,10 @@ bool CCPPass::ReplaceValues() { } bool CCPPass::PropagateConstants(Function* fp) { + if (fp->IsDeclaration()) { + return false; + } + // Mark function parameters as varying. fp->ForEachParam([this](const Instruction* inst) { values_[inst->result_id()] = kVaryingSSAId; diff --git a/third_party/spirv-tools/source/opt/combine_access_chains.cpp b/third_party/spirv-tools/source/opt/combine_access_chains.cpp index facfc24b6c..142897a2e1 100644 --- a/third_party/spirv-tools/source/opt/combine_access_chains.cpp +++ b/third_party/spirv-tools/source/opt/combine_access_chains.cpp @@ -34,6 +34,10 @@ Pass::Status CombineAccessChains::Process() { } bool CombineAccessChains::ProcessFunction(Function& function) { + if (function.IsDeclaration()) { + return false; + } + bool modified = false; cfg()->ForEachBlockInReversePostOrder( diff --git a/third_party/spirv-tools/source/opt/compact_ids_pass.cpp b/third_party/spirv-tools/source/opt/compact_ids_pass.cpp index 67091531c5..8815b8c658 100644 --- a/third_party/spirv-tools/source/opt/compact_ids_pass.cpp +++ b/third_party/spirv-tools/source/opt/compact_ids_pass.cpp @@ -86,9 +86,12 @@ Pass::Status CompactIdsPass::Process() { }, true); - if (modified) + if (modified) { context()->module()->SetIdBound( static_cast(result_id_mapping.size() + 1)); + // There are ids in the feature manager that could now be invalid + context()->ResetFeatureManager(); + } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/third_party/spirv-tools/source/opt/const_folding_rules.cpp b/third_party/spirv-tools/source/opt/const_folding_rules.cpp index d262a7ecab..515a3ed5df 100644 --- a/third_party/spirv-tools/source/opt/const_folding_rules.cpp +++ b/third_party/spirv-tools/source/opt/const_folding_rules.cpp @@ -1002,7 +1002,7 @@ const analysis::Constant* FoldClamp1( "Expecting a GLSLstd450 extended instruction."); // Make sure all Clamp operands are constants. - for (uint32_t i = 1; i < 3; i++) { + for (uint32_t i = 1; i < 4; i++) { if (constants[i] == nullptr) { return nullptr; } @@ -1017,7 +1017,7 @@ const analysis::Constant* FoldClamp1( context); } -// Fold a clamp instruction when |x >= min_val|. +// Fold a clamp instruction when |x <= min_val|. const analysis::Constant* FoldClamp2( IRContext* context, Instruction* inst, const std::vector& constants) { diff --git a/third_party/spirv-tools/source/opt/constants.cpp b/third_party/spirv-tools/source/opt/constants.cpp index 19ca6008da..020e248b29 100644 --- a/third_party/spirv-tools/source/opt/constants.cpp +++ b/third_party/spirv-tools/source/opt/constants.cpp @@ -217,7 +217,8 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule( auto* new_inst_ptr = new_inst.get(); *pos = pos->InsertBefore(std::move(new_inst)); ++(*pos); - context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); MapConstantToInst(new_const, new_inst_ptr); return new_inst_ptr; } @@ -432,6 +433,12 @@ uint32_t ConstantManager::GetSIntConst(int32_t val) { return GetDefiningInstruction(c)->result_id(); } +uint32_t ConstantManager::GetUIntConst(uint32_t val) { + Type* uint_type = context()->get_type_mgr()->GetUIntType(); + const Constant* c = GetConstant(uint_type, {val}); + return GetDefiningInstruction(c)->result_id(); +} + std::vector Constant::GetVectorComponents( analysis::ConstantManager* const_mgr) const { std::vector components; diff --git a/third_party/spirv-tools/source/opt/constants.h b/third_party/spirv-tools/source/opt/constants.h index 95d984fcd2..52bd809ab3 100644 --- a/third_party/spirv-tools/source/opt/constants.h +++ b/third_party/spirv-tools/source/opt/constants.h @@ -642,6 +642,9 @@ class ConstantManager { // Returns the id of a 32-bit signed integer constant with value |val|. uint32_t GetSIntConst(int32_t val); + // Returns the id of a 32-bit unsigned integer constant with value |val|. + uint32_t GetUIntConst(uint32_t val); + private: // Creates a Constant instance with the given type and a vector of constant // defining words. Returns a unique pointer to the created Constant instance diff --git a/third_party/spirv-tools/source/opt/control_dependence.cpp b/third_party/spirv-tools/source/opt/control_dependence.cpp new file mode 100644 index 0000000000..f4879e0f34 --- /dev/null +++ b/third_party/spirv-tools/source/opt/control_dependence.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/control_dependence.h" + +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/cfg.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "spirv/unified1/spirv.h" + +// Computes the control dependence graph (CDG) using the algorithm in Cytron +// 1991, "Efficiently Computing Static Single Assignment Form and the Control +// Dependence Graph." It relies on the fact that the control dependence sources +// (blocks on which a block is control dependent) are exactly the post-dominance +// frontier for that block. The explanation and proofs are given in Section 6 of +// that paper. +// Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf +// +// The algorithm in Section 4.2 of the same paper is used to construct the +// dominance frontier. It uses the post-dominance tree, which is available in +// the IR context. + +namespace spvtools { +namespace opt { +constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock; + +uint32_t ControlDependence::GetConditionID(const CFG& cfg) const { + if (source_bb_id() == 0) { + // Entry dependence; return 0. + return 0; + } + const BasicBlock* source_bb = cfg.block(source_bb_id()); + const Instruction* branch = source_bb->terminator(); + assert((branch->opcode() == SpvOpBranchConditional || + branch->opcode() == SpvOpSwitch) && + "invalid control dependence; last instruction must be conditional " + "branch or switch"); + return branch->GetSingleWordInOperand(0); +} + +bool ControlDependence::operator<(const ControlDependence& other) const { + return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) < + std::tie(other.source_bb_id_, other.target_bb_id_, + other.branch_target_bb_id_); +} + +bool ControlDependence::operator==(const ControlDependence& other) const { + return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) == + std::tie(other.source_bb_id_, other.target_bb_id_, + other.branch_target_bb_id_); +} + +std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) { + os << dep.source_bb_id() << "->" << dep.target_bb_id(); + if (dep.branch_target_bb_id() != dep.target_bb_id()) { + os << " through " << dep.branch_target_bb_id(); + } + return os; +} + +void ControlDependenceAnalysis::ComputePostDominanceFrontiers( + const CFG& cfg, const PostDominatorAnalysis& pdom) { + // Compute post-dominance frontiers (reverse graph). + // The dominance frontier for a block X is equal to (Equation 4) + // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) } + // (ipdom(Z) is the immediate post-dominator of Z.) + // where + // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y } + // represents the contribution of X's predecessors to the DF, and + // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y } + // (note: ipdom(Z) = X.) + // represents the contribution of a block to its immediate post- + // dominator's DF. + // This is computed in one pass through a post-order traversal of the + // post-dominator tree. + + // Assert that there is a block other than the pseudo exit in the pdom tree, + // as we need one to get the function entry point (as the pseudo exit is not + // actually part of the function.) + assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_)); + Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent(); + uint32_t function_entry = function->entry()->id(); + // Explicitly initialize pseudo-entry block, as it doesn't depend on anything, + // so it won't be initialized in the following loop. + reverse_nodes_[kPseudoEntryBlock] = {}; + for (auto it = pdom.GetDomTree().post_cbegin(); + it != pdom.GetDomTree().post_cend(); ++it) { + ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it); + } +} + +void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode( + const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry, + const DominatorTreeNode& pdom_node) { + const uint32_t label = pdom_node.id(); + ControlDependenceList& edges = reverse_nodes_[label]; + for (uint32_t pred : cfg.preds(label)) { + if (!pdom.StrictlyDominates(label, pred)) { + edges.push_back(ControlDependence(pred, label)); + } + } + if (label == function_entry) { + // Add edge from pseudo-entry to entry. + // In CDG construction, an edge is added from entry to exit, so only the + // exit node can post-dominate entry. + edges.push_back(ControlDependence(kPseudoEntryBlock, label)); + } + for (DominatorTreeNode* child : pdom_node) { + // Note: iterate dependences by value, as we need a copy. + for (const ControlDependence& dep : reverse_nodes_[child->id()]) { + // Special-case pseudo-entry, as above. + if (dep.source_bb_id() == kPseudoEntryBlock || + !pdom.StrictlyDominates(label, dep.source_bb_id())) { + edges.push_back(ControlDependence(dep.source_bb_id(), label, + dep.branch_target_bb_id())); + } + } + } +} + +void ControlDependenceAnalysis::ComputeControlDependenceGraph( + const CFG& cfg, const PostDominatorAnalysis& pdom) { + ComputePostDominanceFrontiers(cfg, pdom); + ComputeForwardGraphFromReverse(); +} + +void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() { + for (const auto& entry : reverse_nodes_) { + // Ensure an entry is created for each node. + forward_nodes_[entry.first]; + for (const ControlDependence& dep : entry.second) { + forward_nodes_[dep.source_bb_id()].push_back(dep); + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/control_dependence.h b/third_party/spirv-tools/source/opt/control_dependence.h new file mode 100644 index 0000000000..993f379364 --- /dev/null +++ b/third_party/spirv-tools/source/opt/control_dependence.h @@ -0,0 +1,197 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONTROL_DEPENDENCE_H_ +#define SOURCE_OPT_CONTROL_DEPENDENCE_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/dominator_analysis.h" + +namespace spvtools { +namespace opt { + +class ControlDependence { + public: + // The label of the source of this dependence, i.e. the block on which the + // target is dependent on. + // A |source_bb_id| of 0 represents an "entry" dependence, meaning that the + // execution of |target_bb_id| is only dependent on entry to the function. + uint32_t source_bb_id() const { return source_bb_id_; } + // The label of the target of this dependence, i.e. the block which is + // dependent on the source. + uint32_t target_bb_id() const { return target_bb_id_; } + // The label of the target of the *branch* for this dependence. + // Equal to the ID of the entry block for entry dependences. + // + // For example, for the partial CFG pictured below: + // 1 ---> 2 ---> 4 ---> 6 + // \ \ ^ + // \-> 3 \-> 5 -----/ + // Block 6 is control dependent on block 1, but this dependence comes from the + // branch 1 -> 2, so in this case the branch target ID would be 2. + uint32_t branch_target_bb_id() const { return branch_target_bb_id_; } + + // Create a direct control dependence from BB ID |source| to |target|. + ControlDependence(uint32_t source, uint32_t target) + : source_bb_id_(source), + target_bb_id_(target), + branch_target_bb_id_(target) {} + // Create a control dependence from BB ID |source| to |target| through the + // branch from |source| to |branch_target|. + ControlDependence(uint32_t source, uint32_t target, uint32_t branch_target) + : source_bb_id_(source), + target_bb_id_(target), + branch_target_bb_id_(branch_target) {} + + // Gets the ID of the conditional value for the branch corresponding to this + // control dependence. This is the first input operand for both + // OpConditionalBranch and OpSwitch. + // Returns 0 for entry dependences. + uint32_t GetConditionID(const CFG& cfg) const; + + bool operator==(const ControlDependence& other) const; + bool operator!=(const ControlDependence& other) const { + return !(*this == other); + } + + // Comparison operators, ordered lexicographically. Total ordering. + bool operator<(const ControlDependence& other) const; + bool operator>(const ControlDependence& other) const { return other < *this; } + bool operator<=(const ControlDependence& other) const { + return !(*this > other); + } + bool operator>=(const ControlDependence& other) const { + return !(*this < other); + } + + private: + uint32_t source_bb_id_; + uint32_t target_bb_id_; + uint32_t branch_target_bb_id_; +}; + +// Prints |dep| to |os| in a human-readable way. For example, +// 1->2 (target_bb_id = branch_target_bb_id = 2) +// 3->4 through 5 (target_bb_id = 4, branch_target_bb_id = 5) +std::ostream& operator<<(std::ostream& os, const ControlDependence& dep); + +// Represents the control dependence graph. A basic block is control dependent +// on another if the result of that block (e.g. the condition of a conditional +// branch) influences whether it is executed or not. More formally, a block A is +// control dependent on B iff: +// 1. there exists a path from A to the exit node that does *not* go through B +// (i.e., A does not postdominate B), and +// 2. there exists a path B -> b_1 -> ... -> b_n -> A such that A post-dominates +// all nodes b_i. +class ControlDependenceAnalysis { + public: + // Map basic block labels to control dependencies/dependents. + // Not guaranteed to be in any particular order. + using ControlDependenceList = std::vector; + using ControlDependenceListMap = + std::unordered_map; + + // 0, the label number for the pseudo entry block. + // All control dependences on the pseudo entry block are of type kEntry, and + // vice versa. + static constexpr uint32_t kPseudoEntryBlock = 0; + + // Build the control dependence graph for the given control flow graph |cfg| + // and corresponding post-dominator analysis |pdom|. + void ComputeControlDependenceGraph(const CFG& cfg, + const PostDominatorAnalysis& pdom); + + // Get the list of the nodes that depend on a block. + // Return value is not guaranteed to be in any particular order. + const ControlDependenceList& GetDependenceTargets(uint32_t block) const { + return forward_nodes_.at(block); + } + + // Get the list of the nodes on which a block depends on. + // Return value is not guaranteed to be in any particular order. + const ControlDependenceList& GetDependenceSources(uint32_t block) const { + return reverse_nodes_.at(block); + } + + // Runs the function |f| on each block label in the CDG. If any iteration + // returns false, immediately stops iteration and returns false. Otherwise + // returns true. Nodes are iterated in some undefined order, including the + // pseudo-entry block. + bool WhileEachBlockLabel(std::function f) const { + for (const auto& entry : forward_nodes_) { + if (!f(entry.first)) { + return false; + } + } + return true; + } + + // Runs the function |f| on each block label in the CDG. Nodes are iterated in + // some undefined order, including the pseudo-entry block. + void ForEachBlockLabel(std::function f) const { + WhileEachBlockLabel([&f](uint32_t label) { + f(label); + return true; + }); + } + + // Returns true if the block |id| exists in the control dependence graph. + // This can be false even if the block exists in the function when it is part + // of an infinite loop, since it is not part of the post-dominator tree. + bool HasBlock(uint32_t id) const { return forward_nodes_.count(id) > 0; } + + // Returns true if block |a| is dependent on block |b|. + bool IsDependent(uint32_t a, uint32_t b) const { + if (!HasBlock(a)) return false; + // BBs tend to have more dependents (targets) than they are dependent on + // (sources), so search sources. + const ControlDependenceList& a_sources = GetDependenceSources(a); + return std::find_if(a_sources.begin(), a_sources.end(), + [b](const ControlDependence& dep) { + return dep.source_bb_id() == b; + }) != a_sources.end(); + } + + private: + // Computes the post-dominance frontiers (i.e. the reverse CDG) for each node + // in the post-dominator tree. Only modifies reverse_nodes_; forward_nodes_ is + // not modified. + void ComputePostDominanceFrontiers(const CFG& cfg, + const PostDominatorAnalysis& pdom); + // Computes the post-dominance frontier for a specific node |pdom_node| in the + // post-dominator tree. Result is placed in reverse_nodes_[pdom_node.id()]. + void ComputePostDominanceFrontierForNode(const CFG& cfg, + const PostDominatorAnalysis& pdom, + uint32_t function_entry, + const DominatorTreeNode& pdom_node); + + // Computes the forward graph (forward_nodes_) from the reverse graph + // (reverse_nodes_). + void ComputeForwardGraphFromReverse(); + + ControlDependenceListMap forward_nodes_; + ControlDependenceListMap reverse_nodes_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONTROL_DEPENDENCE_H_ 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 6b3b540aa4..b127eabe96 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 @@ -177,18 +177,21 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) { return modified; } -bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { - // Add float16 converts of any float32 operands and change type - // of phi to float16 equivalent. Operand converts need to be added to - // preceeding blocks. +bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width, + uint32_t to_width) { + // Add converts of any float operands to to_width if they are of from_width. + // If converting to 16, change type of phi to float16 equivalent and remember + // result id. Converts need to be added to preceeding blocks. uint32_t ocnt = 0; uint32_t* prev_idp; - inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) { + bool modified = false; + inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified, + this](uint32_t* idp) { if (ocnt % 2 == 0) { prev_idp = idp; } else { Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp); - if (IsFloat(val_inst, 32)) { + if (IsFloat(val_inst, from_width)) { BasicBlock* bp = context()->get_instr_block(*idp); auto insert_before = bp->tail(); if (insert_before != bp->begin()) { @@ -197,15 +200,19 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { insert_before->opcode() != SpvOpLoopMerge) ++insert_before; } - GenConvert(prev_idp, 16, &*insert_before); + GenConvert(prev_idp, to_width, &*insert_before); + modified = true; } } ++ocnt; }); - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); - get_def_use_mgr()->AnalyzeInstUse(inst); - converted_ids_.insert(inst->result_id()); - return true; + if (to_width == 16u) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u)); + converted_ids_.insert(inst->result_id()); + modified = true; + } + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; } bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { @@ -242,9 +249,10 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) { } bool ConvertToHalfPass::ProcessDefault(Instruction* inst) { - bool modified = false; // If non-relaxed instruction has changed operands, need to convert // them back to float32 + if (inst->opcode() == SpvOpPhi) return ProcessPhi(inst, 16u, 32u); + bool modified = false; inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { if (converted_ids_.count(*idp) == 0) return; uint32_t old_id = *idp; @@ -262,7 +270,7 @@ bool ConvertToHalfPass::GenHalfInst(Instruction* inst) { if (IsArithmetic(inst) && inst_relaxed) modified = GenHalfArith(inst); else if (inst->opcode() == SpvOpPhi && inst_relaxed) - modified = ProcessPhi(inst); + modified = ProcessPhi(inst, 32u, 16u); else if (inst->opcode() == SpvOpFConvert) modified = ProcessConvert(inst); else if (image_ops_.count(inst->opcode()) != 0) @@ -340,7 +348,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() { Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(SpvCapabilityFloat16); // Remove all RelaxedPrecision decorations from instructions and globals 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 b647dd4a31..c6e84d1b74 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 @@ -93,7 +93,7 @@ class ConvertToHalfPass : public Pass { bool GenHalfArith(Instruction* inst); // Gen code for relaxed phi |inst| - bool ProcessPhi(Instruction* inst); + bool ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width); // Gen code for relaxed convert |inst| bool ProcessConvert(Instruction* inst); diff --git a/third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp b/third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp new file mode 100644 index 0000000000..e84d3578a8 --- /dev/null +++ b/third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp @@ -0,0 +1,437 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/convert_to_sampled_image_pass.h" + +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/util/make_unique.h" +#include "source/util/parse_number.h" + +namespace spvtools { +namespace opt { + +using VectorOfDescriptorSetAndBindingPairs = + std::vector; +using DescriptorSetBindingToInstruction = + ConvertToSampledImagePass::DescriptorSetBindingToInstruction; + +namespace { + +using utils::ParseNumber; + +// Returns true if the given char is ':', '\0' or considered as blank space +// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' '). +bool IsSeparator(char ch) { + return std::strchr(":\0", ch) || std::isspace(ch) != 0; +} + +// Reads characters starting from |str| until it meets a separator. Parses a +// number from the characters and stores it into |number|. Returns the pointer +// to the separator if it succeeds. Otherwise, returns nullptr. +const char* ParseNumberUntilSeparator(const char* str, uint32_t* number) { + const char* number_begin = str; + while (!IsSeparator(*str)) str++; + const char* number_end = str; + std::string number_in_str(number_begin, number_end - number_begin); + if (!utils::ParseNumber(number_in_str.c_str(), number)) { + // The descriptor set is not a valid uint32 number. + return nullptr; + } + return str; +} + +// Returns id of the image type used for the sampled image type of +// |sampled_image|. +uint32_t GetImageTypeOfSampledImage(analysis::TypeManager* type_mgr, + Instruction* sampled_image) { + auto* sampled_image_type = + type_mgr->GetType(sampled_image->type_id())->AsSampledImage(); + return type_mgr->GetTypeInstruction(sampled_image_type->image_type()); +} + +// Finds the instruction whose id is |inst_id|. Follows the operand of +// OpCopyObject recursively if the opcode of the instruction is OpCopyObject +// and returns the first instruction that does not have OpCopyObject as opcode. +Instruction* GetNonCopyObjectDef(analysis::DefUseManager* def_use_mgr, + uint32_t inst_id) { + Instruction* inst = def_use_mgr->GetDef(inst_id); + while (inst->opcode() == SpvOpCopyObject) { + inst_id = inst->GetSingleWordInOperand(0u); + inst = def_use_mgr->GetDef(inst_id); + } + return inst; +} + +} // namespace + +bool ConvertToSampledImagePass::GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const { + auto* decoration_manager = context()->get_decoration_mgr(); + bool found_descriptor_set_to_convert = false; + bool found_binding_to_convert = false; + for (auto decorate : + decoration_manager->GetDecorationsFor(inst.result_id(), false)) { + uint32_t decoration = decorate->GetSingleWordInOperand(1u); + if (decoration == SpvDecorationDescriptorSet) { + if (found_descriptor_set_to_convert) { + assert(false && "A resource has two OpDecorate for the descriptor set"); + return false; + } + descriptor_set_binding->descriptor_set = + decorate->GetSingleWordInOperand(2u); + found_descriptor_set_to_convert = true; + } else if (decoration == SpvDecorationBinding) { + if (found_binding_to_convert) { + assert(false && "A resource has two OpDecorate for the binding"); + return false; + } + descriptor_set_binding->binding = decorate->GetSingleWordInOperand(2u); + found_binding_to_convert = true; + } + } + return found_descriptor_set_to_convert && found_binding_to_convert; +} + +bool ConvertToSampledImagePass::ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding_pairs_.find(descriptor_set_binding) != + descriptor_set_binding_pairs_.end(); +} + +const analysis::Type* ConvertToSampledImagePass::GetVariableType( + const Instruction& variable) const { + if (variable.opcode() != SpvOpVariable) return nullptr; + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return nullptr; + + return pointer_type->pointee_type(); +} + +SpvStorageClass ConvertToSampledImagePass::GetStorageClass( + const Instruction& variable) const { + assert(variable.opcode() == SpvOpVariable); + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return SpvStorageClassMax; + + return pointer_type->storage_class(); +} + +bool ConvertToSampledImagePass::CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const { + for (auto& inst : context()->types_values()) { + const auto* variable_type = GetVariableType(inst); + if (variable_type == nullptr) continue; + + DescriptorSetAndBinding descriptor_set_binding; + if (!GetDescriptorSetBinding(inst, &descriptor_set_binding)) continue; + + if (!ShouldResourceBeConverted(descriptor_set_binding)) { + continue; + } + + if (variable_type->AsImage()) { + if (!descriptor_set_binding_pair_to_image + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } else if (variable_type->AsSampler()) { + if (!descriptor_set_binding_pair_to_sampler + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } + } + return true; +} + +Pass::Status ConvertToSampledImagePass::Process() { + Status status = Status::SuccessWithoutChange; + + DescriptorSetBindingToInstruction descriptor_set_binding_pair_to_sampler, + descriptor_set_binding_pair_to_image; + if (!CollectResourcesToConvert(&descriptor_set_binding_pair_to_sampler, + &descriptor_set_binding_pair_to_image)) { + return Status::Failure; + } + + for (auto& image : descriptor_set_binding_pair_to_image) { + status = CombineStatus( + status, UpdateImageVariableToSampledImage(image.second, image.first)); + if (status == Status::Failure) { + return status; + } + } + + for (const auto& sampler : descriptor_set_binding_pair_to_sampler) { + // Converting only a Sampler to Sampled Image is not allowed. It must have a + // corresponding image to combine the sampler with. + auto image_itr = descriptor_set_binding_pair_to_image.find(sampler.first); + if (image_itr == descriptor_set_binding_pair_to_image.end() || + image_itr->second == nullptr) { + return Status::Failure; + } + + status = CombineStatus( + status, CheckUsesOfSamplerVariable(sampler.second, image_itr->second)); + if (status == Status::Failure) { + return status; + } + } + + return status; +} + +void ConvertToSampledImagePass::FindUses(const Instruction* inst, + std::vector* uses, + uint32_t user_opcode) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(inst, [uses, user_opcode, this](Instruction* user) { + if (user->opcode() == user_opcode) { + uses->push_back(user); + } else if (user->opcode() == SpvOpCopyObject) { + FindUses(user, uses, user_opcode); + } + }); +} + +void ConvertToSampledImagePass::FindUsesOfImage( + const Instruction* image, std::vector* uses) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(image, [uses, this](Instruction* user) { + switch (user->opcode()) { + case SpvOpImageFetch: + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageQueryFormat: + case SpvOpImageQueryOrder: + case SpvOpImageQuerySizeLod: + case SpvOpImageQuerySize: + case SpvOpImageQueryLevels: + case SpvOpImageQuerySamples: + case SpvOpImageSparseFetch: + uses->push_back(user); + default: + break; + } + if (user->opcode() == SpvOpCopyObject) { + FindUsesOfImage(user, uses); + } + }); +} + +Instruction* ConvertToSampledImagePass::CreateImageExtraction( + Instruction* sampled_image) { + InstructionBuilder builder( + context(), sampled_image->NextNode(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + return builder.AddUnaryOp( + GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image), + SpvOpImage, sampled_image->result_id()); +} + +uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage( + Instruction* image_variable) { + const auto* variable_type = GetVariableType(*image_variable); + if (variable_type == nullptr) return 0; + const auto* image_type = variable_type->AsImage(); + if (image_type == nullptr) return 0; + + analysis::Image image_type_for_sampled_image(*image_type); + analysis::SampledImage sampled_image_type(&image_type_for_sampled_image); + return context()->get_type_mgr()->GetTypeInstruction(&sampled_image_type); +} + +Instruction* ConvertToSampledImagePass::UpdateImageUses( + Instruction* sampled_image_load) { + std::vector uses_of_load; + FindUsesOfImage(sampled_image_load, &uses_of_load); + if (uses_of_load.empty()) return nullptr; + + auto* extracted_image = CreateImageExtraction(sampled_image_load); + for (auto* user : uses_of_load) { + user->SetInOperand(0, {extracted_image->result_id()}); + context()->get_def_use_mgr()->AnalyzeInstUse(user); + } + return extracted_image; +} + +bool ConvertToSampledImagePass:: + IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding) { + auto* def_use_mgr = context()->get_def_use_mgr(); + uint32_t sampler_id = sampled_image_inst->GetSingleWordInOperand(1u); + auto* sampler_load = def_use_mgr->GetDef(sampler_id); + if (sampler_load->opcode() != SpvOpLoad) return false; + auto* sampler = def_use_mgr->GetDef(sampler_load->GetSingleWordInOperand(0u)); + DescriptorSetAndBinding sampler_descriptor_set_binding; + return GetDescriptorSetBinding(*sampler, &sampler_descriptor_set_binding) && + sampler_descriptor_set_binding == descriptor_set_binding; +} + +void ConvertToSampledImagePass::UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding) { + std::vector sampled_image_users; + FindUses(image_load, &sampled_image_users, SpvOpSampledImage); + + auto* def_use_mgr = context()->get_def_use_mgr(); + for (auto* sampled_image_inst : sampled_image_users) { + if (IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + sampled_image_inst, image_descriptor_set_binding)) { + context()->ReplaceAllUsesWith(sampled_image_inst->result_id(), + image_load->result_id()); + def_use_mgr->AnalyzeInstUse(image_load); + context()->KillInst(sampled_image_inst); + } else { + if (!image_extraction) + image_extraction = CreateImageExtraction(image_load); + sampled_image_inst->SetInOperand(0, {image_extraction->result_id()}); + def_use_mgr->AnalyzeInstUse(sampled_image_inst); + } + } +} + +void ConvertToSampledImagePass::MoveInstructionNextToType(Instruction* inst, + uint32_t type_id) { + auto* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + inst->SetResultType(type_id); + inst->RemoveFromList(); + inst->InsertAfter(type_inst); +} + +bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage( + Instruction* image_variable, uint32_t sampled_image_type_id) { + auto* sampled_image_type = + context()->get_type_mgr()->GetType(sampled_image_type_id); + if (sampled_image_type == nullptr) return false; + auto storage_class = GetStorageClass(*image_variable); + if (storage_class == SpvStorageClassMax) return false; + analysis::Pointer sampled_image_pointer(sampled_image_type, storage_class); + + // Make sure |image_variable| is behind its type i.e., avoid the forward + // reference. + uint32_t type_id = + context()->get_type_mgr()->GetTypeInstruction(&sampled_image_pointer); + MoveInstructionNextToType(image_variable, type_id); + return true; +} + +Pass::Status ConvertToSampledImagePass::UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding) { + std::vector image_variable_loads; + FindUses(image_variable, &image_variable_loads, SpvOpLoad); + if (image_variable_loads.empty()) return Status::SuccessWithoutChange; + + const uint32_t sampled_image_type_id = + GetSampledImageTypeForImage(image_variable); + if (!sampled_image_type_id) return Status::Failure; + + for (auto* load : image_variable_loads) { + load->SetResultType(sampled_image_type_id); + auto* image_extraction = UpdateImageUses(load); + UpdateSampledImageUses(load, image_extraction, descriptor_set_binding); + } + + return ConvertImageVariableToSampledImage(image_variable, + sampled_image_type_id) + ? Status::SuccessWithChange + : Status::Failure; +} + +bool ConvertToSampledImagePass::DoesSampledImageReferenceImage( + Instruction* sampled_image_inst, Instruction* image_variable) { + if (sampled_image_inst->opcode() != SpvOpSampledImage) return false; + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* image_load = GetNonCopyObjectDef( + def_use_mgr, sampled_image_inst->GetSingleWordInOperand(0u)); + if (image_load->opcode() != SpvOpLoad) return false; + auto* image = + GetNonCopyObjectDef(def_use_mgr, image_load->GetSingleWordInOperand(0u)); + return image->opcode() == SpvOpVariable && + image->result_id() == image_variable->result_id(); +} + +Pass::Status ConvertToSampledImagePass::CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with) { + if (image_to_be_combined_with == nullptr) return Status::Failure; + + std::vector sampler_variable_loads; + FindUses(sampler_variable, &sampler_variable_loads, SpvOpLoad); + for (auto* load : sampler_variable_loads) { + std::vector sampled_image_users; + FindUses(load, &sampled_image_users, SpvOpSampledImage); + for (auto* sampled_image_inst : sampled_image_users) { + if (!DoesSampledImageReferenceImage(sampled_image_inst, + image_to_be_combined_with)) { + return Status::Failure; + } + } + } + return Status::SuccessWithoutChange; +} + +std::unique_ptr +ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + const char* str) { + if (!str) return nullptr; + + auto descriptor_set_binding_pairs = + MakeUnique(); + + while (std::isspace(*str)) str++; // skip leading spaces. + + // The parsing loop, break when points to the end. + while (*str) { + // Parse the descriptor set. + uint32_t descriptor_set = 0; + str = ParseNumberUntilSeparator(str, &descriptor_set); + if (str == nullptr) return nullptr; + + // Find the ':', spaces between the descriptor set and the ':' are not + // allowed. + if (*str++ != ':') { + // ':' not found + return nullptr; + } + + // Parse the binding. + uint32_t binding = 0; + str = ParseNumberUntilSeparator(str, &binding); + if (str == nullptr) return nullptr; + + descriptor_set_binding_pairs->push_back({descriptor_set, binding}); + + // Skip trailing spaces. + while (std::isspace(*str)) str++; + } + + return descriptor_set_binding_pairs; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.h b/third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.h new file mode 100644 index 0000000000..d3938af7dc --- /dev/null +++ b/third_party/spirv-tools/source/opt/convert_to_sampled_image_pass.h @@ -0,0 +1,207 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ +#define SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ + +#include +#include +#include + +#include "source/opt/pass.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +// A struct for a pair of descriptor set and binding. +struct DescriptorSetAndBinding { + uint32_t descriptor_set; + uint32_t binding; + + bool operator==(const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding.descriptor_set == descriptor_set && + descriptor_set_binding.binding == binding; + } +}; + +// See optimizer.hpp for documentation. +class ConvertToSampledImagePass : public Pass { + public: + // Hashing functor for the pair of descriptor set and binding. + struct DescriptorSetAndBindingHash { + size_t operator()( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return std::hash()(descriptor_set_binding.descriptor_set) ^ + std::hash()(descriptor_set_binding.binding); + } + }; + + using SetOfDescriptorSetAndBindingPairs = + std::unordered_set; + using DescriptorSetBindingToInstruction = + std::unordered_map; + + explicit ConvertToSampledImagePass( + const std::vector& descriptor_set_binding_pairs) + : descriptor_set_binding_pairs_(descriptor_set_binding_pairs.begin(), + descriptor_set_binding_pairs.end()) {} + + const char* name() const override { return "convert-to-sampled-image"; } + Status Process() override; + + // Parses the given null-terminated C string to get a vector of descriptor set + // and binding pairs. Returns a unique pointer to the vector of descriptor set + // and binding pairs built from the given |str| on success. Returns a nullptr + // if the given string is not valid for building the vector of pairs. + // A valid string for building the vector of pairs should follow the rule + // below: + // + // ": : ..." + // Example: + // "3:5 2:1 0:4" + // + // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', + // '\f', '\v'). Each entry corresponds to a descriptor set and binding pair. + // Multiple spaces between, before or after entries are allowed. However, + // spaces are not allowed within a descriptor set or binding. + // + // In each entry, the descriptor set and binding are separated by ':'. + // Missing ':' in any entry is invalid. And it is invalid to have blank + // spaces in between the descriptor set and ':' or ':' and the binding. + // + // : the descriptor set. + // The text must represent a valid uint32_t number. + // + // : the binding. + // The text must represent a valid uint32_t number. + static std::unique_ptr> + ParseDescriptorSetBindingPairsString(const char* str); + + private: + // Collects resources to convert to sampled image and saves them in + // |descriptor_set_binding_pair_to_sampler| if the resource is a sampler and + // saves them in |descriptor_set_binding_pair_to_image| if the resource is an + // image. Returns false if two samplers or two images have the same descriptor + // set and binding. Otherwise, returns true. + bool CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const; + + // Finds an OpDecorate with DescriptorSet decorating |inst| and another + // OpDecorate with Binding decorating |inst|. Stores the descriptor set and + // binding in |descriptor_set_binding|. Returns whether it successfully finds + // the descriptor set and binding or not. + bool GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const; + + // Returns whether |descriptor_set_binding| is a pair of a descriptor set + // and a binding that we have to convert resources with it to a sampled image + // or not. + bool ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const; + + // Returns the pointee type of the type of variable |variable|. If |variable| + // is not an OpVariable instruction, just returns nullptr. + const analysis::Type* GetVariableType(const Instruction& variable) const; + + // Returns the storage class of |variable|. + SpvStorageClass GetStorageClass(const Instruction& variable) const; + + // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject + // instructions of |inst| whose opcode is |user_opcode| and puts them in + // |uses|. + void FindUses(const Instruction* inst, std::vector* uses, + uint32_t user_opcode) const; + + // Finds OpImage* instructions using |image| or OpCopyObject instructions that + // copy |image| and puts them in |uses|. + void FindUsesOfImage(const Instruction* image, + std::vector* uses) const; + + // Creates an OpImage instruction that extracts the image from the sampled + // image |sampled_image|. + Instruction* CreateImageExtraction(Instruction* sampled_image); + + // Converts |image_variable| whose type is an image pointer to sampled image + // type. Updates users of |image_variable| accordingly. If some instructions + // e.g., OpImageRead use |image_variable| as an Image operand, creates an + // image extracted from the sampled image using OpImage and replace the Image + // operands of the users with the extracted image. If some OpSampledImage + // instructions use |image_variable| and sampler whose descriptor set and + // binding are the same with |image_variable|, just combines |image_variable| + // and the sampler to a sampled image. + Pass::Status UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Returns the id of type sampled image type whose image type is the one of + // |image_variable|. + uint32_t GetSampledImageTypeForImage(Instruction* image_variable); + + // Moves |inst| next to the OpType* instruction with |type_id|. + void MoveInstructionNextToType(Instruction* inst, uint32_t type_id); + + // Converts |image_variable| whose type is an image pointer to sampled image + // with the type id |sampled_image_type_id|. Returns whether it successfully + // converts the type of |image_variable| or not. + bool ConvertImageVariableToSampledImage(Instruction* image_variable, + uint32_t sampled_image_type_id); + + // Replaces |sampled_image_load| instruction used by OpImage* with the image + // extracted from |sampled_image_load|. Returns the extracted image or nullptr + // if it does not have uses. + Instruction* UpdateImageUses(Instruction* sampled_image_load); + + // Returns true if the sampler of |sampled_image_inst| is decorated by a + // descriptor set and a binding |descriptor_set_binding|. + bool IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Replaces OpSampledImage instructions using |image_load| with |image_load| + // if the sampler of the OpSampledImage instruction has descriptor set and + // binding |image_descriptor_set_binding|. Otherwise, replaces |image_load| + // with |image_extraction|. + void UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding); + + // Checks the uses of |sampler_variable|. When a sampler is used by + // OpSampledImage instruction, the corresponding image must be + // |image_to_be_combined_with| that should be already converted to a sampled + // image by UpdateImageVariableToSampledImage() method. + Pass::Status CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with); + + // Returns true if Image operand of |sampled_image_inst| is the image of + // |image_variable|. + bool DoesSampledImageReferenceImage(Instruction* sampled_image_inst, + Instruction* image_variable); + + // A set of pairs of descriptor set and binding. If an image and/or a sampler + // have a pair of descriptor set and binding that is an element of + // |descriptor_set_binding_pairs_|, they/it will be converted to a sampled + // image by this pass. + const SetOfDescriptorSetAndBindingPairs descriptor_set_binding_pairs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp b/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp index 67a97b6af6..62ed5e77ab 100644 --- a/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp +++ b/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp @@ -29,10 +29,10 @@ const uint32_t kCompositeExtractObjectInOperand = 0; const uint32_t kTypePointerStorageClassInIdx = 0; const uint32_t kTypePointerPointeeInIdx = 1; -bool IsOpenCL100DebugDeclareOrValue(Instruction* di) { - auto dbg_opcode = di->GetOpenCL100DebugOpcode(); - return dbg_opcode == OpenCLDebugInfo100DebugDeclare || - dbg_opcode == OpenCLDebugInfo100DebugValue; +bool IsDebugDeclareOrValue(Instruction* di) { + auto dbg_opcode = di->GetCommonDebugOpcode(); + return dbg_opcode == CommonDebugInfoDebugDeclare || + dbg_opcode == CommonDebugInfoDebugValue; } } // namespace @@ -40,6 +40,10 @@ bool IsOpenCL100DebugDeclareOrValue(Instruction* di) { Pass::Status CopyPropagateArrays::Process() { bool modified = false; for (Function& function : *get_module()) { + if (function.IsDeclaration()) { + continue; + } + BasicBlock* entry_bb = &*function.begin(); for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable; @@ -194,7 +198,7 @@ bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst, return ptr_inst->opcode() == SpvOpVariable && store_inst->GetSingleWordInOperand(kStorePointerInOperand) == ptr_inst->result_id(); - } else if (IsOpenCL100DebugDeclareOrValue(use)) { + } else if (IsDebugDeclareOrValue(use)) { return true; } // Some other instruction. Be conservative. @@ -500,7 +504,7 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, const_mgr, type](Instruction* use, uint32_t) { - if (IsOpenCL100DebugDeclareOrValue(use)) return true; + if (IsDebugDeclareOrValue(use)) return true; switch (use->opcode()) { case SpvOpLoad: { @@ -592,9 +596,9 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, Instruction* use = pair.first; uint32_t index = pair.second; - if (use->IsOpenCL100DebugInstr()) { - switch (use->GetOpenCL100DebugOpcode()) { - case OpenCLDebugInfo100DebugDeclare: { + if (use->IsCommonDebugInstr()) { + switch (use->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: { if (new_ptr_inst->opcode() == SpvOpVariable || new_ptr_inst->opcode() == SpvOpFunctionParameter) { context()->ForgetUses(use); @@ -608,9 +612,8 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, context()->ForgetUses(use); // Change DebugDeclare to DebugValue. - use->SetOperand( - index - 2, - {static_cast(OpenCLDebugInfo100DebugValue)}); + use->SetOperand(index - 2, + {static_cast(CommonDebugInfoDebugValue)}); use->SetOperand(index, {new_ptr_inst->result_id()}); // Add Deref operation. @@ -625,7 +628,7 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, } break; } - case OpenCLDebugInfo100DebugValue: + case CommonDebugInfoDebugValue: context()->ForgetUses(use); use->SetOperand(index, {new_ptr_inst->result_id()}); context()->AnalyzeUses(use); diff --git a/third_party/spirv-tools/source/opt/dataflow.cpp b/third_party/spirv-tools/source/opt/dataflow.cpp new file mode 100644 index 0000000000..c91fad08e7 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dataflow.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dataflow.h" + +#include +#include + +namespace spvtools { +namespace opt { + +bool DataFlowAnalysis::Enqueue(Instruction* inst) { + bool& is_enqueued = on_worklist_[inst]; + if (is_enqueued) return false; + is_enqueued = true; + worklist_.push(inst); + return true; +} + +DataFlowAnalysis::VisitResult DataFlowAnalysis::RunOnce( + Function* function, bool is_first_iteration) { + InitializeWorklist(function, is_first_iteration); + VisitResult ret = VisitResult::kResultFixed; + while (!worklist_.empty()) { + Instruction* top = worklist_.front(); + worklist_.pop(); + on_worklist_[top] = false; + VisitResult result = Visit(top); + if (result == VisitResult::kResultChanged) { + EnqueueSuccessors(top); + ret = VisitResult::kResultChanged; + } + } + return ret; +} + +void DataFlowAnalysis::Run(Function* function) { + VisitResult result = RunOnce(function, true); + while (result == VisitResult::kResultChanged) { + result = RunOnce(function, false); + } +} + +void ForwardDataFlowAnalysis::InitializeWorklist(Function* function, + bool /*is_first_iteration*/) { + context().cfg()->ForEachBlockInReversePostOrder( + function->entry().get(), [this](BasicBlock* bb) { + if (label_position_ == LabelPosition::kLabelsOnly) { + Enqueue(bb->GetLabelInst()); + return; + } + if (label_position_ == LabelPosition::kLabelsAtBeginning) { + Enqueue(bb->GetLabelInst()); + } + for (Instruction& inst : *bb) { + Enqueue(&inst); + } + if (label_position_ == LabelPosition::kLabelsAtEnd) { + Enqueue(bb->GetLabelInst()); + } + }); +} + +void ForwardDataFlowAnalysis::EnqueueUsers(Instruction* inst) { + context().get_def_use_mgr()->ForEachUser( + inst, [this](Instruction* user) { Enqueue(user); }); +} + +void ForwardDataFlowAnalysis::EnqueueBlockSuccessors(Instruction* inst) { + if (inst->opcode() != SpvOpLabel) return; + context() + .cfg() + ->block(inst->result_id()) + ->ForEachSuccessorLabel([this](uint32_t* label) { + Enqueue(context().cfg()->block(*label)->GetLabelInst()); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/dataflow.h b/third_party/spirv-tools/source/opt/dataflow.h new file mode 100644 index 0000000000..be07415db5 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dataflow.h @@ -0,0 +1,148 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DATAFLOW_H_ +#define SOURCE_OPT_DATAFLOW_H_ + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Generic data-flow analysis. +// Maintains a worklist of instructions to process and processes them in a +// specified order. See also ForwardDataFlowAnalysis, which is specialized for +// forward data-flow analysis. +class DataFlowAnalysis { + public: + // The result of a |Visit| operation on an instruction. + // This is used to determine when analysis has reached a fixpoint. + enum class VisitResult { + // The analysis result for this instruction has changed. + // This means that any instructions that depend on it (its successors) must + // be recomputed. + kResultChanged, + // The analysis result for this instruction has not changed. + // When all visit operations return |kResultFixed|, the analysis has reached + // a fixpoint (converged). + kResultFixed, + }; + + virtual ~DataFlowAnalysis() {} + + // Run this analysis on a given function. + // For analyses which work interprocedurally, |function| may be ignored. + void Run(Function* function); + + protected: + DataFlowAnalysis(IRContext& context) : context_(context) {} + + // Initialize the worklist for a given function. + // |is_first_iteration| is true on the first call to |Run| and false + // afterwards. All subsequent runs are only necessary to check if the analysis + // has converged; if |EnqueueSuccessors| is complete, |InitializeWorklist| + // should do nothing after the first iteration. + virtual void InitializeWorklist(Function* function, + bool is_first_iteration) = 0; + + // Enqueues the successors (instructions which use the analysis result) of + // |inst|. This is not required to be complete, but convergence is faster when + // it is. This is called whenever |Visit| returns |kResultChanged|. + virtual void EnqueueSuccessors(Instruction* inst) = 0; + + // Visits the given instruction, recomputing the analysis result. This is + // called once per instruction queued in |InitializeWorklist| and afterward + // when a predecessor is changed, through |EnqueueSuccessors|. + virtual VisitResult Visit(Instruction* inst) = 0; + + // Enqueues the given instruction to be visited. Ignored if already in the + // worklist. + bool Enqueue(Instruction* inst); + + IRContext& context() { return context_; } + + private: + // Runs one pass, calling |InitializeWorklist| and then iterating through the + // worklist until all fixed. + VisitResult RunOnce(Function* function, bool is_first_iteration); + + IRContext& context_; + std::unordered_map on_worklist_; + // The worklist, which contains the list of instructions to be visited. + // + // The choice of data structure was influenced by the data in "Iterative + // Data-flow Analysis, Revisited" (Cooper et al, 2002). + // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.125.1549&rep=rep1&type=pdf + // The paper shows that the overall performance benefit of a priority queue + // over a regular queue or stack is relatively small (or negative). + // + // A queue has the advantage that nodes are visited in the same order they are + // enqueued, which relieves the analysis from inserting nodes "backwards", for + // example in worklist initialization. Also, as the paper claims that sorting + // successors does not improve runtime, we can use a single queue which is + // modified during iteration. + std::queue worklist_; +}; + +// A generic data flow analysis, specialized for forward analysis. +class ForwardDataFlowAnalysis : public DataFlowAnalysis { + public: + // Indicates where labels should be in the worklist RPO ordering. + enum class LabelPosition { + // Labels should be placed at the beginning of their blocks. + kLabelsAtBeginning, + // Labels should be placed at the end of their blocks. + kLabelsAtEnd, + // Labels should not be in the worklist. + kNoLabels, + // Only labels should be placed in the worklist. + kLabelsOnly, + }; + + ForwardDataFlowAnalysis(IRContext& context, LabelPosition label_position) + : DataFlowAnalysis(context), label_position_(label_position) {} + + protected: + // Initializes the worklist in reverse postorder, regardless of + // |is_first_iteration|. Labels are placed according to the label position + // specified in the constructor. + void InitializeWorklist(Function* function, bool is_first_iteration) override; + + // Enqueues the users and block successors of the given instruction. + // See |EnqueueUsers| and |EnqueueBlockSuccessors|. + void EnqueueSuccessors(Instruction* inst) override { + EnqueueUsers(inst); + EnqueueBlockSuccessors(inst); + } + + // Enqueues the users of the given instruction. + void EnqueueUsers(Instruction* inst); + + // Enqueues the labels of the successors of the block corresponding to the + // given label instruction. Does nothing for other instructions. + void EnqueueBlockSuccessors(Instruction* inst); + + private: + LabelPosition label_position_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DATAFLOW_H_ diff --git a/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp b/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp index 0054f57647..356dbcb34a 100644 --- a/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp +++ b/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp @@ -346,6 +346,7 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks( if (operands.size() == 4) { // First input data operands is at index 2. uint32_t replId = operands[2u].words[0]; + context()->KillNamesAndDecorates(inst->result_id()); context()->ReplaceAllUsesWith(inst->result_id(), replId); iter = context()->KillInst(&*inst); } else { @@ -419,6 +420,10 @@ bool DeadBranchElimPass::EraseDeadBlocks( } bool DeadBranchElimPass::EliminateDeadBranches(Function* func) { + if (func->IsDeclaration()) { + return false; + } + bool modified = false; std::unordered_set live_blocks; modified |= MarkLiveBlocks(func, &live_blocks); diff --git a/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp b/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp index 46f4f12480..d877f0f96a 100644 --- a/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp +++ b/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp @@ -196,7 +196,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) { } const uint32_t id = ii->result_id(); get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) { - if (user->IsOpenCL100DebugInstr()) return; + if (user->IsCommonDebugInstr()) return; switch (user->opcode()) { case SpvOpCompositeInsert: case SpvOpPhi: @@ -256,7 +256,7 @@ Pass::Status DeadInsertElimPass::Process() { ProcessFunction pfn = [this](Function* fp) { return EliminateDeadInserts(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/third_party/spirv-tools/source/opt/debug_info_manager.cpp b/third_party/spirv-tools/source/opt/debug_info_manager.cpp index e782641f22..060e0d9312 100644 --- a/third_party/spirv-tools/source/opt/debug_info_manager.cpp +++ b/third_party/spirv-tools/source/opt/debug_info_manager.cpp @@ -18,12 +18,15 @@ #include "source/opt/ir_context.h" -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100 +// extension instructions. static const uint32_t kOpLineOperandLineIndex = 1; static const uint32_t kLineOperandIndexDebugFunction = 7; static const uint32_t kLineOperandIndexDebugLexicalBlock = 5; static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4; +static const uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5; static const uint32_t kDebugFunctionOperandParentIndex = 9; static const uint32_t kDebugTypeCompositeOperandParentIndex = 9; static const uint32_t kDebugLexicalBlockOperandParentIndex = 7; @@ -46,8 +49,8 @@ namespace { void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { assert(dbg_inlined_at); - assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInlinedAt); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) { dbg_inlined_at->AddOperand( {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}}); @@ -59,8 +62,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { assert(dbg_inlined_at); - assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInlinedAt); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) return kNoInlinedAt; return dbg_inlined_at->GetSingleWordOperand( @@ -68,8 +71,7 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { } bool IsEmptyDebugExpression(Instruction* instr) { - return instr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugExpression && + return (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression) && instr->NumOperands() == kDebugExpressOperandOperationIndex; } @@ -79,43 +81,63 @@ DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) { AnalyzeDebugInsts(*c->module()); } +uint32_t DebugInfoManager::GetDbgSetImportId() { + uint32_t setId = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + if (setId == 0) { + setId = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + } + return setId; +} + Instruction* DebugInfoManager::GetDbgInst(uint32_t id) { auto dbg_inst_it = id_to_dbg_inst_.find(id); return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second; } void DebugInfoManager::RegisterDbgInst(Instruction* inst) { - assert( - inst->NumInOperands() != 0 && - context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == - inst->GetInOperand(0).words[0] && - "Given instruction is not a debug instruction"); + assert(inst->NumInOperands() != 0 && + (GetDbgSetImportId() == inst->GetInOperand(0).words[0]) && + "Given instruction is not a debug instruction"); id_to_dbg_inst_[inst->result_id()] = inst; } void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { - assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction && - "inst is not a DebugFunction"); - auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); - // Do not register function that has been optimized away - auto fn_inst = GetDbgInst(fn_id); - if (fn_inst != nullptr) { - assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInfoNone); - return; + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + // Do not register function that has been optimized away. + auto fn_inst = GetDbgInst(fn_id); + if (fn_inst != nullptr) { + assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInfoNone); + return; + } + assert( + fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunction for a function that already has DebugFunction"); + fn_id_to_dbg_fn_[fn_id] = inst; + } else if (inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + auto fn_id = inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + auto fn_inst = GetDbgInst(inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandDebugFunctionIndex)); + assert(fn_inst && fn_inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunction); + assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunctionDefinition for a function that already has " + "DebugFunctionDefinition"); + fn_id_to_dbg_fn_[fn_id] = fn_inst; + } else { + assert(false && "inst is not a DebugFunction"); } - assert( - fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && - "Register DebugFunction for a function that already has DebugFunction"); - fn_id_to_dbg_fn_[fn_id] = inst; } void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare) { - assert(dbg_declare->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare || - dbg_declare->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugValue); + assert(dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugValue); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id); if (dbg_decl_itr == var_id_to_dbg_decl_.end()) { var_id_to_dbg_decl_[var_id] = {dbg_declare}; @@ -124,29 +146,57 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, } } +// Create new constant directly into global value area, bypassing the +// Constant manager. This is used when the DefUse or Constant managers +// are invalid and cannot be regenerated due to the module being in an +// inconsistant state e.g. in the middle of significant modification +// such as inlining. Invalidate Constant and DefUse managers if used. +uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) { + uint32_t id = context->TakeNextId(); + std::unique_ptr new_const(new Instruction( + context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + {const_value}}, + })); + context->module()->AddGlobalValue(std::move(new_const)); + context->InvalidateAnalyses(IRContext::kAnalysisConstants); + context->InvalidateAnalyses(IRContext::kAnalysisDefUse); + return id; +} + uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, const DebugScope& scope) { - if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == - 0) - return kNoInlinedAt; + uint32_t setId = GetDbgSetImportId(); + + if (setId == 0) return kNoInlinedAt; + + spv_operand_type_t line_number_type = + spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER; + + // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant, + // not literals. + if (setId == + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) + line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID; uint32_t line_number = 0; if (line == nullptr) { auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope()); if (lexical_scope_inst == nullptr) return kNoInlinedAt; - OpenCLDebugInfo100Instructions debug_opcode = - lexical_scope_inst->GetOpenCL100DebugOpcode(); + CommonDebugInfoInstructions debug_opcode = + lexical_scope_inst->GetCommonDebugOpcode(); switch (debug_opcode) { - case OpenCLDebugInfo100DebugFunction: + case CommonDebugInfoDebugFunction: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugFunction); break; - case OpenCLDebugInfo100DebugLexicalBlock: + case CommonDebugInfoDebugLexicalBlock: line_number = lexical_scope_inst->GetSingleWordOperand( kLineOperandIndexDebugLexicalBlock); break; - case OpenCLDebugInfo100DebugTypeComposite: - case OpenCLDebugInfo100DebugCompilationUnit: + case CommonDebugInfoDebugTypeComposite: + case CommonDebugInfoDebugCompilationUnit: assert(false && "DebugTypeComposite and DebugCompilationUnit are lexical " "scopes, but we inline functions into a function or a block " @@ -161,6 +211,21 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, } } else { line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); + + // If we need the line number as an ID, generate that constant now. + // If Constant or DefUse managers are invalid, generate constant + // directly into the global value section of the module; do not + // use Constant manager which may attempt to invoke building of the + // DefUse manager which cannot be done during inlining. The extra + // constants that may be generated here is likely not significant + // and will likely be cleaned up in later passes. + if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) { + if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) || + !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants)) + line_number = AddNewConstInGlobals(context(), line_number); + else + line_number = context()->get_constant_mgr()->GetUIntConst(line_number); + } } uint32_t result_id = context()->TakeNextId(); @@ -168,13 +233,10 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {setId}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugInlinedAt)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {static_cast(CommonDebugInfoDebugInlinedAt)}}, + {line_number_type, {line_number}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}}, })); // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt @@ -257,19 +319,34 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { if (deref_operation_ != nullptr) return deref_operation_; uint32_t result_id = context()->TakeNextId(); - std::unique_ptr deref_operation(new Instruction( - context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), - result_id, - { - {SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, - {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugOperation)}}, - {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, - {static_cast(OpenCLDebugInfo100Deref)}}, - })); + std::unique_ptr deref_operation; + + if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + deref_operation = std::unique_ptr(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, + {static_cast(OpenCLDebugInfo100Deref)}}, + })); + } else { + uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst( + NonSemanticShaderDebugInfo100Deref); + + deref_operation = std::unique_ptr( + new Instruction(context(), SpvOpExtInst, + context()->get_type_mgr()->GetVoidTypeId(), result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast( + NonSemanticShaderDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_ID, {deref_id}}, + })); + } // Add to the front of |ext_inst_debuginfo_|. deref_operation_ = @@ -283,8 +360,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() { } Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { - assert(dbg_expr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugExpression); + assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression); std::unique_ptr deref_expr(dbg_expr->Clone(context())); deref_expr->SetResultId(context()->TakeNextId()); deref_expr->InsertOperand( @@ -306,12 +382,9 @@ Instruction* DebugInfoManager::GetDebugInfoNone() { context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugInfoNone)}}, + {static_cast(CommonDebugInfoDebugInfoNone)}}, })); // Add to the front of |ext_inst_debuginfo_|. @@ -333,12 +406,9 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() { context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugExpression)}}, + {static_cast(CommonDebugInfoDebugExpression)}}, })); // Add to the front of |ext_inst_debuginfo_|. @@ -355,8 +425,7 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() { Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) { auto* inlined_at = GetDbgInst(dbg_inlined_at_id); if (inlined_at == nullptr) return nullptr; - if (inlined_at->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugInlinedAt) { + if (inlined_at->GetCommonDebugOpcode() != CommonDebugInfoDebugInlinedAt) { return nullptr; } return inlined_at; @@ -403,23 +472,23 @@ bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) { auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope); assert(dbg_scope_itr != id_to_dbg_inst_.end()); - OpenCLDebugInfo100Instructions debug_opcode = - dbg_scope_itr->second->GetOpenCL100DebugOpcode(); + CommonDebugInfoInstructions debug_opcode = + dbg_scope_itr->second->GetCommonDebugOpcode(); uint32_t parent_scope = kNoDebugScope; switch (debug_opcode) { - case OpenCLDebugInfo100DebugFunction: + case CommonDebugInfoDebugFunction: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugFunctionOperandParentIndex); break; - case OpenCLDebugInfo100DebugLexicalBlock: + case CommonDebugInfoDebugLexicalBlock: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugLexicalBlockOperandParentIndex); break; - case OpenCLDebugInfo100DebugTypeComposite: + case CommonDebugInfoDebugTypeComposite: parent_scope = dbg_scope_itr->second->GetSingleWordOperand( kDebugTypeCompositeOperandParentIndex); break; - case OpenCLDebugInfo100DebugCompilationUnit: + case CommonDebugInfoDebugCompilationUnit: // DebugCompilationUnit does not have a parent scope. break; default: @@ -513,8 +582,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( std::unique_ptr dbg_val(dbg_decl->Clone(context())); dbg_val->SetResultId(context()->TakeNextId()); - dbg_val->SetInOperand(kExtInstInstructionInIdx, - {OpenCLDebugInfo100DebugValue}); + dbg_val->SetInOperand(kExtInstInstructionInIdx, {CommonDebugInfoDebugValue}); dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); dbg_val->SetOperand(kDebugValueOperandExpressionIndex, {GetEmptyDebugExpression()->result_id()}); @@ -532,9 +600,20 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( return added_dbg_val; } +uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) { + assert(inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation && + "inst must be Vulkan DebugOperation"); + return context() + ->get_constant_mgr() + ->GetConstantFromInst(context()->get_def_use_mgr()->GetDef( + inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex))) + ->GetU32(); +} + uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( Instruction* inst) { - if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0; + if (inst->GetCommonDebugOpcode() != CommonDebugInfoDebugValue) return 0; auto* expr = GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); @@ -544,9 +623,19 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( auto* operation = GetDbgInst( expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex)); if (operation == nullptr) return 0; - if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != - OpenCLDebugInfo100Deref) { - return 0; + + // OpenCL.DebugInfo.100 contains a literal for the operation, Vulkan uses an + // OpConstant. + if (inst->IsOpenCL100DebugInstr()) { + if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != + OpenCLDebugInfo100Deref) { + return 0; + } + } else { + uint32_t operation_const = GetVulkanDebugOperation(operation); + if (operation_const != NonSemanticShaderDebugInfo100Deref) { + return 0; + } } uint32_t var_id = @@ -567,8 +656,8 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( } bool DebugInfoManager::IsDebugDeclare(Instruction* instr) { - if (!instr->IsOpenCL100DebugInstr()) return false; - return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || + if (!instr->IsCommonDebugInstr()) return false; + return instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || GetVariableIdOfDebugValueUsedForDeclare(instr) != 0; } @@ -615,14 +704,13 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { users.insert(inst); } - if (!inst->IsOpenCL100DebugInstr()) return; + if (!inst->IsCommonDebugInstr()) return; RegisterDbgInst(inst); - if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { - assert(GetDebugFunction(inst->GetSingleWordOperand( - kDebugFunctionOperandFunctionIndex)) == nullptr && - "Two DebugFunction instruction exists for a single OpFunction."); + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction || + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { RegisterDbgFunction(inst); } @@ -633,8 +721,17 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { deref_operation_ = inst; } + if (deref_operation_ == nullptr && + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(inst); + if (operation_const == NonSemanticShaderDebugInfo100Deref) { + deref_operation_ = inst; + } + } + if (debug_info_none_inst_ == nullptr && - inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + inst->GetCommonDebugOpcode() == CommonDebugInfoDebugInfoNone) { debug_info_none_inst_ = inst; } @@ -642,7 +739,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { empty_debug_expr_inst_ = inst; } - if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { uint32_t var_id = inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); RegisterDbgDeclare(var_id, inst); @@ -655,8 +752,8 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { void DebugInfoManager::ConvertDebugGlobalToLocalVariable( Instruction* dbg_global_var, Instruction* local_var) { - if (dbg_global_var->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugGlobalVariable) { + if (dbg_global_var->GetCommonDebugOpcode() != + CommonDebugInfoDebugGlobalVariable) { return; } assert(local_var->opcode() == SpvOpVariable || @@ -664,7 +761,7 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( // Convert |dbg_global_var| to DebugLocalVariable dbg_global_var->SetInOperand(kExtInstInstructionInIdx, - {OpenCLDebugInfo100DebugLocalVariable}); + {CommonDebugInfoDebugLocalVariable}); auto flags = dbg_global_var->GetSingleWordOperand( kDebugGlobalVariableOperandFlagsIndex); for (uint32_t i = dbg_global_var->NumInOperands() - 1; @@ -680,20 +777,20 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable( context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), context()->TakeNextId(), { - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {context() - ->get_feature_mgr() - ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast(OpenCLDebugInfo100DebugDeclare)}}, + {static_cast(CommonDebugInfoDebugDeclare)}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_global_var->result_id()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetEmptyDebugExpression()->result_id()}}, })); - auto* added_dbg_decl = - local_var->NextNode()->InsertBefore(std::move(new_dbg_decl)); + // Must insert after all OpVariables in block + Instruction* insert_before = local_var; + while (insert_before->opcode() == SpvOpVariable) + insert_before = insert_before->NextNode(); + auto* added_dbg_decl = insert_before->InsertBefore(std::move(new_dbg_decl)); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl); if (context()->AreAnalysesValid( @@ -713,7 +810,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (empty_debug_expr_inst_ != nullptr && empty_debug_expr_inst_->PreviousNode() != nullptr && - empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + empty_debug_expr_inst_->PreviousNode()->IsCommonDebugInstr()) { empty_debug_expr_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -722,7 +819,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (debug_info_none_inst_ != nullptr && debug_info_none_inst_->PreviousNode() != nullptr && - debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + debug_info_none_inst_->PreviousNode()->IsCommonDebugInstr()) { debug_info_none_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -740,7 +837,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { inlinedat_id_to_users_itr->second.erase(instr); } - if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) { + if (instr == nullptr || !instr->IsCommonDebugInstr()) { return; } @@ -751,9 +848,15 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); } + if (instr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunction) { + auto fn_id = instr->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + fn_id_to_dbg_fn_.erase(fn_id); + } - if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || - instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + instr->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { auto var_or_value_id = instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id); @@ -767,6 +870,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { + // OpenCL.DebugInfo.100 contains the operation as a literal operand, in + // Vulkan it's referenced as an OpConstant. if (instr != &*dbg_instr_itr && dbg_instr_itr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && @@ -775,6 +880,14 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { OpenCLDebugInfo100Deref) { deref_operation_ = &*dbg_instr_itr; break; + } else if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr); + if (operation_const == NonSemanticShaderDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } } } } @@ -784,9 +897,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); ++dbg_instr_itr) { - if (instr != &*dbg_instr_itr && - dbg_instr_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugInfoNone) { + if (instr != &*dbg_instr_itr && dbg_instr_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugInfoNone) { debug_info_none_inst_ = &*dbg_instr_itr; break; } diff --git a/third_party/spirv-tools/source/opt/debug_info_manager.h b/third_party/spirv-tools/source/opt/debug_info_manager.h index 776e9baab5..df34b30f40 100644 --- a/third_party/spirv-tools/source/opt/debug_info_manager.h +++ b/third_party/spirv-tools/source/opt/debug_info_manager.h @@ -67,8 +67,8 @@ class DebugInlinedAtContext { std::unordered_map callee_inlined_at2chain_; }; -// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension -// instructions. +// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and +// NonSemantic.Shader.DebugInfo.100 extension instructions. class DebugInfoManager { public: // Constructs a debug information manager from the given |context|. @@ -85,7 +85,7 @@ class DebugInfoManager { return !(lhs == rhs); } - // Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|. + // Analyzes DebugInfo instruction |dbg_inst|. void AnalyzeDebugInst(Instruction* dbg_inst); // Creates new DebugInlinedAt and returns its id. Its line operand is the @@ -164,6 +164,9 @@ class DebugInfoManager { // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr); + // Return the opcode for the Vulkan DebugOperation inst + uint32_t GetVulkanDebugOperation(Instruction* inst); + // Returns the id of Value operand if |inst| is DebugValue who has Deref // operation and its Value operand is a result id of OpVariable with // Function storage class. Otherwise, returns 0. @@ -190,10 +193,13 @@ class DebugInfoManager { private: IRContext* context() { return context_; } - // Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and + // Analyzes DebugInfo instructions in the given |module| and // populates data structures in this class. void AnalyzeDebugInsts(Module& module); + // Get the DebugInfo ExtInstImport Id, or 0 if no DebugInfo is available. + uint32_t GetDbgSetImportId(); + // Returns the debug instruction whose id is |id|. Returns |nullptr| if one // does not exists. Instruction* GetDbgInst(uint32_t id); @@ -230,7 +236,7 @@ class DebugInfoManager { IRContext* context_; - // Mapping from ids of OpenCL.DebugInfo.100 extension instructions + // Mapping from ids of DebugInfo extension instructions. // to their Instruction instances. std::unordered_map id_to_dbg_inst_; diff --git a/third_party/spirv-tools/source/opt/decoration_manager.cpp b/third_party/spirv-tools/source/opt/decoration_manager.cpp index 4bf026efd8..2146c359d6 100644 --- a/third_party/spirv-tools/source/opt/decoration_manager.cpp +++ b/third_party/spirv-tools/source/opt/decoration_manager.cpp @@ -490,6 +490,14 @@ void DecorationManager::ForEachDecoration( }); } +bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) { + bool has_decoration = false; + ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) { + has_decoration = true; + }); + return has_decoration; +} + bool DecorationManager::FindDecoration( uint32_t id, uint32_t decoration, std::function f) { diff --git a/third_party/spirv-tools/source/opt/decoration_manager.h b/third_party/spirv-tools/source/opt/decoration_manager.h index b753e6be1d..fe78f2ce68 100644 --- a/third_party/spirv-tools/source/opt/decoration_manager.h +++ b/third_party/spirv-tools/source/opt/decoration_manager.h @@ -90,6 +90,10 @@ class DecorationManager { bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2, bool ignore_target) const; + // Returns whether a decoration instruction for |id| with decoration + // |decoration| exists or not. + bool HasDecoration(uint32_t id, uint32_t decoration); + // |f| is run on each decoration instruction for |id| with decoration // |decoration|. Processed are all decorations which target |id| either // directly or indirectly by Decoration Groups. diff --git a/third_party/spirv-tools/source/opt/def_use_manager.cpp b/third_party/spirv-tools/source/opt/def_use_manager.cpp index 0ec98cae10..394b9fa174 100644 --- a/third_party/spirv-tools/source/opt/def_use_manager.cpp +++ b/third_party/spirv-tools/source/opt/def_use_manager.cpp @@ -58,7 +58,7 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { case SPV_OPERAND_TYPE_SCOPE_ID: { uint32_t use_id = inst->GetSingleWordOperand(i); Instruction* def = GetDef(use_id); - assert(def && "Definition is not registered."); + if (!def) assert(false && "Definition is not registered."); id_to_users_.insert(UserEntry(def, inst)); used_ids->push_back(use_id); } break; @@ -71,6 +71,9 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { void DefUseManager::AnalyzeInstDefUse(Instruction* inst) { AnalyzeInstDef(inst); AnalyzeInstUse(inst); + // Analyze lines last otherwise they will be cleared when inst is + // cleared by preceding two calls + for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst); } void DefUseManager::UpdateDefUse(Instruction* inst) { @@ -224,9 +227,11 @@ void DefUseManager::AnalyzeDefUse(Module* module) { if (!module) return; // Analyze all the defs before any uses to catch forward references. module->ForEachInst( - std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1)); + std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1), + true); module->ForEachInst( - std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1)); + std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1), + true); } void DefUseManager::ClearInst(Instruction* inst) { @@ -261,6 +266,16 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) { if (lhs.id_to_def_ != rhs.id_to_def_) { + for (auto p : lhs.id_to_def_) { + if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) { + return false; + } + } + for (auto p : rhs.id_to_def_) { + if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) { + return false; + } + } return false; } diff --git a/third_party/spirv-tools/source/opt/desc_sroa.cpp b/third_party/spirv-tools/source/opt/desc_sroa.cpp index 5e950069da..bcbdde94ca 100644 --- a/third_party/spirv-tools/source/opt/desc_sroa.cpp +++ b/third_party/spirv-tools/source/opt/desc_sroa.cpp @@ -14,10 +14,19 @@ #include "source/opt/desc_sroa.h" +#include "source/opt/desc_sroa_util.h" #include "source/util/string_utils.h" namespace spvtools { namespace opt { +namespace { + +bool IsDecorationBinding(Instruction* inst) { + if (inst->opcode() != SpvOpDecorate) return false; + return inst->GetSingleWordInOperand(1u) == SpvDecorationBinding; +} + +} // namespace Pass::Status DescriptorScalarReplacement::Process() { bool modified = false; @@ -25,7 +34,7 @@ Pass::Status DescriptorScalarReplacement::Process() { std::vector vars_to_kill; for (Instruction& var : context()->types_values()) { - if (IsCandidate(&var)) { + if (descsroautil::IsDescriptorArray(context(), &var)) { modified = true; if (!ReplaceCandidate(&var)) { return Status::Failure; @@ -41,72 +50,6 @@ Pass::Status DescriptorScalarReplacement::Process() { return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); } -bool DescriptorScalarReplacement::IsCandidate(Instruction* var) { - if (var->opcode() != SpvOpVariable) { - return false; - } - - uint32_t ptr_type_id = var->type_id(); - Instruction* ptr_type_inst = - context()->get_def_use_mgr()->GetDef(ptr_type_id); - if (ptr_type_inst->opcode() != SpvOpTypePointer) { - return false; - } - - uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); - Instruction* var_type_inst = - context()->get_def_use_mgr()->GetDef(var_type_id); - if (var_type_inst->opcode() != SpvOpTypeArray && - var_type_inst->opcode() != SpvOpTypeStruct) { - return false; - } - - // All structures with descriptor assignments must be replaced by variables, - // one for each of their members - with the exceptions of buffers. - if (IsTypeOfStructuredBuffer(var_type_inst)) { - return false; - } - - bool has_desc_set_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var->result_id(), SpvDecorationDescriptorSet, - [&has_desc_set_decoration](const Instruction&) { - has_desc_set_decoration = true; - }); - if (!has_desc_set_decoration) { - return false; - } - - bool has_binding_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var->result_id(), SpvDecorationBinding, - [&has_binding_decoration](const Instruction&) { - has_binding_decoration = true; - }); - if (!has_binding_decoration) { - return false; - } - - return true; -} - -bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer( - const Instruction* type) const { - if (type->opcode() != SpvOpTypeStruct) { - return false; - } - - // All buffers have offset decorations for members of their structure types. - // This is how we distinguish it from a structure of descriptors. - bool has_offset_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - type->result_id(), SpvDecorationOffset, - [&has_offset_decoration](const Instruction&) { - has_offset_decoration = true; - }); - return has_offset_decoration; -} - bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { std::vector access_chain_work_list; std::vector load_work_list; @@ -162,16 +105,15 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, return false; } - uint32_t idx_id = use->GetSingleWordInOperand(1); - const analysis::Constant* idx_const = - context()->get_constant_mgr()->FindDeclaredConstant(idx_id); - if (idx_const == nullptr) { + const analysis::Constant* const_index = + descsroautil::GetAccessChainIndexAsConst(context(), use); + if (const_index == nullptr) { context()->EmitErrorMessage("Variable cannot be replaced: invalid index", use); return false; } - uint32_t idx = idx_const->GetU32(); + uint32_t idx = const_index->GetU32(); uint32_t replacement_var = GetReplacementVariable(var, idx); if (use->NumInOperands() == 2) { @@ -208,39 +150,12 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, uint32_t idx) { auto replacement_vars = replacement_variables_.find(var); if (replacement_vars == replacement_variables_.end()) { - uint32_t ptr_type_id = var->type_id(); - Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); - assert(ptr_type_inst->opcode() == SpvOpTypePointer && - "Variable should be a pointer to an array or structure."); - uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); - Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id); - const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray; - const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct; - assert((is_array || is_struct) && - "Variable should be a pointer to an array or structure."); - - // For arrays, each array element should be replaced with a new replacement - // variable - if (is_array) { - uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1); - const analysis::Constant* array_len_const = - context()->get_constant_mgr()->FindDeclaredConstant(array_len_id); - assert(array_len_const != nullptr && "Array length must be a constant."); - uint32_t array_len = array_len_const->GetU32(); - - replacement_vars = replacement_variables_ - .insert({var, std::vector(array_len, 0)}) - .first; - } - // For structures, each member should be replaced with a new replacement - // variable - if (is_struct) { - const uint32_t num_members = pointee_type_inst->NumInOperands(); - replacement_vars = - replacement_variables_ - .insert({var, std::vector(num_members, 0)}) - .first; - } + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + replacement_vars = + replacement_variables_ + .insert({var, std::vector(number_of_elements, 0)}) + .first; } if (replacement_vars->second[idx] == 0) { @@ -250,6 +165,74 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, return replacement_vars->second[idx]; } +void DescriptorScalarReplacement::CopyDecorationsForNewVariable( + Instruction* old_var, uint32_t index, uint32_t new_var_id, + uint32_t new_var_ptr_type_id, const bool is_old_var_array, + const bool is_old_var_struct, Instruction* old_var_type) { + // Handle OpDecorate instructions. + for (auto old_decoration : + get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) { + uint32_t new_binding = 0; + if (IsDecorationBinding(old_decoration)) { + new_binding = GetNewBindingForElement( + old_decoration->GetSingleWordInOperand(2), index, new_var_ptr_type_id, + is_old_var_array, is_old_var_struct, old_var_type); + } + CreateNewDecorationForNewVariable(old_decoration, new_var_id, new_binding); + } + + // Handle OpMemberDecorate instructions. + for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor( + old_var_type->result_id(), true)) { + assert(old_decoration->opcode() == SpvOpMemberDecorate); + if (old_decoration->GetSingleWordInOperand(1u) != index) continue; + CreateNewDecorationForMemberDecorate(old_decoration, new_var_id); + } +} + +uint32_t DescriptorScalarReplacement::GetNewBindingForElement( + uint32_t old_binding, uint32_t index, uint32_t new_var_ptr_type_id, + const bool is_old_var_array, const bool is_old_var_struct, + Instruction* old_var_type) { + if (is_old_var_array) { + return old_binding + index * GetNumBindingsUsedByType(new_var_ptr_type_id); + } + if (is_old_var_struct) { + // The binding offset that should be added is the sum of binding + // numbers used by previous members of the current struct. + uint32_t new_binding = old_binding; + for (uint32_t i = 0; i < index; ++i) { + new_binding += + GetNumBindingsUsedByType(old_var_type->GetSingleWordInOperand(i)); + } + return new_binding; + } + return old_binding; +} + +void DescriptorScalarReplacement::CreateNewDecorationForNewVariable( + Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) { + assert(old_decoration->opcode() == SpvOpDecorate); + std::unique_ptr new_decoration(old_decoration->Clone(context())); + new_decoration->SetInOperand(0, {new_var_id}); + + if (IsDecorationBinding(new_decoration.get())) { + new_decoration->SetInOperand(2, {new_binding}); + } + context()->AddAnnotationInst(std::move(new_decoration)); +} + +void DescriptorScalarReplacement::CreateNewDecorationForMemberDecorate( + Instruction* old_member_decoration, uint32_t new_var_id) { + std::vector operands( + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {new_var_id}}}); + auto new_decorate_operand_begin = old_member_decoration->begin() + 2u; + auto new_decorate_operand_end = old_member_decoration->end(); + operands.insert(operands.end(), new_decorate_operand_begin, + new_decorate_operand_end); + get_decoration_mgr()->AddDecoration(SpvOpDecorate, std::move(operands)); +} + uint32_t DescriptorScalarReplacement::CreateReplacementVariable( Instruction* var, uint32_t idx) { // The storage class for the new variable is the same as the original. @@ -285,33 +268,8 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable( {static_cast(storage_class)}}})); context()->AddGlobalValue(std::move(variable)); - // Copy all of the decorations to the new variable. The only difference is - // the Binding decoration needs to be adjusted. - for (auto old_decoration : - get_decoration_mgr()->GetDecorationsFor(var->result_id(), true)) { - assert(old_decoration->opcode() == SpvOpDecorate); - std::unique_ptr new_decoration( - old_decoration->Clone(context())); - new_decoration->SetInOperand(0, {id}); - - uint32_t decoration = new_decoration->GetSingleWordInOperand(1u); - if (decoration == SpvDecorationBinding) { - uint32_t new_binding = new_decoration->GetSingleWordInOperand(2); - if (is_array) { - new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id); - } - if (is_struct) { - // The binding offset that should be added is the sum of binding numbers - // used by previous members of the current struct. - for (uint32_t i = 0; i < idx; ++i) { - new_binding += GetNumBindingsUsedByType( - pointee_type_inst->GetSingleWordInOperand(i)); - } - } - new_decoration->SetInOperand(2, {new_binding}); - } - context()->AddAnnotationInst(std::move(new_decoration)); - } + CopyDecorationsForNewVariable(var, idx, id, ptr_element_type_id, is_array, + is_struct, pointee_type_inst); // Create a new OpName for the replacement variable. std::vector> names_to_add; @@ -377,7 +335,7 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType( // The number of bindings consumed by a structure is the sum of the bindings // used by its members. if (type_inst->opcode() == SpvOpTypeStruct && - !IsTypeOfStructuredBuffer(type_inst)) { + !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) { uint32_t sum = 0; for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i)); diff --git a/third_party/spirv-tools/source/opt/desc_sroa.h b/third_party/spirv-tools/source/opt/desc_sroa.h index cd72fd3019..fea062559c 100644 --- a/third_party/spirv-tools/source/opt/desc_sroa.h +++ b/third_party/spirv-tools/source/opt/desc_sroa.h @@ -46,10 +46,6 @@ class DescriptorScalarReplacement : public Pass { } private: - // Returns true if |var| is an OpVariable instruction that represents a - // descriptor array. These are the variables that we want to replace. - bool IsCandidate(Instruction* var); - // Replaces all references to |var| by new variables, one for each element of // the array |var|. The binding for the new variables corresponding to // element i will be the binding of |var| plus i. Returns true if successful. @@ -93,10 +89,45 @@ class DescriptorScalarReplacement : public Pass { // bindings used by its members. uint32_t GetNumBindingsUsedByType(uint32_t type_id); - // Returns true if |type| is a type that could be used for a structured buffer - // as opposed to a type that would be used for a structure of resource - // descriptors. - bool IsTypeOfStructuredBuffer(const Instruction* type) const; + // Copy all of the decorations of variable |old_var| and make them as + // decorations for the new variable whose id is |new_var_id|. The new variable + // is supposed to replace |index|th element of |old_var|. + // |new_var_ptr_type_id| is the id of the pointer to the type of the new + // variable. |is_old_var_array| is true if |old_var| has an array type. + // |is_old_var_struct| is true if |old_var| has a structure type. + // |old_var_type| is the pointee type of |old_var|. + void CopyDecorationsForNewVariable(Instruction* old_var, uint32_t index, + uint32_t new_var_id, + uint32_t new_var_ptr_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Get the new binding number for a new variable that will be replaced with an + // |index|th element of an old variable. The old variable has |old_binding| + // as its binding number. |ptr_elem_type_id| the id of the pointer to the + // element type. |is_old_var_array| is true if the old variable has an array + // type. |is_old_var_struct| is true if the old variable has a structure type. + // |old_var_type| is the pointee type of the old variable. + uint32_t GetNewBindingForElement(uint32_t old_binding, uint32_t index, + uint32_t ptr_elem_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Create a new OpDecorate instruction by cloning |old_decoration|. The new + // OpDecorate instruction will be used for a variable whose id is + // |new_var_ptr_type_id|. If |old_decoration| is a decoration for a binding, + // the new OpDecorate instruction will have |new_binding| as its binding. + void CreateNewDecorationForNewVariable(Instruction* old_decoration, + uint32_t new_var_id, + uint32_t new_binding); + + // Create a new OpDecorate instruction whose operand is the same as an + // OpMemberDecorate instruction |old_member_decoration| except Target operand. + // The Target operand of the new OpDecorate instruction will be |new_var_id|. + void CreateNewDecorationForMemberDecorate(Instruction* old_decoration, + uint32_t new_var_id); // A map from an OpVariable instruction to the set of variables that will be // used to replace it. The entry |replacement_variables_[var][i]| is the id of diff --git a/third_party/spirv-tools/source/opt/desc_sroa_util.cpp b/third_party/spirv-tools/source/opt/desc_sroa_util.cpp new file mode 100644 index 0000000000..1954e2cc36 --- /dev/null +++ b/third_party/spirv-tools/source/opt/desc_sroa_util.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/desc_sroa_util.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpAccessChainInOperandIndexes = 1; + +// Returns the length of array type |type|. +uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) { + assert(type->opcode() == SpvOpTypeArray && "type must be array"); + uint32_t length_id = type->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant(length_id); + assert(length_const != nullptr); + return length_const->GetU32(); +} + +} // namespace + +namespace descsroautil { + +bool IsDescriptorArray(IRContext* context, Instruction* var) { + if (var->opcode() != SpvOpVariable) { + return false; + } + + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + if (ptr_type_inst->opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id); + if (var_type_inst->opcode() != SpvOpTypeArray && + var_type_inst->opcode() != SpvOpTypeStruct) { + return false; + } + + // All structures with descriptor assignments must be replaced by variables, + // one for each of their members - with the exceptions of buffers. + if (IsTypeOfStructuredBuffer(context, var_type_inst)) { + return false; + } + + if (!context->get_decoration_mgr()->HasDecoration( + var->result_id(), SpvDecorationDescriptorSet)) { + return false; + } + + return context->get_decoration_mgr()->HasDecoration(var->result_id(), + SpvDecorationBinding); +} + +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) { + if (type->opcode() != SpvOpTypeStruct) { + return false; + } + + // All buffers have offset decorations for members of their structure types. + // This is how we distinguish it from a structure of descriptors. + return context->get_decoration_mgr()->HasDecoration(type->result_id(), + SpvDecorationOffset); +} + +const analysis::Constant* GetAccessChainIndexAsConst( + IRContext* context, Instruction* access_chain) { + if (access_chain->NumInOperands() <= 1) { + return nullptr; + } + uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain); + const analysis::Constant* idx_const = + context->get_constant_mgr()->FindDeclaredConstant(idx_id); + return idx_const; +} + +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) { + assert(access_chain->NumInOperands() > 1 && + "OpAccessChain does not have Indexes operand"); + return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes); +} + +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var) { + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == SpvOpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = + context->get_def_use_mgr()->GetDef(pointee_type_id); + if (pointee_type_inst->opcode() == SpvOpTypeArray) { + return GetLengthOfArrayType(context, pointee_type_inst); + } + assert(pointee_type_inst->opcode() == SpvOpTypeStruct && + "Variable should be a pointer to an array or structure."); + return pointee_type_inst->NumInOperands(); +} + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/desc_sroa_util.h b/third_party/spirv-tools/source/opt/desc_sroa_util.h new file mode 100644 index 0000000000..2f45c0c2f4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/desc_sroa_util.h @@ -0,0 +1,54 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DESC_SROA_UTIL_H_ +#define SOURCE_OPT_DESC_SROA_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functions for the descriptor array SROA. +namespace descsroautil { + +// Returns true if |var| is an OpVariable instruction that represents a +// descriptor array. +bool IsDescriptorArray(IRContext* context, Instruction* var); + +// Returns true if |type| is a type that could be used for a structured buffer +// as opposed to a type that would be used for a structure of resource +// descriptors. +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type); + +// Returns the first index of the OpAccessChain instruction |access_chain| as +// a constant. Returns nullptr if it is not a constant. +const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context, + Instruction* access_chain); + +// Returns the number of elements of an OpVariable instruction |var| whose type +// must be a pointer to an array or a struct. +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var); + +// Returns the first Indexes operand id of the OpAccessChain or +// OpInBoundsAccessChain instruction |access_chain|. The access chain must have +// at least 1 index. +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain); + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DESC_SROA_UTIL_H_ diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp b/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp index 173df6209a..a24ba8f41c 100644 --- a/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp +++ b/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp @@ -20,6 +20,7 @@ namespace { const uint32_t kRemovedMember = 0xFFFFFFFF; const uint32_t kSpecConstOpOpcodeIdx = 0; +constexpr uint32_t kArrayElementTypeIdx = 0; } // namespace namespace spvtools { @@ -64,6 +65,10 @@ void EliminateDeadMembersPass::FindLiveMembers() { MarkPointeeTypeAsFullUsed(inst.type_id()); break; default: + // Ignore structured buffers as layout(offset) qualifiers cannot be + // applied to structure fields + if (inst.IsVulkanStorageBufferVariable()) + MarkPointeeTypeAsFullUsed(inst.type_id()); break; } } @@ -136,18 +141,22 @@ void EliminateDeadMembersPass::MarkMembersAsLiveForStore( void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); assert(type_inst != nullptr); - if (type_inst->opcode() != SpvOpTypeStruct) { - return; - } - // Mark every member of the current struct as used. - for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { - used_members_[type_id].insert(i); - } - - // Mark any sub struct as fully used. - for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { - MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: + // Mark every member and its type as fully used. + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + used_members_[type_id].insert(i); + MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + } + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + MarkTypeAsFullyUsed( + type_inst->GetSingleWordInOperand(kArrayElementTypeIdx)); + break; + default: + break; } } diff --git a/third_party/spirv-tools/source/opt/feature_manager.cpp b/third_party/spirv-tools/source/opt/feature_manager.cpp index ad70c1e4f1..39a4a3486e 100644 --- a/third_party/spirv-tools/source/opt/feature_manager.cpp +++ b/third_party/spirv-tools/source/opt/feature_manager.cpp @@ -80,6 +80,8 @@ void FeatureManager::AddExtInstImportIds(Module* module) { extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); extinst_importid_OpenCL100DebugInfo_ = module->GetExtInstImportId("OpenCL.DebugInfo.100"); + extinst_importid_Shader100DebugInfo_ = + module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100"); } bool operator==(const FeatureManager& a, const FeatureManager& b) { @@ -107,6 +109,11 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) { return false; } + if (a.extinst_importid_Shader100DebugInfo_ != + b.extinst_importid_Shader100DebugInfo_) { + return false; + } + return true; } } // namespace opt diff --git a/third_party/spirv-tools/source/opt/feature_manager.h b/third_party/spirv-tools/source/opt/feature_manager.h index 66d1cbac33..68c8e9a230 100644 --- a/third_party/spirv-tools/source/opt/feature_manager.h +++ b/third_party/spirv-tools/source/opt/feature_manager.h @@ -55,6 +55,10 @@ class FeatureManager { return extinst_importid_OpenCL100DebugInfo_; } + uint32_t GetExtInstImportId_Shader100DebugInfo() const { + return extinst_importid_Shader100DebugInfo_; + } + friend bool operator==(const FeatureManager& a, const FeatureManager& b); friend bool operator!=(const FeatureManager& a, const FeatureManager& b) { return !(a == b); @@ -92,6 +96,10 @@ class FeatureManager { // Common OpenCL100DebugInfo external instruction import ids, cached // for performance. uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; + + // Common NonSemanticShader100DebugInfo external instruction import ids, + // cached for performance. + uint32_t extinst_importid_Shader100DebugInfo_ = 0; }; } // 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 e3e926c3a5..4904f1862e 100644 --- a/third_party/spirv-tools/source/opt/folding_rules.cpp +++ b/third_party/spirv-tools/source/opt/folding_rules.cpp @@ -14,6 +14,7 @@ #include "source/opt/folding_rules.h" +#include #include #include #include @@ -523,7 +524,8 @@ uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr, float fval = val.getAsFloat(); \ if (!IsValidResult(fval)) return 0; \ words = val.GetWords(); \ - } static_assert(true, "require extra semicolon") + } \ + static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpFMul: FOLD_OP(*); @@ -558,24 +560,19 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr, uint32_t width = type->AsInteger()->width(); assert(width == 32 || width == 64); std::vector words; -#define FOLD_OP(op) \ - if (width == 64) { \ - if (type->IsSigned()) { \ - int64_t val = input1->GetS64() op input2->GetS64(); \ - words = ExtractInts(static_cast(val)); \ - } else { \ - uint64_t val = input1->GetU64() op input2->GetU64(); \ - words = ExtractInts(val); \ - } \ - } else { \ - if (type->IsSigned()) { \ - int32_t val = input1->GetS32() op input2->GetS32(); \ - words.push_back(static_cast(val)); \ - } else { \ - uint32_t val = input1->GetU32() op input2->GetU32(); \ - words.push_back(val); \ - } \ - } static_assert(true, "require extra semicalon") + // Regardless of the sign of the constant, folding is performed on an unsigned + // interpretation of the constant data. This avoids signed integer overflow + // while folding, and works because sign is irrelevant for the IAdd, ISub and + // IMul instructions. +#define FOLD_OP(op) \ + if (width == 64) { \ + uint64_t val = input1->GetU64() op input2->GetU64(); \ + words = ExtractInts(val); \ + } else { \ + uint32_t val = input1->GetU32() op input2->GetU32(); \ + words.push_back(val); \ + } \ + static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpIMul: FOLD_OP(*); @@ -967,30 +964,21 @@ FoldingRule MergeDivMulArithmetic() { // Fold divides of a constant and a negation. // Cases: // (-x) / 2 = x / -2 -// 2 / (-x) = 2 / -x +// 2 / (-x) = -2 / x FoldingRule MergeDivNegateArithmetic() { return [](IRContext* context, Instruction* inst, const std::vector& constants) { - assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv || - inst->opcode() == SpvOpUDiv); + assert(inst->opcode() == SpvOpFDiv); analysis::ConstantManager* const_mgr = context->get_constant_mgr(); - const analysis::Type* type = - context->get_type_mgr()->GetType(inst->type_id()); - bool uses_float = HasFloatingPoint(type); - if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; - - uint32_t width = ElementWidth(type); - if (width != 32 && width != 64) return false; + if (!inst->IsFloatingPointFoldingAllowed()) return false; const analysis::Constant* const_input1 = ConstInput(constants); if (!const_input1) return false; Instruction* other_inst = NonConstInput(context, constants[0], inst); - if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) - return false; + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; bool first_is_variable = constants[0] == nullptr; - if (other_inst->opcode() == SpvOpFNegate || - other_inst->opcode() == SpvOpSNegate) { + if (other_inst->opcode() == SpvOpFNegate) { uint32_t neg_id = NegateConstant(const_mgr, const_input1); if (first_is_variable) { @@ -1468,90 +1456,121 @@ FoldingRule IntMultipleBy1() { }; } -FoldingRule CompositeConstructFeedingExtract() { - return [](IRContext* context, Instruction* inst, - const std::vector&) { - // If the input to an OpCompositeExtract is an OpCompositeConstruct, - // then we can simply use the appropriate element in the construction. - assert(inst->opcode() == SpvOpCompositeExtract && - "Wrong opcode. Should be OpCompositeExtract."); - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - analysis::TypeManager* type_mgr = context->get_type_mgr(); +// Returns the number of elements that the |index|th in operand in |inst| +// contributes to the result of |inst|. |inst| must be an +// OpCompositeConstructInstruction. +uint32_t GetNumOfElementsContributedByOperand(IRContext* context, + const Instruction* inst, + uint32_t index) { + assert(inst->opcode() == SpvOpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); - // If there are no index operands, then this rule cannot do anything. - if (inst->NumInOperands() <= 1) { - return false; - } + analysis::Vector* result_type = + type_mgr->GetType(inst->type_id())->AsVector(); + if (result_type == nullptr) { + // If the result of the OpCompositeConstruct is not a vector then every + // operands corresponds to a single element in the result. + return 1; + } - uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); - Instruction* cinst = def_use_mgr->GetDef(cid); + // If the result type is a vector then the operands are either scalars or + // vectors. If it is a scalar, then it corresponds to a single element. If it + // is a vector, then each element in the vector will be an element in the + // result. + uint32_t id = inst->GetSingleWordInOperand(index); + Instruction* def = def_use_mgr->GetDef(id); + analysis::Vector* type = type_mgr->GetType(def->type_id())->AsVector(); + if (type == nullptr) { + return 1; + } + return type->element_count(); +} - if (cinst->opcode() != SpvOpCompositeConstruct) { - return false; - } +// Returns the in-operands for an OpCompositeExtract instruction that are needed +// to extract the |result_index|th element in the result of |inst| without using +// the result of |inst|. Returns the empty vector if |result_index| is +// out-of-bounds. |inst| must be an |OpCompositeConstruct| instruction. +std::vector GetExtractOperandsForElementOfCompositeConstruct( + IRContext* context, const Instruction* inst, uint32_t result_index) { + assert(inst->opcode() == SpvOpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); - std::vector operands; - analysis::Type* composite_type = type_mgr->GetType(cinst->type_id()); - if (composite_type->AsVector() == nullptr) { - // Get the element being extracted from the OpCompositeConstruct - // Since it is not a vector, it is simple to extract the single element. - uint32_t element_index = inst->GetSingleWordInOperand(1); - uint32_t element_id = cinst->GetSingleWordInOperand(element_index); - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); + analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + if (result_type->AsVector() == nullptr) { + uint32_t id = inst->GetSingleWordInOperand(result_index); + return {Operand(SPV_OPERAND_TYPE_ID, {id})}; + } - // Add the remaining indices for extraction. - for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { - operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, - {inst->GetSingleWordInOperand(i)}}); - } - - } else { - // With vectors we have to handle the case where it is concatenating - // vectors. - assert(inst->NumInOperands() == 2 && - "Expecting a vector of scalar values."); - - uint32_t element_index = inst->GetSingleWordInOperand(1); - for (uint32_t construct_index = 0; - construct_index < cinst->NumInOperands(); ++construct_index) { - uint32_t element_id = cinst->GetSingleWordInOperand(construct_index); - Instruction* element_def = def_use_mgr->GetDef(element_id); - analysis::Vector* element_type = - type_mgr->GetType(element_def->type_id())->AsVector(); - if (element_type) { - uint32_t vector_size = element_type->element_count(); - if (vector_size <= element_index) { - // The element we want comes after this vector. - element_index -= vector_size; - } else { - // We want an element of this vector. - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - operands.push_back( - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_index}}); - break; - } - } else { - if (element_index == 0) { - // This is a scalar, and we this is the element we are extracting. - operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); - break; - } else { - // Skip over this scalar value. - --element_index; - } - } + // If the result type is a vector, then vector operands are concatenated. + uint32_t total_element_count = 0; + for (uint32_t idx = 0; idx < inst->NumInOperands(); ++idx) { + uint32_t element_count = + GetNumOfElementsContributedByOperand(context, inst, idx); + total_element_count += element_count; + if (result_index < total_element_count) { + std::vector operands; + uint32_t id = inst->GetSingleWordInOperand(idx); + Instruction* operand_def = def_use_mgr->GetDef(id); + analysis::Type* operand_type = type_mgr->GetType(operand_def->type_id()); + + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + if (operand_type->AsVector()) { + uint32_t start_index_of_id = total_element_count - element_count; + uint32_t index_into_id = result_index - start_index_of_id; + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index_into_id}}); } + return operands; } + } + return {}; +} +bool CompositeConstructFeedingExtract( + IRContext* context, Instruction* inst, + const std::vector&) { + // If the input to an OpCompositeExtract is an OpCompositeConstruct, + // then we can simply use the appropriate element in the construction. + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + // If there are no index operands, then this rule cannot do anything. + if (inst->NumInOperands() <= 1) { + return false; + } + + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != SpvOpCompositeConstruct) { + return false; + } + + uint32_t index_into_result = inst->GetSingleWordInOperand(1); + std::vector operands = + GetExtractOperandsForElementOfCompositeConstruct(context, cinst, + index_into_result); + + if (operands.empty()) { + return false; + } + + // Add the remaining indices for extraction. + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {inst->GetSingleWordInOperand(i)}}); + } + + if (operands.size() == 1) { // If there were no extra indices, then we have the final object. No need - // to extract even more. - if (operands.size() == 1) { - inst->SetOpcode(SpvOpCopyObject); - } + // to extract any more. + inst->SetOpcode(SpvOpCopyObject); + } - inst->SetInOperands(std::move(operands)); - return true; - }; + inst->SetInOperands(std::move(operands)); + return true; } // If the OpCompositeConstruct is simply putting back together elements that @@ -2510,7 +2529,7 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct); rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract()); - rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract()); + rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract); rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract()); rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract()); @@ -2562,8 +2581,6 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpPhi].push_back(RedundantPhi()); - rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic()); - rules_[SpvOpSNegate].push_back(MergeNegateArithmetic()); rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic()); rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic()); @@ -2572,8 +2589,6 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpStore].push_back(StoringUndef()); - rules_[SpvOpUDiv].push_back(MergeDivNegateArithmetic()); - rules_[SpvOpVectorShuffle].push_back(VectorShuffleFeedingShuffle()); rules_[SpvOpImageSampleImplicitLod].push_back(UpdateImageOperands()); diff --git a/third_party/spirv-tools/source/opt/function.h b/third_party/spirv-tools/source/opt/function.h index 9e1c72746e..917bf58435 100644 --- a/third_party/spirv-tools/source/opt/function.h +++ b/third_party/spirv-tools/source/opt/function.h @@ -177,6 +177,9 @@ class Function { // debuggers. void Dump() const; + // Returns true is a function declaration and not a function definition. + bool IsDeclaration() { return begin() == end(); } + private: // The OpFunction instruction that begins the definition of this function. std::unique_ptr def_inst_; diff --git a/third_party/spirv-tools/source/opt/if_conversion.cpp b/third_party/spirv-tools/source/opt/if_conversion.cpp index 4284069a8b..492066177f 100644 --- a/third_party/spirv-tools/source/opt/if_conversion.cpp +++ b/third_party/spirv-tools/source/opt/if_conversion.cpp @@ -129,6 +129,7 @@ Pass::Status IfConversion::Process() { Instruction* select = builder.AddSelect(phi->type_id(), condition, true_value->result_id(), false_value->result_id()); + context()->get_def_use_mgr()->AnalyzeInstDefUse(select); select->UpdateDebugInfoFrom(phi); context()->ReplaceAllUsesWith(phi->result_id(), select->result_id()); to_kill.push_back(phi); diff --git a/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp b/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp index f24f744d05..bef45017f2 100644 --- a/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp +++ b/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp @@ -65,7 +65,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() { status = CombineStatus(status, InlineExhaustive(fp)); return false; }; - context()->ProcessEntryPointCallTree(pfn); + context()->ProcessReachableCallTree(pfn); return status; } diff --git a/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp b/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp index 6ccaf90870..fe9c67993f 100644 --- a/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp +++ b/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp @@ -105,7 +105,7 @@ Pass::Status InlineOpaquePass::ProcessImpl() { status = CombineStatus(status, InlineOpaque(fp)); return false; }; - context()->ProcessEntryPointCallTree(pfn); + context()->ProcessReachableCallTree(pfn); return status; } diff --git a/third_party/spirv-tools/source/opt/inline_pass.cpp b/third_party/spirv-tools/source/opt/inline_pass.cpp index 8159ebf787..2cc31258e3 100644 --- a/third_party/spirv-tools/source/opt/inline_pass.cpp +++ b/third_party/spirv-tools/source/opt/inline_pass.cpp @@ -92,7 +92,7 @@ void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}})); if (line_inst != nullptr) { - newStore->dbg_line_insts().push_back(*line_inst); + newStore->AddDebugLine(line_inst); } newStore->SetDebugScope(dbg_scope); (*block_ptr)->AddInstruction(std::move(newStore)); @@ -106,7 +106,7 @@ void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id, new Instruction(context(), SpvOpLoad, type_id, resultId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}})); if (line_inst != nullptr) { - newLoad->dbg_line_insts().push_back(*line_inst); + newLoad->AddDebugLine(line_inst); } newLoad->SetDebugScope(dbg_scope); (*block_ptr)->AddInstruction(std::move(newLoad)); @@ -158,8 +158,8 @@ bool InlinePass::CloneAndMapLocals( auto callee_block_itr = calleeFn->begin(); auto callee_var_itr = callee_block_itr->begin(); while (callee_var_itr->opcode() == SpvOp::SpvOpVariable || - callee_var_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + callee_var_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugDeclare) { if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) { ++callee_var_itr; continue; @@ -300,8 +300,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers( UptrVectorIterator callee_first_block_itr) { auto callee_itr = callee_first_block_itr->begin(); while (callee_itr->opcode() == SpvOp::SpvOpVariable || - callee_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { if (callee_itr->opcode() == SpvOp::SpvOpVariable && callee_itr->NumInOperands() == 2) { assert(callee2caller.count(callee_itr->result_id()) && @@ -315,8 +314,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers( context()->get_debug_info_mgr()->BuildDebugScope( callee_itr->GetDebugScope(), inlined_at_ctx)); } - if (callee_itr->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugDeclare) { + if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { InlineSingleInstruction( callee2caller, new_blk_ptr->get(), &*callee_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( @@ -405,6 +403,14 @@ bool InlinePass::InlineEntryBlock( callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block); while (callee_inst_itr != callee_first_block->end()) { + // Don't inline function definition links, the calling function is not a + // definition. + if (callee_inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + ++callee_inst_itr; + continue; + } + if (!InlineSingleInstruction( callee2caller, new_blk_ptr->get(), &*callee_inst_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( @@ -435,6 +441,11 @@ std::unique_ptr InlinePass::InlineBasicBlocks( auto tail_inst_itr = callee_block_itr->end(); for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr; ++inst_itr) { + // Don't inline function definition links, the calling function is not a + // definition + if (inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) + continue; if (!InlineSingleInstruction( callee2caller, new_blk_ptr.get(), &*inst_itr, context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( diff --git a/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp b/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp index 06acc7ea55..e2336d3601 100644 --- a/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp +++ b/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp @@ -130,13 +130,48 @@ void InstBuffAddrCheckPass::GenCheckCode( context()->KillInst(ref_inst); } +uint32_t InstBuffAddrCheckPass::GetTypeAlignment(uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeVector: + return GetTypeLength(type_id); + case SpvOpTypeMatrix: + return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeStruct: { + uint32_t max = 0; + type_inst->ForEachInId([&max, this](const uint32_t* iid) { + uint32_t alignment = GetTypeAlignment(*iid); + max = (alignment > max) ? alignment : max; + }); + return max; + } + case SpvOpTypePointer: + assert(type_inst->GetSingleWordInOperand(0) == + SpvStorageClassPhysicalStorageBufferEXT && + "unexpected pointer type"); + return 8u; + default: + assert(false && "unexpected type"); + return 0; + } +} + uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeFloat: case SpvOpTypeInt: return type_inst->GetSingleWordInOperand(0) / 8u; - case SpvOpTypeVector: + case SpvOpTypeVector: { + uint32_t raw_cnt = type_inst->GetSingleWordInOperand(1); + uint32_t adj_cnt = (raw_cnt == 3u) ? 4u : raw_cnt; + return adj_cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); + } case SpvOpTypeMatrix: return type_inst->GetSingleWordInOperand(1) * GetTypeLength(type_inst->GetSingleWordInOperand(0)); @@ -145,8 +180,29 @@ uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { SpvStorageClassPhysicalStorageBufferEXT && "unexpected pointer type"); return 8u; + case SpvOpTypeArray: { + uint32_t const_id = type_inst->GetSingleWordInOperand(1); + Instruction* const_inst = get_def_use_mgr()->GetDef(const_id); + uint32_t cnt = const_inst->GetSingleWordInOperand(0); + return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); + } + case SpvOpTypeStruct: { + uint32_t len = 0; + type_inst->ForEachInId([&len, this](const uint32_t* iid) { + // Align struct length + uint32_t alignment = GetTypeAlignment(*iid); + uint32_t mod = len % alignment; + uint32_t diff = (mod != 0) ? alignment - mod : 0; + len += diff; + // Increment struct length by component length + uint32_t comp_len = GetTypeLength(*iid); + len += comp_len; + }); + return len; + } + case SpvOpTypeRuntimeArray: default: - assert(false && "unexpected buffer reference type"); + assert(false && "unexpected type"); return 0; } } 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 ec7bb6846f..a82322395a 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,7 +28,9 @@ namespace opt { // external design of this class may change as the layer evolves. class InstBuffAddrCheckPass : public InstrumentPass { public: - // Preferred interface + // For test harness only + InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {} + // For all other interfaces InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id) : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {} @@ -40,8 +42,12 @@ class InstBuffAddrCheckPass : public InstrumentPass { const char* name() const override { return "inst-bindless-check-pass"; } private: - // Return byte length of type |type_id|. Must be int, float, vector, matrix - // or physical pointer. + // Return byte alignment of type |type_id|. Must be int, float, vector, + // matrix, struct, array or physical pointer. Uses std430 alignment. + uint32_t GetTypeAlignment(uint32_t type_id); + + // Return byte length of type |type_id|. Must be int, float, vector, matrix, + // struct, array or physical pointer. Uses std430 alignment and sizes. uint32_t GetTypeLength(uint32_t type_id); // Add |type_id| param to |input_func| and add id to |param_vec|. diff --git a/third_party/spirv-tools/source/opt/instruction.cpp b/third_party/spirv-tools/source/opt/instruction.cpp index 1054a20393..2461e41e96 100644 --- a/third_party/spirv-tools/source/opt/instruction.cpp +++ b/third_party/spirv-tools/source/opt/instruction.cpp @@ -30,9 +30,11 @@ namespace { const uint32_t kTypeImageDimIndex = 1; const uint32_t kLoadBaseIndex = 0; const uint32_t kPointerTypeStorageClassIndex = 0; +const uint32_t kVariableStorageClassIndex = 0; const uint32_t kTypeImageSampledIndex = 5; -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. const uint32_t kExtInstSetIdInIdx = 0; const uint32_t kExtInstInstructionInIdx = 1; const uint32_t kDebugScopeNumWords = 7; @@ -64,15 +66,14 @@ Instruction::Instruction(IRContext* c, SpvOp op) Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, std::vector&& dbg_line) - : context_(c), + : utils::IntrusiveNodeBase(), + context_(c), opcode_(static_cast(inst.opcode)), has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), unique_id_(c->TakeNextUniqueId()), dbg_line_insts_(std::move(dbg_line)), dbg_scope_(kNoDebugScope, kNoInlinedAt) { - assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) && - "Op(No)Line attaching to Op(No)Line found"); for (uint32_t i = 0; i < inst.num_operands; ++i) { const auto& current_payload = inst.operands[i]; std::vector words( @@ -80,11 +81,14 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, inst.words + current_payload.offset + current_payload.num_words); operands_.emplace_back(current_payload.type, std::move(words)); } + assert((!IsLineInst() || dbg_line.empty()) && + "Op(No)Line attaching to Op(No)Line found"); } Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, const DebugScope& dbg_scope) - : context_(c), + : utils::IntrusiveNodeBase(), + context_(c), opcode_(static_cast(inst.opcode)), has_type_id_(inst.type_id != 0), has_result_id_(inst.result_id != 0), @@ -122,6 +126,7 @@ Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id, Instruction::Instruction(Instruction&& that) : utils::IntrusiveNodeBase(), + context_(that.context_), opcode_(that.opcode_), has_type_id_(that.has_type_id_), has_result_id_(that.has_result_id_), @@ -135,6 +140,7 @@ Instruction::Instruction(Instruction&& that) } Instruction& Instruction::operator=(Instruction&& that) { + context_ = that.context_; opcode_ = that.opcode_; has_type_id_ = that.has_type_id_; has_result_id_ = that.has_result_id_; @@ -153,6 +159,10 @@ Instruction* Instruction::Clone(IRContext* c) const { clone->unique_id_ = c->TakeNextUniqueId(); clone->operands_ = operands_; clone->dbg_line_insts_ = dbg_line_insts_; + for (auto& i : clone->dbg_line_insts_) { + i.unique_id_ = c->TakeNextUniqueId(); + if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId()); + } clone->dbg_scope_ = dbg_scope_; return clone; } @@ -394,6 +404,21 @@ bool Instruction::IsVulkanStorageBuffer() const { return false; } +bool Instruction::IsVulkanStorageBufferVariable() const { + if (opcode() != SpvOpVariable) { + return false; + } + + uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex); + if (storage_class == SpvStorageClassStorageBuffer || + storage_class == SpvStorageClassUniform) { + Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id()); + return var_type != nullptr && var_type->IsVulkanStorageBuffer(); + } + + return false; +} + bool Instruction::IsVulkanUniformBuffer() const { if (opcode() != SpvOpTypePointer) { return false; @@ -506,7 +531,7 @@ void Instruction::UpdateLexicalScope(uint32_t scope) { for (auto& i : dbg_line_insts_) { i.dbg_scope_.SetLexicalScope(scope); } - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } @@ -517,24 +542,61 @@ void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) { for (auto& i : dbg_line_insts_) { i.dbg_scope_.SetInlinedAt(new_inlined_at); } - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } } +void Instruction::ClearDbgLineInsts() { + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + auto def_use_mgr = context()->get_def_use_mgr(); + for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst); + } + clear_dbg_line_insts(); +} + void Instruction::UpdateDebugInfoFrom(const Instruction* from) { if (from == nullptr) return; - clear_dbg_line_insts(); + ClearDbgLineInsts(); if (!from->dbg_line_insts().empty()) - dbg_line_insts().push_back(from->dbg_line_insts().back()); + AddDebugLine(&from->dbg_line_insts().back()); SetDebugScope(from->GetDebugScope()); - if (!IsDebugLineInst(opcode()) && + if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } } +void Instruction::AddDebugLine(const Instruction* inst) { + dbg_line_insts_.push_back(*inst); + dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId(); + if (inst->IsDebugLineInst()) + dbg_line_insts_.back().SetResultId(context_->TakeNextId()); + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back()); +} + +bool Instruction::IsDebugLineInst() const { + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) || + (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine)); +} + +bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); } + +bool Instruction::IsLine() const { + if (opcode() == SpvOpLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugLine; +} + +bool Instruction::IsNoLine() const { + if (opcode() == SpvOpNoLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine; +} + Instruction* Instruction::InsertBefore(std::unique_ptr&& inst) { inst.get()->InsertBefore(this); return inst.release(); @@ -618,6 +680,49 @@ OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { GetSingleWordInOperand(kExtInstInstructionInIdx)); } +NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() + const { + if (opcode() != SpvOpExtInst) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + return NonSemanticShaderDebugInfo100Instructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + +CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { + if (opcode() != SpvOpExtInst) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t opencl_set_id = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + const uint32_t shader_set_id = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + + if (!opencl_set_id && !shader_set_id) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx); + + if (used_set_id != opencl_set_id && used_set_id != shader_set_id) { + return CommonDebugInfoInstructionsMax; + } + + return CommonDebugInfoInstructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + bool Instruction::IsValidBaseImage() const { uint32_t tid = type_id(); if (tid == 0) { @@ -940,10 +1045,10 @@ void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set, std::vector* binary) const { uint32_t num_words = kDebugScopeNumWords; - OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope; + CommonDebugInfoInstructions dbg_opcode = CommonDebugInfoDebugScope; if (GetLexicalScope() == kNoDebugScope) { num_words = kDebugNoScopeNumWords; - dbg_opcode = OpenCLDebugInfo100DebugNoScope; + dbg_opcode = CommonDebugInfoDebugNoScope; } else if (GetInlinedAt() == kNoInlinedAt) { num_words = kDebugScopeNumWordsWithoutInlinedAt; } diff --git a/third_party/spirv-tools/source/opt/instruction.h b/third_party/spirv-tools/source/opt/instruction.h index 3e557dd7bf..ce568f6626 100644 --- a/third_party/spirv-tools/source/opt/instruction.h +++ b/third_party/spirv-tools/source/opt/instruction.h @@ -22,7 +22,9 @@ #include #include +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/common_debug_info.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_spirv_header.h" #include "source/opcode.h" @@ -250,11 +252,6 @@ class Instruction : public utils::IntrusiveNodeBase { // Clear line-related debug instructions attached to this instruction. void clear_dbg_line_insts() { dbg_line_insts_.clear(); } - // Set line-related debug instructions. - void set_dbg_line_insts(const std::vector& lines) { - dbg_line_insts_ = lines; - } - // Same semantics as in the base class except the list the InstructionList // containing |pos| will now assume ownership of |this|. // inline void MoveBefore(Instruction* pos); @@ -304,8 +301,21 @@ class Instruction : public utils::IntrusiveNodeBase { // Sets DebugScope. inline void SetDebugScope(const DebugScope& scope); inline const DebugScope& GetDebugScope() const { return dbg_scope_; } + // Add debug line inst. Renew result id if Debug[No]Line + void AddDebugLine(const Instruction* inst); // Updates DebugInlinedAt of DebugScope and OpLine. void UpdateDebugInlinedAt(uint32_t new_inlined_at); + // Clear line-related debug instructions attached to this instruction + // along with def-use entries. + void ClearDbgLineInsts(); + // Return true if Shader100:Debug[No]Line + bool IsDebugLineInst() const; + // Return true if Op[No]Line or Shader100:Debug[No]Line + bool IsLineInst() const; + // Return true if OpLine or Shader100:DebugLine + bool IsLine() const; + // Return true if OpNoLine or Shader100:DebugNoLine + bool IsNoLine() const; inline uint32_t GetDebugInlinedAt() const { return dbg_scope_.GetInlinedAt(); } @@ -454,6 +464,10 @@ class Instruction : public utils::IntrusiveNodeBase { // storage buffer. bool IsVulkanStorageBuffer() const; + // Returns true if the instruction defines a variable in StorageBuffer or + // Uniform storage class with a pointer type that points to a storage buffer. + bool IsVulkanStorageBufferVariable() const; + // Returns true if the instruction defines a pointer type that points to a // uniform buffer. bool IsVulkanUniformBuffer() const; @@ -550,11 +564,30 @@ class Instruction : public utils::IntrusiveNodeBase { // OpenCLDebugInfo100InstructionsMax. OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If + // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return + // NonSemanticShaderDebugInfo100InstructionsMax. + NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const; + + // Returns debug opcode of an OpenCL.100.DebugInfo or + // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we + // return the OpenCLDebugInfo code + CommonDebugInfoInstructions GetCommonDebugOpcode() const; + // Returns true if it is an OpenCL.DebugInfo.100 instruction. bool IsOpenCL100DebugInstr() const { return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; } + // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction. + bool IsShader100DebugInstr() const { + return GetShader100DebugOpcode() != + NonSemanticShaderDebugInfo100InstructionsMax; + } + bool IsCommonDebugInstr() const { + return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax; + } + // Returns true if this instructions a non-semantic instruction. bool IsNonSemanticInstruction() const; @@ -588,9 +621,9 @@ class Instruction : public utils::IntrusiveNodeBase { uint32_t unique_id_; // Unique instruction id // All logical operands, including result type id and result id. OperandList operands_; - // Opline and OpNoLine instructions preceding this instruction. Note that for - // Instructions representing OpLine or OpNonLine itself, this field should be - // empty. + // Op[No]Line or Debug[No]Line instructions preceding this instruction. Note + // that for Instructions representing Op[No]Line or Debug[No]Line themselves, + // this field should be empty. std::vector dbg_line_insts_; // DebugScope that wraps this instruction. diff --git a/third_party/spirv-tools/source/opt/ir_builder.h b/third_party/spirv-tools/source/opt/ir_builder.h index fe5feff569..4433cf0d15 100644 --- a/third_party/spirv-tools/source/opt/ir_builder.h +++ b/third_party/spirv-tools/source/opt/ir_builder.h @@ -359,8 +359,9 @@ class InstructionBuilder { return AddInstruction(std::move(select)); } - // Adds a signed int32 constant to the binary. - // The |value| parameter is the constant value to be added. + // Returns a pointer to the definition of a signed 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. Instruction* GetSintConstant(int32_t value) { return GetIntConstant(value, true); } @@ -381,21 +382,24 @@ class InstructionBuilder { GetContext()->TakeNextId(), ops)); return AddInstruction(std::move(construct)); } - // Adds an unsigned int32 constant to the binary. - // The |value| parameter is the constant value to be added. + + // Returns a pointer to the definition of an unsigned 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. Instruction* GetUintConstant(uint32_t value) { return GetIntConstant(value, false); } uint32_t GetUintConstantId(uint32_t value) { Instruction* uint_inst = GetUintConstant(value); - return uint_inst->result_id(); + return (uint_inst != nullptr ? uint_inst->result_id() : 0); } // Adds either a signed or unsigned 32 bit integer constant to the binary - // depedning on the |sign|. If |sign| is true then the value is added as a + // depending on the |sign|. If |sign| is true then the value is added as a // signed constant otherwise as an unsigned constant. If |sign| is false the - // value must not be a negative number. + // value must not be a negative number. Returns false if the constant does + // not exists and could be be created. template Instruction* GetIntConstant(T value, bool sign) { // Assert that we are not trying to store a negative number in an unsigned @@ -411,6 +415,10 @@ class InstructionBuilder { uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); + if (type_id == 0) { + return nullptr; + } + // Get the memory managed type so that it is safe to be stored by // GetConstant. analysis::Type* rebuilt_type = diff --git a/third_party/spirv-tools/source/opt/ir_context.cpp b/third_party/spirv-tools/source/opt/ir_context.cpp index 74f387867b..612a831add 100644 --- a/third_party/spirv-tools/source/opt/ir_context.cpp +++ b/third_party/spirv-tools/source/opt/ir_context.cpp @@ -30,7 +30,8 @@ static const int kSpvDecorateBuiltinInIdx = 2; static const int kEntryPointInterfaceInIdx = 3; static const int kEntryPointFunctionIdInIdx = 1; -// Constants for OpenCL.DebugInfo.100 extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; @@ -170,7 +171,9 @@ Instruction* IRContext::KillInst(Instruction* inst) { KillOperandFromDebugInstructions(inst); if (AreAnalysesValid(kAnalysisDefUse)) { - get_def_use_mgr()->ClearInst(inst); + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + def_use_mgr->ClearInst(inst); + for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst); } if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { instr_to_block_.erase(inst); @@ -217,6 +220,8 @@ Instruction* IRContext::KillInst(Instruction* inst) { void IRContext::CollectNonSemanticTree( Instruction* inst, std::unordered_set* to_kill) { if (!inst->HasResultId()) return; + // Debug[No]Line result id is not used, so we are done + if (inst->IsDebugLineInst()) return; std::vector work_list; std::unordered_set seen; work_list.push_back(inst); @@ -437,8 +442,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { if (opcode == SpvOpVariable || IsConstantInst(opcode)) { for (auto it = module()->ext_inst_debuginfo_begin(); it != module()->ext_inst_debuginfo_end(); ++it) { - if (it->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100DebugGlobalVariable) + if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) continue; auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); if (operand.words[0] == id) { @@ -930,7 +934,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { while (line_inst != nullptr) { // Stop at the beginning of the basic block. if (!line_inst->dbg_line_insts().empty()) { line_inst = &line_inst->dbg_line_insts().back(); - if (line_inst->opcode() == SpvOpNoLine) { + if (line_inst->IsNoLine()) { line_inst = nullptr; } break; diff --git a/third_party/spirv-tools/source/opt/ir_context.h b/third_party/spirv-tools/source/opt/ir_context.h index f5f38a648e..658534760d 100644 --- a/third_party/spirv-tools/source/opt/ir_context.h +++ b/third_party/spirv-tools/source/opt/ir_context.h @@ -98,6 +98,7 @@ class IRContext { module_(new Module()), consumer_(std::move(c)), def_use_mgr_(nullptr), + feature_mgr_(nullptr), valid_analyses_(kAnalysisNone), constant_mgr_(nullptr), type_mgr_(nullptr), @@ -116,6 +117,7 @@ class IRContext { module_(std::move(m)), consumer_(std::move(c)), def_use_mgr_(nullptr), + feature_mgr_(nullptr), valid_analyses_(kAnalysisNone), type_mgr_(nullptr), id_to_name_(nullptr), @@ -190,8 +192,8 @@ class IRContext { inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) - // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 - // or DebugInfo extension placed between section 9 and 10. + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. inline Module::inst_iterator ext_inst_debuginfo_begin(); inline Module::inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); @@ -769,6 +771,8 @@ class IRContext { // The instruction decoration manager for |module_|. std::unique_ptr decoration_mgr_; + + // The feature manager for |module_|. std::unique_ptr feature_mgr_; // A map from instructions to the basic block they belong to. This mapping is diff --git a/third_party/spirv-tools/source/opt/ir_loader.cpp b/third_party/spirv-tools/source/opt/ir_loader.cpp index e443ebb5f3..a82b530e6c 100644 --- a/third_party/spirv-tools/source/opt/ir_loader.cpp +++ b/third_party/spirv-tools/source/opt/ir_loader.cpp @@ -19,6 +19,7 @@ #include "DebugInfo.h" #include "OpenCLDebugInfo100.h" #include "source/ext_inst.h" +#include "source/opt/ir_context.h" #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" @@ -37,26 +38,40 @@ IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) inst_index_(0), last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} +bool IsLineInst(const spv_parsed_instruction_t* inst) { + const auto opcode = static_cast(inst->opcode); + if (IsOpLineInst(opcode)) return true; + if (opcode != SpvOpExtInst) return false; + if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) + return false; + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine; +} + bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { ++inst_index_; - const auto opcode = static_cast(inst->opcode); - if (IsDebugLineInst(opcode)) { + if (IsLineInst(inst)) { module()->SetContainsDebugInfo(); last_line_inst_.reset(); - dbg_line_info_.push_back( - Instruction(module()->context(), *inst, last_dbg_scope_)); + dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_); return true; } // If it is a DebugScope or DebugNoScope of debug extension, we do not // create a new instruction, but simply keep the information in // struct DebugScope. + const auto opcode = static_cast(inst->opcode); if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) { const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; - if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { - const OpenCLDebugInfo100Instructions ext_inst_key = - OpenCLDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key == OpenCLDebugInfo100DebugScope) { + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + if (ext_inst_key == CommonDebugInfoDebugScope) { uint32_t inlined_at = 0; if (inst->num_words > kInlinedAtIndex) inlined_at = inst->words[kInlinedAtIndex]; @@ -65,7 +80,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { module()->SetContainsDebugInfo(); return true; } - if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { + if (ext_inst_key == CommonDebugInfoDebugNoScope) { last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); module()->SetContainsDebugInfo(); return true; @@ -94,14 +109,20 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); if (!spv_inst->dbg_line_insts().empty()) { if (extra_line_tracking_ && - (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) { + (!spv_inst->dbg_line_insts().back().IsNoLine())) { last_line_inst_ = std::unique_ptr( spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); } dbg_line_info_.clear(); } else if (last_line_inst_ != nullptr) { last_line_inst_->SetDebugScope(last_dbg_scope_); spv_inst->dbg_line_insts().push_back(*last_line_inst_); + last_line_inst_ = std::unique_ptr( + spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); } const char* src = source_.c_str(); @@ -230,6 +251,35 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { block_->AddInstruction(std::move(spv_inst)); break; } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and " + "DebugValue found inside function", + opcode); + return false; + } + } + } else if (inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case NonSemanticShaderDebugInfo100DebugDeclare: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { + if (block_ == nullptr) { // Inside function but outside blocks + Errorf(consumer_, src, loc, + "Debug info extension instruction found inside function " + "but outside block", + opcode); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + break; + } default: { Errorf(consumer_, src, loc, "Debug info extension instruction other than DebugScope, " diff --git a/third_party/spirv-tools/source/opt/iterator.h b/third_party/spirv-tools/source/opt/iterator.h index 2280582dfe..847e1bbd52 100644 --- a/third_party/spirv-tools/source/opt/iterator.h +++ b/third_party/spirv-tools/source/opt/iterator.h @@ -142,7 +142,7 @@ inline IteratorRange make_range(const IteratorType& begin, template inline IteratorRange make_range(IteratorType&& begin, IteratorType&& end) { - return {std::move(begin), std::move(end)}; + return {std::forward(begin), std::forward(end)}; } // Returns a (begin, end) iterator pair for the given container. diff --git a/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp index 5c10ecec78..da9ba8cc01 100644 --- a/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp +++ b/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp @@ -184,8 +184,8 @@ bool LocalAccessChainConvertPass::IsConstantIndexAccessChain( bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) { if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue || - user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { return true; } SpvOp op = user->opcode(); @@ -333,6 +333,20 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + 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(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -420,6 +434,8 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_terminate_invocation", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } diff --git a/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp b/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp index 05ed28ae8a..5fd4f658d4 100644 --- a/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp +++ b/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp @@ -31,9 +31,9 @@ const uint32_t kStoreValIdInIdx = 1; bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) { if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { return true; } SpvOp op = user->opcode(); @@ -188,6 +188,20 @@ bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + 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(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -209,7 +223,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() { return LocalSingleBlockLoadStoreElim(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } @@ -272,6 +286,8 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_KHR_terminate_invocation", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } diff --git a/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp b/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp index 7eb4b1fda9..051bcada76 100644 --- a/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp +++ b/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp @@ -53,6 +53,20 @@ bool LocalSingleStoreElimPass::AllExtensionsSupported() const { if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + 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(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && + 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", + 32)) { + return false; + } + } return true; } @@ -67,7 +81,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() { ProcessFunction pfn = [this](Function* fp) { return LocalSingleStoreElim(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } @@ -125,6 +139,8 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_KHR_terminate_invocation", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { @@ -223,9 +239,9 @@ Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( case SpvOpCopyObject: break; case SpvOpExtInst: { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { break; } return nullptr; @@ -292,9 +308,9 @@ bool LocalSingleStoreElimPass::RewriteLoads( bool modified = false; for (Instruction* use : uses) { if (use->opcode() == SpvOpStore) continue; - auto dbg_op = use->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) + auto dbg_op = use->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) continue; if (use->opcode() == SpvOpLoad && dominator_analysis->Dominates(store_inst, use)) { diff --git a/third_party/spirv-tools/source/opt/loop_unroller.cpp b/third_party/spirv-tools/source/opt/loop_unroller.cpp index df68bd20ed..aff191febc 100644 --- a/third_party/spirv-tools/source/opt/loop_unroller.cpp +++ b/third_party/spirv-tools/source/opt/loop_unroller.cpp @@ -760,7 +760,7 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, IRContext::Analysis::kAnalysisInstrToBlockMapping); Instruction* new_branch = builder.AddBranch(new_target); - new_branch->set_dbg_line_insts(lines); + if (!lines.empty()) new_branch->AddDebugLine(&lines.back()); new_branch->SetDebugScope(scope); } @@ -873,6 +873,10 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst()); for (Instruction& inst : *basic_block) { + // Do def/use analysis on new lines + for (auto& line : inst.dbg_line_insts()) + def_use_mgr->AnalyzeInstDefUse(&line); + uint32_t old_id = inst.result_id(); // Ignore stores etc. @@ -1098,6 +1102,10 @@ void LoopUtils::Finalize() { Pass::Status LoopUnroller::Process() { bool changed = false; for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + LoopDescriptor* LD = context()->GetLoopDescriptor(&f); for (Loop& loop : *LD) { LoopUtils loop_utils{context(), &loop}; diff --git a/third_party/spirv-tools/source/opt/mem_pass.cpp b/third_party/spirv-tools/source/opt/mem_pass.cpp index 573879842c..ca4889b7cc 100644 --- a/third_party/spirv-tools/source/opt/mem_pass.cpp +++ b/third_party/spirv-tools/source/opt/mem_pass.cpp @@ -20,7 +20,6 @@ #include #include -#include "OpenCLDebugInfo100.h" #include "source/cfa.h" #include "source/opt/basic_block.h" #include "source/opt/dominator_analysis.h" @@ -87,8 +86,8 @@ bool MemPass::IsPtr(uint32_t ptrId) { } const SpvOp op = ptrInst->opcode(); if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true; - if (op != SpvOpFunctionParameter) return false; const uint32_t varTypeId = ptrInst->type_id(); + if (varTypeId == 0) return false; const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); return varTypeInst->opcode() == SpvOpTypePointer; } @@ -226,9 +225,9 @@ MemPass::MemPass() {} bool MemPass::HasOnlySupportedRefs(uint32_t varId) { return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) { - auto dbg_op = user->GetOpenCL100DebugOpcode(); - if (dbg_op == OpenCLDebugInfo100DebugDeclare || - dbg_op == OpenCLDebugInfo100DebugValue) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { return true; } SpvOp op = user->opcode(); diff --git a/third_party/spirv-tools/source/opt/merge_return_pass.cpp b/third_party/spirv-tools/source/opt/merge_return_pass.cpp index f1601049e9..a962a7ccb0 100644 --- a/third_party/spirv-tools/source/opt/merge_return_pass.cpp +++ b/third_party/spirv-tools/source/opt/merge_return_pass.cpp @@ -111,7 +111,9 @@ bool MergeReturnPass::ProcessStructured( } RecordImmediateDominators(function); - AddSingleCaseSwitchAroundFunction(); + if (!AddSingleCaseSwitchAroundFunction()) { + return false; + } std::list order; cfg()->ComputeStructuredOrder(function, &*function->begin(), &order); @@ -770,7 +772,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element, list->insert(pos, new_element); } -void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { +bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() { CreateReturnBlock(); CreateReturn(final_return_block_); @@ -778,7 +780,10 @@ void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { cfg()->RegisterBlock(final_return_block_); } - CreateSingleCaseSwitch(final_return_block_); + if (!CreateSingleCaseSwitch(final_return_block_)) { + return false; + } + return true; } BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { @@ -813,7 +818,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { return new_block; } -void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { +bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { // Insert the switch before any code is run. We have to split the entry // block to make sure the OpVariable instructions remain in the entry block. BasicBlock* start_block = &*function_->begin(); @@ -830,13 +835,17 @@ void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { context(), start_block, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {}, - merge_target->id()); + uint32_t const_zero_id = builder.GetUintConstantId(0u); + if (const_zero_id == 0) { + return false; + } + builder.AddSwitch(const_zero_id, old_block->id(), {}, merge_target->id()); if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { cfg()->RegisterBlock(old_block); cfg()->AddEdges(start_block); } + return true; } bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) { diff --git a/third_party/spirv-tools/source/opt/merge_return_pass.h b/third_party/spirv-tools/source/opt/merge_return_pass.h index 06a3e7b550..4096ce7dd4 100644 --- a/third_party/spirv-tools/source/opt/merge_return_pass.h +++ b/third_party/spirv-tools/source/opt/merge_return_pass.h @@ -277,7 +277,7 @@ class MergeReturnPass : public MemPass { // current function where the switch and case value are both zero and the // default is the merge block. Returns after the switch is executed. Sets // |final_return_block_|. - void AddSingleCaseSwitchAroundFunction(); + bool AddSingleCaseSwitchAroundFunction(); // Creates a new basic block that branches to |header_label_id|. Returns the // new basic block. The block will be the second last basic block in the @@ -286,7 +286,7 @@ class MergeReturnPass : public MemPass { // Creates a one case switch around the executable code of the function with // |merge_target| as the merge node. - void CreateSingleCaseSwitch(BasicBlock* merge_target); + bool CreateSingleCaseSwitch(BasicBlock* merge_target); // Returns true if |function| has an unreachable block that is not a continue // target that simply branches back to the header, or a merge block containing diff --git a/third_party/spirv-tools/source/opt/module.cpp b/third_party/spirv-tools/source/opt/module.cpp index 0c88601097..f97defbd6f 100644 --- a/third_party/spirv-tools/source/opt/module.cpp +++ b/third_party/spirv-tools/source/opt/module.cpp @@ -145,20 +145,21 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { DebugScope last_scope(kNoDebugScope, kNoInlinedAt); const Instruction* last_line_inst = nullptr; bool between_merge_and_branch = false; + bool between_label_and_phi_var = false; auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst, - &between_merge_and_branch, this](const Instruction* i) { + &between_merge_and_branch, &between_label_and_phi_var, + this](const Instruction* i) { // Skip emitting line instructions between merge and branch instructions. auto opcode = i->opcode(); - if (between_merge_and_branch && - (opcode == SpvOpLine || opcode == SpvOpNoLine)) { + if (between_merge_and_branch && i->IsLineInst()) { return; } between_merge_and_branch = false; if (last_line_inst != nullptr) { - // If the current instruction is OpLine and it is the same with - // the last line instruction that is still effective (can be applied + // If the current instruction is OpLine or DebugLine and it is the same + // as the last line instruction that is still effective (can be applied // to the next instruction), we skip writing the current instruction. - if (opcode == SpvOpLine) { + if (i->IsLine()) { uint32_t operand_index = 0; if (last_line_inst->WhileEachInOperand( [&operand_index, i](const uint32_t* word) { @@ -167,39 +168,67 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { })) { return; } - } else if (opcode != SpvOpNoLine && i->dbg_line_insts().empty()) { + } else if (!i->IsNoLine() && i->dbg_line_insts().empty()) { // If the current instruction does not have the line information, // the last line information is not effective any more. Emit OpNoLine - // to specify it. - binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + // or DebugNoLine to specify it. + uint32_t shader_set_id = context() + ->get_feature_mgr() + ->GetExtInstImportId_Shader100DebugInfo(); + if (shader_set_id != 0) { + binary->push_back((5 << 16) | static_cast(SpvOpExtInst)); + binary->push_back(context()->get_type_mgr()->GetVoidTypeId()); + binary->push_back(context()->TakeNextId()); + binary->push_back(shader_set_id); + binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine); + } else { + binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + } last_line_inst = nullptr; } } + + if (opcode == SpvOpLabel) { + between_label_and_phi_var = true; + } else if (opcode != SpvOpVariable && opcode != SpvOpPhi && + !spvtools::opt::IsOpLineInst(opcode)) { + between_label_and_phi_var = false; + } + if (!(skip_nop && i->IsNop())) { const auto& scope = i->GetDebugScope(); if (scope != last_scope) { - // Emit DebugScope |scope| to |binary|. - auto dbg_inst = ext_inst_debuginfo_.begin(); - scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), - dbg_inst->GetSingleWordOperand(2), binary); + // Can only emit nonsemantic instructions after all phi instructions + // in a block so don't emit scope instructions before phi instructions + // for NonSemantic.Shader.DebugInfo.100. + if (!between_label_and_phi_var || + context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()) { + // Emit DebugScope |scope| to |binary|. + auto dbg_inst = ext_inst_debuginfo_.begin(); + scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), + dbg_inst->GetSingleWordOperand(2), binary); + } last_scope = scope; } i->ToBinaryWithoutAttachedDebugInsts(binary); } // Update the last line instruction. - if (spvOpcodeIsBlockTerminator(opcode) || opcode == SpvOpNoLine) { + if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) { last_line_inst = nullptr; } else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { between_merge_and_branch = true; last_line_inst = nullptr; - } else if (opcode == SpvOpLine) { + } else if (i->IsLine()) { last_line_inst = i; } }; ForEachInst(write_inst, true); - // We create new instructions for DebugScope. The bound must be updated. + // We create new instructions for DebugScope and DebugNoLine. The bound must + // be updated. binary->data()[bound_idx] = header_.bound; } diff --git a/third_party/spirv-tools/source/opt/module.h b/third_party/spirv-tools/source/opt/module.h index d609b603ab..0360b7d5b4 100644 --- a/third_party/spirv-tools/source/opt/module.h +++ b/third_party/spirv-tools/source/opt/module.h @@ -103,8 +103,8 @@ class Module { // This is due to decision by the SPIR Working Group, pending publication. inline void AddDebug3Inst(std::unique_ptr d); - // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo) - // instruction to this module. + // Appends a debug info extension (OpenCL.DebugInfo.100, + // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module. inline void AddExtInstDebugInfo(std::unique_ptr d); // Appends an annotation instruction to this module. @@ -192,8 +192,8 @@ class Module { inline IteratorRange debugs3() const; // Iterators for debug info instructions (excluding OpLine & OpNoLine) - // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 - // or DebugInfo extension placed between section 9 and 10. + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. inline inst_iterator ext_inst_debuginfo_begin(); inline inst_iterator ext_inst_debuginfo_end(); inline IteratorRange ext_inst_debuginfo(); diff --git a/third_party/spirv-tools/source/opt/optimizer.cpp b/third_party/spirv-tools/source/opt/optimizer.cpp index 69103b26c9..e74db26f0c 100644 --- a/third_party/spirv-tools/source/opt/optimizer.cpp +++ b/third_party/spirv-tools/source/opt/optimizer.cpp @@ -320,6 +320,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateCombineAccessChainsPass()); } else if (pass_name == "convert-local-access-chains") { RegisterPass(CreateLocalAccessChainConvertPass()); + } else if (pass_name == "replace-desc-array-access-using-var-index") { + RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass()); } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { @@ -385,7 +387,23 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "loop-invariant-code-motion") { RegisterPass(CreateLoopInvariantCodeMotionPass()); } else if (pass_name == "reduce-load-size") { - RegisterPass(CreateReduceLoadSizePass()); + if (pass_args.size() == 0) { + RegisterPass(CreateReduceLoadSizePass()); + } else { + double load_replacement_threshold = 0.9; + if (pass_args.find_first_not_of(".0123456789") == std::string::npos) { + load_replacement_threshold = atof(pass_args.c_str()); + } + + if (load_replacement_threshold >= 0) { + RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold)); + } else { + Error(consumer(), nullptr, {}, + "--reduce-load-size must have no arguments or a non-negative " + "double argument"); + return false; + } + } } else if (pass_name == "redundancy-elimination") { RegisterPass(CreateRedundancyEliminationPass()); } else if (pass_name == "private-to-local") { @@ -401,22 +419,22 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-desc-idx-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-oob-check") { RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-addr-check") { RegisterPass(CreateInstBuffAddrCheckPass(7, 23)); - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "convert-relaxed-to-half") { RegisterPass(CreateConvertRelaxedToHalfPass()); } else if (pass_name == "relax-float-ops") { @@ -499,6 +517,26 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateAmdExtToKhrPass()); } else if (pass_name == "interpolate-fixup") { RegisterPass(CreateInterpolateFixupPass()); + } else if (pass_name == "convert-to-sampled-image") { + if (pass_args.size() > 0) { + auto descriptor_set_binding_pairs = + opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + pass_args.c_str()); + if (!descriptor_set_binding_pairs) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --convert-to-sampled-image: %s", + pass_args.c_str()); + return false; + } + RegisterPass(CreateConvertToSampledImagePass( + std::move(*descriptor_set_binding_pairs))); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid pairs of descriptor set and binding '%s'. Expected a " + "string of : pairs.", + pass_args.c_str()); + return false; + } } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -726,9 +764,9 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() { MakeUnique()); } -Optimizer::PassToken CreateAggressiveDCEPass() { +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { return MakeUnique( - MakeUnique()); + MakeUnique(preserve_interface)); } Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() { @@ -859,9 +897,10 @@ Optimizer::PassToken CreateVectorDCEPass() { return MakeUnique(MakeUnique()); } -Optimizer::PassToken CreateReduceLoadSizePass() { +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold) { return MakeUnique( - MakeUnique()); + MakeUnique(load_replacement_threshold)); } Optimizer::PassToken CreateCombineAccessChainsPass() { @@ -921,6 +960,11 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() { MakeUnique()); } +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() { + return MakeUnique( + MakeUnique()); +} + Optimizer::PassToken CreateDescriptorScalarReplacementPass() { return MakeUnique( MakeUnique()); @@ -940,4 +984,11 @@ Optimizer::PassToken CreateInterpolateFixupPass() { MakeUnique()); } +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs) { + return MakeUnique( + MakeUnique(descriptor_set_binding_pairs)); +} + } // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/pass.cpp b/third_party/spirv-tools/source/opt/pass.cpp index 09b78af933..017aad10f9 100644 --- a/third_party/spirv-tools/source/opt/pass.cpp +++ b/third_party/spirv-tools/source/opt/pass.cpp @@ -43,8 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) { if (status == Status::SuccessWithChange) { ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); } - assert((status == Status::Failure || ctx->IsConsistent()) && - "An analysis in the context is out of date."); + if (!(status == Status::Failure || ctx->IsConsistent())) + assert(false && "An analysis in the context is out of date."); return status; } diff --git a/third_party/spirv-tools/source/opt/passes.h b/third_party/spirv-tools/source/opt/passes.h index aeae44494d..f3c30d578b 100644 --- a/third_party/spirv-tools/source/opt/passes.h +++ b/third_party/spirv-tools/source/opt/passes.h @@ -26,6 +26,7 @@ #include "source/opt/combine_access_chains.h" #include "source/opt/compact_ids_pass.h" #include "source/opt/convert_to_half_pass.h" +#include "source/opt/convert_to_sampled_image_pass.h" #include "source/opt/copy_prop_arrays.h" #include "source/opt/dead_branch_elim_pass.h" #include "source/opt/dead_insert_elim_pass.h" @@ -65,6 +66,7 @@ #include "source/opt/relax_float_ops_pass.h" #include "source/opt/remove_duplicates_pass.h" #include "source/opt/remove_unused_interface_variables_pass.h" +#include "source/opt/replace_desc_array_access_using_var_index.h" #include "source/opt/replace_invalid_opc.h" #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" diff --git a/third_party/spirv-tools/source/opt/private_to_local_pass.cpp b/third_party/spirv-tools/source/opt/private_to_local_pass.cpp index dd6cbbdef8..12a226d5f1 100644 --- a/third_party/spirv-tools/source/opt/private_to_local_pass.cpp +++ b/third_party/spirv-tools/source/opt/private_to_local_pass.cpp @@ -157,8 +157,7 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const { // The cases in this switch have to match the cases in |UpdateUse|. // If we don't know how to update it, it is not valid. - if (inst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { return true; } switch (inst->opcode()) { @@ -183,8 +182,7 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) { // The cases in this switch have to match the cases in |IsValidUse|. If we // don't think it is valid, the optimization will not view the variable as a // candidate, and therefore the use will not be updated. - if (inst->GetOpenCL100DebugOpcode() == - OpenCLDebugInfo100DebugGlobalVariable) { + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst, user); return true; diff --git a/third_party/spirv-tools/source/opt/reduce_load_size.cpp b/third_party/spirv-tools/source/opt/reduce_load_size.cpp index c58adf47cd..e9b8087489 100644 --- a/third_party/spirv-tools/source/opt/reduce_load_size.cpp +++ b/third_party/spirv-tools/source/opt/reduce_load_size.cpp @@ -27,7 +27,6 @@ namespace { const uint32_t kExtractCompositeIdInIdx = 0; const uint32_t kVariableStorageClassInIdx = 0; const uint32_t kLoadPointerInIdx = 0; -const double kThreshold = 0.9; } // namespace @@ -139,7 +138,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { all_elements_used = !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) { - if (use->IsOpenCL100DebugInstr()) return true; + if (use->IsCommonDebugInstr()) return true; if (use->opcode() != SpvOpCompositeExtract || use->NumInOperands() == 1) { return false; @@ -151,6 +150,8 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { bool should_replace = false; if (all_elements_used) { should_replace = false; + } else if (1.0 <= replacement_threshold_) { + should_replace = true; } else { analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -172,7 +173,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { } double percent_used = static_cast(elements_used.size()) / static_cast(total_size); - should_replace = (percent_used < kThreshold); + should_replace = (percent_used < replacement_threshold_); } should_replace_cache_[op_inst->result_id()] = should_replace; diff --git a/third_party/spirv-tools/source/opt/reduce_load_size.h b/third_party/spirv-tools/source/opt/reduce_load_size.h index ccac49be6d..b323845366 100644 --- a/third_party/spirv-tools/source/opt/reduce_load_size.h +++ b/third_party/spirv-tools/source/opt/reduce_load_size.h @@ -27,6 +27,9 @@ namespace opt { // See optimizer.hpp for documentation. class ReduceLoadSize : public Pass { public: + explicit ReduceLoadSize(double replacement_threshold) + : replacement_threshold_(replacement_threshold) {} + const char* name() const override { return "reduce-load-size"; } Status Process() override; @@ -54,6 +57,11 @@ class ReduceLoadSize : public Pass { // on the load feeding |inst|. bool ShouldReplaceExtract(Instruction* inst); + // Threshold to determine whether we have to replace the load or not. If the + // ratio of the used components of the load is less than the threshold, we + // replace the load. + double replacement_threshold_; + // Maps the result id of an OpLoad instruction to the result of whether or // not the OpCompositeExtract that use the id should be replaced. std::unordered_map should_replace_cache_; diff --git a/third_party/spirv-tools/source/opt/redundancy_elimination.cpp b/third_party/spirv-tools/source/opt/redundancy_elimination.cpp index 362e54dc6b..398225bb5d 100644 --- a/third_party/spirv-tools/source/opt/redundancy_elimination.cpp +++ b/third_party/spirv-tools/source/opt/redundancy_elimination.cpp @@ -24,6 +24,10 @@ Pass::Status RedundancyEliminationPass::Process() { ValueNumberTable vnTable(context()); for (auto& func : *get_module()) { + if (func.IsDeclaration()) { + continue; + } + // Build the dominator tree for this function. It is how the code is // traversed. DominatorTree& dom_tree = diff --git a/third_party/spirv-tools/source/opt/reflect.h b/third_party/spirv-tools/source/opt/reflect.h index c7d46df548..c2ffb0beb4 100644 --- a/third_party/spirv-tools/source/opt/reflect.h +++ b/third_party/spirv-tools/source/opt/reflect.h @@ -34,7 +34,7 @@ inline bool IsDebug2Inst(SpvOp opcode) { inline bool IsDebug3Inst(SpvOp opcode) { return opcode == SpvOpModuleProcessed; } -inline bool IsDebugLineInst(SpvOp opcode) { +inline bool IsOpLineInst(SpvOp opcode) { return opcode == SpvOpLine || opcode == SpvOpNoLine; } inline bool IsAnnotationInst(SpvOp opcode) { @@ -51,7 +51,8 @@ inline bool IsTypeInst(SpvOp opcode) { opcode == SpvOpTypeCooperativeMatrixNV; } inline bool IsConstantInst(SpvOp opcode) { - return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp; + return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) || + opcode == SpvOpConstantFunctionPointerINTEL; } inline bool IsCompileTimeConstantInst(SpvOp opcode) { return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull; diff --git a/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp b/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp index 73f16ddf3f..3fcf879557 100644 --- a/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp +++ b/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp @@ -76,7 +76,7 @@ Pass::Status RelaxFloatOpsPass::ProcessImpl() { Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; - bool modified = context()->ProcessEntryPointCallTree(pfn); + bool modified = context()->ProcessReachableCallTree(pfn); return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp b/third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp new file mode 100644 index 0000000000..1082e679b6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp @@ -0,0 +1,423 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/replace_desc_array_access_using_var_index.h" + +#include "source/opt/desc_sroa_util.h" +#include "source/opt/ir_builder.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpAccessChainInOperandIndexes = 1; +const uint32_t kOpTypePointerInOperandType = 1; +const uint32_t kOpTypeArrayInOperandType = 0; +const uint32_t kOpTypeStructInOperandMember = 0; +IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping = + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + +uint32_t GetValueWithKeyExistenceCheck( + uint32_t key, const std::unordered_map& map) { + auto itr = map.find(key); + assert(itr != map.end() && "Key does not exist"); + return itr->second; +} + +} // namespace + +Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + if (descsroautil::IsDescriptorArray(context(), &var)) { + if (ReplaceVariableAccessesWithConstantElements(&var)) + status = Status::SuccessWithChange; + } + } + return status; +} + +bool ReplaceDescArrayAccessUsingVarIndex:: + ReplaceVariableAccessesWithConstantElements(Instruction* var) const { + std::vector work_list; + get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) { + switch (use->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + work_list.push_back(use); + break; + default: + break; + } + }); + + bool updated = false; + for (Instruction* access_chain : work_list) { + if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) == + nullptr) { + ReplaceAccessChain(var, access_chain); + updated = true; + } + } + // Note that we do not consider OpLoad and OpCompositeExtract because + // OpCompositeExtract always has constant literals for indices. + return updated; +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain( + Instruction* var, Instruction* access_chain) const { + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + assert(number_of_elements != 0 && "Number of element is 0"); + if (number_of_elements == 1) { + UseConstIndexForAccessChain(access_chain, 0); + get_def_use_mgr()->AnalyzeInstUse(access_chain); + return; + } + ReplaceUsersOfAccessChain(access_chain, number_of_elements); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain( + Instruction* access_chain, uint32_t number_of_elements) const { + std::vector final_users; + CollectRecursiveUsersWithConcreteType(access_chain, &final_users); + for (auto* inst : final_users) { + std::deque insts_to_be_cloned = + CollectRequiredImageInsts(inst); + ReplaceNonUniformAccessWithSwitchCase( + inst, access_chain, number_of_elements, insts_to_be_cloned); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const { + std::queue work_list; + work_list.push(access_chain); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + get_def_use_mgr()->ForEachUser( + inst_from_work_list, [this, final_users, &work_list](Instruction* use) { + // TODO: Support Boolean type as well. + if (!use->HasResultId() || IsConcreteType(use->type_id())) { + final_users->push_back(use); + } else { + work_list.push(use); + } + }); + } +} + +std::deque +ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts( + Instruction* user_of_image_insts) const { + std::unordered_set seen_inst_ids; + std::queue work_list; + + auto decision_to_include_operand = [this, &seen_inst_ids, + &work_list](uint32_t* idp) { + if (!seen_inst_ids.insert(*idp).second) return; + Instruction* operand = get_def_use_mgr()->GetDef(*idp); + if (context()->get_instr_block(operand) != nullptr && + HasImageOrImagePtrType(operand)) { + work_list.push(operand); + } + }; + + std::deque required_image_insts; + required_image_insts.push_front(user_of_image_insts); + user_of_image_insts->ForEachInId(decision_to_include_operand); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + required_image_insts.push_front(inst_from_work_list); + inst_from_work_list->ForEachInId(decision_to_include_operand); + } + return required_image_insts; +} + +bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType( + const Instruction* inst) const { + assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction"); + return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id())); +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType( + const Instruction* type_inst) const { + if (type_inst->opcode() == SpvOpTypeImage || + type_inst->opcode() == SpvOpTypeSampler || + type_inst->opcode() == SpvOpTypeSampledImage) { + return true; + } + if (type_inst->opcode() == SpvOpTypePointer) { + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType)); + return IsImageOrImagePtrType(pointee_type_inst); + } + if (type_inst->opcode() == SpvOpTypeArray) { + Instruction* element_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType)); + return IsImageOrImagePtrType(element_type_inst); + } + if (type_inst->opcode() != SpvOpTypeStruct) return false; + for (uint32_t in_operand_idx = kOpTypeStructInOperandMember; + in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) { + Instruction* member_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember)); + if (IsImageOrImagePtrType(member_type_inst)) return true; + } + return false; +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType( + uint32_t type_id) const { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + if (type_inst->opcode() == SpvOpTypeInt || + type_inst->opcode() == SpvOpTypeFloat) { + return true; + } + if (type_inst->opcode() == SpvOpTypeVector || + type_inst->opcode() == SpvOpTypeMatrix || + type_inst->opcode() == SpvOpTypeArray) { + return IsConcreteType(type_inst->GetSingleWordInOperand(0)); + } + if (type_inst->opcode() == SpvOpTypeStruct) { + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false; + } + return true; + } + return false; +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const { + auto* case_block = CreateNewBlock(); + AddConstElementAccessToCaseBlock(case_block, access_chain, element_index, + old_ids_to_new_ids); + CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned, + old_ids_to_new_ids); + AddBranchToBlock(case_block, branch_target_id); + UseNewIdsInBlock(case_block, *old_ids_to_new_ids); + return case_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const { + for (auto* inst_to_be_cloned : insts_to_be_cloned) { + if (inst_to_be_cloned == inst_to_skip_cloning) continue; + std::unique_ptr clone(inst_to_be_cloned->Clone(context())); + if (inst_to_be_cloned->HasResultId()) { + uint32_t new_id = context()->TakeNextId(); + clone->SetResultId(new_id); + (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id; + } + get_def_use_mgr()->AnalyzeInstDefUse(clone.get()); + context()->set_instr_block(clone.get(), block); + block->AddInstruction(std::move(clone)); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const { + for (auto block_itr = block->begin(); block_itr != block->end(); + ++block_itr) { + (&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) { + auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp); + if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return; + *idp = old_ids_to_new_ids_itr->second; + }); + get_def_use_mgr()->AnalyzeInstUse(&*block_itr); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& insts_to_be_cloned) const { + // Create merge block and add terminator + auto* block = context()->get_instr_block(access_chain_final_user); + auto* merge_block = SeparateInstructionsIntoNewBlock( + block, access_chain_final_user->NextNode()); + + auto* function = block->GetParent(); + + // Add case blocks + std::vector phi_operands; + std::vector case_block_ids; + for (uint32_t idx = 0; idx < number_of_elements; ++idx) { + std::unordered_map old_ids_to_new_ids_for_cloned_insts; + std::unique_ptr case_block(CreateCaseBlock( + access_chain, idx, insts_to_be_cloned, merge_block->id(), + &old_ids_to_new_ids_for_cloned_insts)); + case_block_ids.push_back(case_block->id()); + function->InsertBasicBlockBefore(std::move(case_block), merge_block); + + // Keep the operand for OpPhi + if (!access_chain_final_user->HasResultId()) continue; + uint32_t phi_operand = + GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(), + old_ids_to_new_ids_for_cloned_insts); + phi_operands.push_back(phi_operand); + } + + // Create default block + std::unique_ptr default_block( + CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands, + merge_block->id())); + uint32_t default_block_id = default_block->id(); + function->InsertBasicBlockBefore(std::move(default_block), merge_block); + + // Create OpSwitch + uint32_t access_chain_index_var_id = + descsroautil::GetFirstIndexOfAccessChain(access_chain); + AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id, + merge_block->id(), case_block_ids); + + // Create phi instructions + if (!phi_operands.empty()) { + uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands, + case_block_ids, default_block_id); + context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id); + } + + // Replace OpPhi incoming block operand that uses |block| with |merge_block| + ReplacePhiIncomingBlock(block->id(), merge_block->id()); +} + +BasicBlock* +ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const { + auto separation_begin = block->begin(); + while (separation_begin != block->end() && + &*separation_begin != separation_begin_inst) { + ++separation_begin; + } + return block->SplitBasicBlock(context(), context()->TakeNextId(), + separation_begin); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const { + auto* new_block = new BasicBlock(std::unique_ptr( + new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {}))); + get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst()); + context()->set_instr_block(new_block->GetLabelInst(), new_block); + return new_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain( + Instruction* access_chain, uint32_t const_element_idx) const { + uint32_t const_element_idx_id = + context()->get_constant_mgr()->GetUIntConst(const_element_idx); + access_chain->SetInOperand(kOpAccessChainInOperandIndexes, + {const_element_idx_id}); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const { + std::unique_ptr access_clone(access_chain->Clone(context())); + UseConstIndexForAccessChain(access_clone.get(), const_element_idx); + + uint32_t new_access_id = context()->TakeNextId(); + (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id; + access_clone->SetResultId(new_access_id); + get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get()); + + context()->set_instr_block(access_clone.get(), case_block); + case_block->AddInstruction(std::move(access_clone)); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock( + BasicBlock* parent_block, uint32_t branch_destination) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + builder.AddBranch(branch_destination); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock( + bool null_const_for_phi_is_needed, std::vector* phi_operands, + uint32_t merge_block_id) const { + auto* default_block = CreateNewBlock(); + AddBranchToBlock(default_block, merge_block_id); + if (!null_const_for_phi_is_needed) return default_block; + + // Create null value for OpPhi + Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]); + auto* null_const_inst = GetConstNull(inst->type_id()); + phi_operands->push_back(null_const_inst->result_id()); + return default_block; +} + +Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull( + uint32_t type_id) const { + assert(type_id != 0 && "Result type is expected"); + auto* type = context()->get_type_mgr()->GetType(type_id); + auto* null_const = context()->get_constant_mgr()->GetConstant(type, {}); + return context()->get_constant_mgr()->GetDefiningInstruction(null_const); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + std::vector> cases; + for (uint32_t i = 0; i < static_cast(case_block_ids.size()); ++i) { + cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]); + } + builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id); +} + +uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction( + BasicBlock* parent_block, const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const { + std::vector incomings; + assert(case_block_ids.size() + 1 == phi_operands.size() && + "Number of Phi operands must be exactly 1 bigger than the one of case " + "blocks"); + for (size_t i = 0; i < case_block_ids.size(); ++i) { + incomings.push_back(phi_operands[i]); + incomings.push_back(case_block_ids[i]); + } + incomings.push_back(phi_operands.back()); + incomings.push_back(default_block_id); + + InstructionBuilder builder{context(), &*parent_block->begin(), + kAnalysisDefUseAndInstrToBlockMapping}; + uint32_t phi_result_type_id = + context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id(); + auto* phi = builder.AddPhi(phi_result_type_id, incomings); + return phi->result_id(); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock( + uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const { + context()->ReplaceAllUsesWithPredicate( + old_incoming_block_id, new_incoming_block_id, + [](Instruction* use) { return use->opcode() == SpvOpPhi; }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h b/third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h new file mode 100644 index 0000000000..e18222c858 --- /dev/null +++ b/third_party/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h @@ -0,0 +1,204 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ +#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class ReplaceDescArrayAccessUsingVarIndex : public Pass { + public: + ReplaceDescArrayAccessUsingVarIndex() {} + + const char* name() const override { + return "replace-desc-array-access-using-var-index"; + } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces all acceses to |var| using variable indices with constant + // elements of the array |var|. Creates switch-case statements to determine + // the value of the variable index for all the possible cases. Returns + // whether replacement is done or not. + bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const; + + // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that + // uses the descriptor variable |var| with the OpAccessChain or + // OpInBoundsAccessChain instruction with a constant Indexes operand. + void ReplaceAccessChain(Instruction* var, Instruction* use) const; + + // Updates the first Indexes operand of the OpAccessChain or + // OpInBoundsAccessChain instruction |access_chain| to let it use a constant + // index |const_element_idx|. + void UseConstIndexForAccessChain(Instruction* access_chain, + uint32_t const_element_idx) const; + + // Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| that accesses an array descriptor variable using variable + // indices with constant elements. |number_of_elements| is the number + // of array elements. + void ReplaceUsersOfAccessChain(Instruction* access_chain, + uint32_t number_of_elements) const; + + // Puts all the recursive users of |access_chain| with concrete result types + // or the ones without result it in |final_users|. + void CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const; + + // Recursively collects the operands of |user_of_image_insts| (and operands + // of the operands) whose result types are images/samplers or pointers/array/ + // struct of them and returns them. + std::deque CollectRequiredImageInsts( + Instruction* user_of_image_insts) const; + + // Returns whether result type of |inst| is an image/sampler/pointer of image + // or sampler or not. + bool HasImageOrImagePtrType(const Instruction* inst) const; + + // Returns whether |type_inst| is an image/sampler or pointer/array/struct of + // image or sampler or not. + bool IsImageOrImagePtrType(const Instruction* type_inst) const; + + // Returns whether the type with |type_id| is a concrete type or not. + bool IsConcreteType(uint32_t type_id) const; + + // Replaces the non-uniform access to a descriptor variable + // |access_chain_final_user| with OpSwitch instruction and case blocks. Each + // case block will contain a clone of |access_chain| and clones of + // |non_uniform_accesses_to_clone| that are recursively used by + // |access_chain_final_user|. The clone of |access_chain| (or + // OpInBoundsAccessChain) will have a constant index for its first index. The + // OpSwitch instruction will have the cases for the variable index of + // |access_chain| from 0 to |number_of_elements| - 1. + void ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& non_uniform_accesses_to_clone) const; + + // Creates and returns a new basic block that contains all instructions of + // |block| after |separation_begin_inst|. The new basic block is added to the + // function in this method. + BasicBlock* SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const; + + // Creates and returns a new block. + BasicBlock* CreateNewBlock() const; + + // Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain + // instruction |access_chain|. + uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const; + + // Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| to |case_block|. The clone of |access_chain| will use + // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the + // mapping from the result id of |access_chain| to the result of its clone. + void AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const; + + // Clones all instructions in |insts_to_be_cloned| and put them to |block|. + // |old_ids_to_new_ids| keeps the mapping from the result id of each + // instruction of |insts_to_be_cloned| to the result of their clones. + void CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const; + + // Adds OpBranch to |branch_destination| at the end of |parent_block|. + void AddBranchToBlock(BasicBlock* parent_block, + uint32_t branch_destination) const; + + // Replaces in-operands of all instructions in the basic block |block| using + // |old_ids_to_new_ids|. It conducts the replacement only if the in-operand + // id is a key of |old_ids_to_new_ids|. + void UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const; + + // Creates a case block for |element_index| case. It adds clones of + // |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as + // its first index. The termination instruction of the created case block will + // be a branch to |branch_target_id|. Puts old ids to new ids map for the + // cloned instructions in |old_ids_to_new_ids|. + BasicBlock* CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const; + + // Creates a default block for switch-case statement that has only a single + // instruction OpBranch whose target is a basic block with |merge_block_id|. + // If |null_const_for_phi_is_needed| is true, gets or creates a default null + // constant value for a phi instruction whose operands are |phi_operands| and + // puts it in |phi_operands|. + BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed, + std::vector* phi_operands, + uint32_t merge_block_id) const; + + // Creates and adds an OpSwitch used for the selection of OpAccessChain whose + // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be + // added at the end of |parent_block|. It will jump to |default_id| for the + // default case and jumps to one of case blocks whoes ids are |case_block_ids| + // if |access_chain_index_var_id| matches the case number. |merge_id| is the + // merge block id. + void AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const; + + // Creates a phi instruction with |phi_operands| as values and + // |case_block_ids| and |default_block_id| as incoming blocks. The size of + // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|. + // The last element of |phi_operands| will be used for |default_block_id|. It + // adds the phi instruction to the beginning of |parent_block|. + uint32_t CreatePhiInstruction(BasicBlock* parent_block, + const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const; + + // Replaces the incoming block operand of OpPhi instructions with + // |new_incoming_block_id| if the incoming block operand is + // |old_incoming_block_id|. + void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id, + uint32_t new_incoming_block_id) const; + + // Create an OpConstantNull instruction whose result type id is |type_id|. + Instruction* GetConstNull(uint32_t type_id) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ diff --git a/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp b/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp index 38b7539bf9..e3b9d3e403 100644 --- a/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp +++ b/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp @@ -71,10 +71,10 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, function->ForEachInst( [model, &modified, &last_line_dbg_inst, this](Instruction* inst) { // Track the debug information so we can have a meaningful message. - if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) { + if (inst->opcode() == SpvOpLabel || inst->IsNoLine()) { last_line_dbg_inst = nullptr; return; - } else if (inst->opcode() == SpvOpLine) { + } else if (inst->IsLine()) { last_line_dbg_inst = inst; return; } @@ -100,8 +100,18 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, ReplaceInstruction(inst, nullptr, 0, 0); } else { // Get the name of the source file. - Instruction* file_name = context()->get_def_use_mgr()->GetDef( - last_line_dbg_inst->GetSingleWordInOperand(0)); + uint32_t file_name_id = 0; + if (last_line_dbg_inst->opcode() == SpvOpLine) { + file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0); + } else { // Shader100::DebugLine + uint32_t debug_source_id = + last_line_dbg_inst->GetSingleWordInOperand(2); + Instruction* debug_source_inst = + context()->get_def_use_mgr()->GetDef(debug_source_id); + file_name_id = debug_source_inst->GetSingleWordInOperand(2); + } + Instruction* file_name = + context()->get_def_use_mgr()->GetDef(file_name_id); const char* source = reinterpret_cast( &file_name->GetInOperand(0).words[0]); diff --git a/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp b/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp index 4d47bdd8df..4d6a7aadd5 100644 --- a/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp +++ b/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp @@ -27,6 +27,7 @@ static const uint32_t kDebugValueOperandValueIndex = 5; static const uint32_t kDebugValueOperandExpressionIndex = 6; +static const uint32_t kDebugDeclareOperandVariableIndex = 5; namespace spvtools { namespace opt { @@ -34,6 +35,10 @@ namespace opt { Pass::Status ScalarReplacementPass::Process() { Status status = Status::SuccessWithoutChange; for (auto& f : *get_module()) { + if (f.IsDeclaration()) { + continue; + } + Status functionStatus = ProcessFunction(&f); if (functionStatus == Status::Failure) return functionStatus; @@ -83,14 +88,14 @@ Pass::Status ScalarReplacementPass::ReplaceVariable( std::vector dead; bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( inst, [this, &replacements, &dead](Instruction* user) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { if (ReplaceWholeDebugDeclare(user, replacements)) { dead.push_back(user); return true; } return false; } - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { if (ReplaceWholeDebugValue(user, replacements)) { dead.push_back(user); return true; @@ -172,10 +177,14 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare( // Add DebugValue instruction with Indexes operand and Deref operation. int32_t idx = 0; for (const auto* var : replacements) { + Instruction* insert_before = var->NextNode(); + while (insert_before->opcode() == SpvOpVariable) + insert_before = insert_before->NextNode(); + assert(insert_before != nullptr && "unexpected end of list"); Instruction* added_dbg_value = context()->get_debug_info_mgr()->AddDebugValueForDecl( dbg_decl, /*value_id=*/var->result_id(), - /*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl); + /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl); if (added_dbg_value == nullptr) return false; added_dbg_value->AddOperand( @@ -504,7 +513,7 @@ void ScalarReplacementPass::CreateVariable( } } - // Update the OpenCL.DebugInfo.100 debug information. + // Update the DebugInfo debug information. inst->UpdateDebugInfoFrom(varInst); replacements->push_back(inst); @@ -791,8 +800,8 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst, get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( const Instruction* user, uint32_t index) { - if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || - user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { // TODO: include num_partial_accesses if it uses Fragment operation or // DebugValue has Indexes operand. stats->num_full_accesses++; @@ -864,6 +873,11 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { case SpvOpImageTexelPointer: if (!CheckImageTexelPointer(index)) ok = false; break; + case SpvOpExtInst: + if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare || + !CheckDebugDeclare(index)) + ok = false; + break; default: ok = false; break; @@ -894,6 +908,12 @@ bool ScalarReplacementPass::CheckStore(const Instruction* inst, return false; return true; } + +bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const { + if (index != kDebugDeclareOperandVariableIndex) return false; + return true; +} + bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const { if (max_num_elements_ == 0) { return false; diff --git a/third_party/spirv-tools/source/opt/scalar_replacement_pass.h b/third_party/spirv-tools/source/opt/scalar_replacement_pass.h index 9e9f073948..0928830c07 100644 --- a/third_party/spirv-tools/source/opt/scalar_replacement_pass.h +++ b/third_party/spirv-tools/source/opt/scalar_replacement_pass.h @@ -142,6 +142,9 @@ class ScalarReplacementPass : public Pass { // of |inst| and the store is not to volatile memory. bool CheckStore(const Instruction* inst, uint32_t index) const; + // Returns true if the DebugDeclare can be scalarized at |index|. + bool CheckDebugDeclare(uint32_t index) const; + // Returns true if |index| is the pointer operand of an OpImageTexelPointer // instruction. bool CheckImageTexelPointer(uint32_t index) const; diff --git a/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp b/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp index 4c8d116f76..4def2b09a9 100644 --- a/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp +++ b/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp @@ -85,6 +85,10 @@ std::vector ParseDefaultValueStr(const char* text, // with 0x1, which represents a 'true'. // If all words in the bit pattern are zero, returns a bit pattern with 0x0, // which represents a 'false'. +// For integer and floating point types narrower than 32 bits, the upper bits +// in the input bit pattern are ignored. Instead the upper bits are set +// according to SPIR-V literal requirements: sign extend a signed integer, and +// otherwise set the upper bits to zero. std::vector ParseDefaultValueBitPattern( const std::vector& input_bit_pattern, const analysis::Type* type) { @@ -98,12 +102,33 @@ std::vector ParseDefaultValueBitPattern( } return result; } else if (const auto* IT = type->AsInteger()) { - if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { - return std::vector(input_bit_pattern); + const auto width = IT->width(); + assert(width > 0); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + const uint32_t high_active_bit = (1u << width) >> 1; + if (IT->IsSigned() && (high_active_bit & result[0])) { + // Sign extend. This overwrites the sign bit again, but that's ok. + result[0] = result[0] | ~(high_active_bit - 1); + } else { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + } + return result; } } else if (const auto* FT = type->AsFloat()) { - if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { - return std::vector(input_bit_pattern); + const auto width = FT->width(); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + return result; } } result.clear(); diff --git a/third_party/spirv-tools/source/opt/simplification_pass.cpp b/third_party/spirv-tools/source/opt/simplification_pass.cpp index 319ceecfd4..43ec15f805 100644 --- a/third_party/spirv-tools/source/opt/simplification_pass.cpp +++ b/third_party/spirv-tools/source/opt/simplification_pass.cpp @@ -45,6 +45,10 @@ void SimplificationPass::AddNewOperands( } bool SimplificationPass::SimplifyFunction(Function* function) { + if (function->IsDeclaration()) { + return false; + } + bool modified = false; // Phase 1: Traverse all instructions in dominance order. // The second phase will only be on the instructions whose inputs have changed diff --git a/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp b/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp index 81770d779f..29ab6123f9 100644 --- a/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp +++ b/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp @@ -753,6 +753,9 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { Pass::Status SSARewritePass::Process() { Status status = Status::SuccessWithoutChange; for (auto& fn : *get_module()) { + if (fn.IsDeclaration()) { + continue; + } status = CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn)); // Kill DebugDeclares for target variables. 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 32d6de94f9..5271e3fe92 100644 --- a/third_party/spirv-tools/source/opt/value_number_table.cpp +++ b/third_party/spirv-tools/source/opt/value_number_table.cpp @@ -50,7 +50,7 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { // OpSampledImage and OpImage must remain in the same basic block in which // they are used, because of this we will assign each one it own value number. if (!context()->IsCombinatorInstruction(inst) && - !inst->IsOpenCL100DebugInstr()) { + !inst->IsCommonDebugInstr()) { value = TakeNextValueNumber(); id_to_value_[inst->result_id()] = value; return value; diff --git a/third_party/spirv-tools/source/opt/vector_dce.cpp b/third_party/spirv-tools/source/opt/vector_dce.cpp index 14a55c1eab..28d94a076f 100644 --- a/third_party/spirv-tools/source/opt/vector_dce.cpp +++ b/third_party/spirv-tools/source/opt/vector_dce.cpp @@ -52,7 +52,7 @@ void VectorDCE::FindLiveComponents(Function* function, // components are live because of arbitrary nesting of structs. function->ForEachInst( [&work_list, this, live_components](Instruction* current_inst) { - if (current_inst->IsOpenCL100DebugInstr()) { + if (current_inst->IsCommonDebugInstr()) { return; } if (!HasVectorOrScalarResult(current_inst) || @@ -110,7 +110,11 @@ void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst, if (current_inst->NumInOperands() < 2) { new_item.components = live_elements; } else { - new_item.components.Set(current_inst->GetSingleWordInOperand(1)); + uint32_t element_index = current_inst->GetSingleWordInOperand(1); + uint32_t item_size = GetVectorComponentCount(operand_inst->type_id()); + if (element_index < item_size) { + new_item.components.Set(element_index); + } } AddItemToWorkListIfNeeded(new_item, live_components, work_list); } @@ -176,10 +180,10 @@ void VectorDCE::MarkVectorShuffleUsesAsLive( second_operand.instruction = def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1)); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::Vector* first_type = - type_mgr->GetType(first_operand.instruction->type_id())->AsVector(); - uint32_t size_of_first_operand = first_type->element_count(); + uint32_t size_of_first_operand = + GetVectorComponentCount(first_operand.instruction->type_id()); + uint32_t size_of_second_operand = + GetVectorComponentCount(second_operand.instruction->type_id()); for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands(); ++in_op) { @@ -187,7 +191,7 @@ void VectorDCE::MarkVectorShuffleUsesAsLive( if (current_item.components.Get(in_op - 2)) { if (index < size_of_first_operand) { first_operand.components.Set(index); - } else { + } else if (index - size_of_first_operand < size_of_second_operand) { second_operand.components.Set(index - size_of_first_operand); } } @@ -202,7 +206,6 @@ void VectorDCE::MarkCompositeContructUsesAsLive( VectorDCE::LiveComponentMap* live_components, std::vector* work_list) { analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); uint32_t current_component = 0; Instruction* current_inst = work_item.instruction; @@ -223,8 +226,7 @@ void VectorDCE::MarkCompositeContructUsesAsLive( assert(HasVectorResult(op_inst)); WorkListItem new_work_item; new_work_item.instruction = op_inst; - uint32_t op_vector_size = - type_mgr->GetType(op_inst->type_id())->AsVector()->element_count(); + uint32_t op_vector_size = GetVectorComponentCount(op_inst->type_id()); for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size; op_vector_idx++, current_component++) { @@ -297,6 +299,18 @@ bool VectorDCE::HasScalarResult(const Instruction* inst) const { } } +uint32_t VectorDCE::GetVectorComponentCount(uint32_t type_id) { + assert(type_id != 0 && + "Trying to get the vector element count, but the type id is 0"); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Vector* vector_type = type->AsVector(); + assert( + vector_type && + "Trying to get the vector element count, but the type is not a vector"); + return vector_type->element_count(); +} + bool VectorDCE::RewriteInstructions( Function* function, const VectorDCE::LiveComponentMap& live_components) { bool modified = false; @@ -394,7 +408,7 @@ void VectorDCE::MarkDebugValueUsesAsDead( Instruction* composite, std::vector* dead_dbg_value) { context()->get_def_use_mgr()->ForEachUser( composite, [&dead_dbg_value](Instruction* use) { - if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) + if (use->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) dead_dbg_value->push_back(use); }); } diff --git a/third_party/spirv-tools/source/opt/vector_dce.h b/third_party/spirv-tools/source/opt/vector_dce.h index 0df9aee13d..4d30b926b8 100644 --- a/third_party/spirv-tools/source/opt/vector_dce.h +++ b/third_party/spirv-tools/source/opt/vector_dce.h @@ -94,12 +94,15 @@ class VectorDCE : public MemPass { // Returns true if the result of |inst| is a vector or a scalar. bool HasVectorOrScalarResult(const Instruction* inst) const; - // Returns true if the result of |inst| is a scalar. + // Returns true if the result of |inst| is a vector. bool HasVectorResult(const Instruction* inst) const; - // Returns true if the result of |inst| is a vector. + // Returns true if the result of |inst| is a scalar. bool HasScalarResult(const Instruction* inst) const; + // Returns the number of elements in the vector type with id |type_id|. + uint32_t GetVectorComponentCount(uint32_t type_id); + // Adds |work_item| to |work_list| if it is not already live according to // |live_components|. |live_components| is updated to indicate that // |work_item| is now live. diff --git a/third_party/spirv-tools/source/spirv_constant.h b/third_party/spirv-tools/source/spirv_constant.h index 39771ccb26..8636806c54 100644 --- a/third_party/spirv-tools/source/spirv_constant.h +++ b/third_party/spirv-tools/source/spirv_constant.h @@ -84,6 +84,7 @@ typedef enum spv_generator_t { SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6, SPV_GENERATOR_KHRONOS_ASSEMBLER = 7, SPV_GENERATOR_KHRONOS_GLSLANG = 8, + SPV_GENERATOR_KHRONOS_LINKER = 17, SPV_GENERATOR_NUM_ENTRIES, SPV_FORCE_16_BIT_ENUM(spv_generator_t) } spv_generator_t; diff --git a/third_party/spirv-tools/source/spirv_target_env.cpp b/third_party/spirv-tools/source/spirv_target_env.cpp index f20ebb4fb5..187ab61ec9 100644 --- a/third_party/spirv-tools/source/spirv_target_env.cpp +++ b/third_party/spirv-tools/source/spirv_target_env.cpp @@ -62,7 +62,7 @@ const char* spvTargetEnvDescription(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return "SPIR-V 1.3 (under Vulkan 1.1 semantics)"; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); break; case SPV_ENV_UNIVERSAL_1_4: return "SPIR-V 1.4"; @@ -72,6 +72,9 @@ const char* spvTargetEnvDescription(spv_target_env env) { return "SPIR-V 1.5"; case SPV_ENV_VULKAN_1_2: return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; } return ""; } @@ -102,7 +105,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return SPV_SPIRV_VERSION_WORD(1, 3); case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); break; case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: @@ -110,6 +113,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: return SPV_SPIRV_VERSION_WORD(1, 5); + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; } return SPV_SPIRV_VERSION_WORD(0, 0); } @@ -212,7 +218,10 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_2: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -246,7 +255,10 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_OPENCL_2_2: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return false; @@ -280,7 +292,43 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_OPENGL_4_5: return true; case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return false; +} + +bool spvIsValidEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return true; + case SPV_ENV_WEBGPU_0: + case SPV_ENV_MAX: break; } return false; @@ -320,7 +368,10 @@ std::string spvLogStringForEnv(spv_target_env env) { return "Universal"; } case SPV_ENV_WEBGPU_0: - assert(false); + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); break; } return "Unknown"; diff --git a/third_party/spirv-tools/source/spirv_target_env.h b/third_party/spirv-tools/source/spirv_target_env.h index a804d615c5..cc06deca74 100644 --- a/third_party/spirv-tools/source/spirv_target_env.h +++ b/third_party/spirv-tools/source/spirv_target_env.h @@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env); // Returns true if |env| is an OPENGL environment, false otherwise. bool spvIsOpenGLEnv(spv_target_env env); +// Returns true if |env| is an implemented/valid environment, false otherwise. +bool spvIsValidEnv(spv_target_env env); + // Returns the version number for the given SPIR-V target environment. uint32_t spvVersionForTargetEnv(spv_target_env env); diff --git a/third_party/spirv-tools/source/spirv_validator_options.cpp b/third_party/spirv-tools/source/spirv_validator_options.cpp index 2716cca9be..e5b1eece0d 100644 --- a/third_party/spirv-tools/source/spirv_validator_options.cpp +++ b/third_party/spirv-tools/source/spirv_validator_options.cpp @@ -120,3 +120,8 @@ void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options, bool val) { options->skip_block_layout = val; } + +void spvValidatorOptionsSetAllowLocalSizeId(spv_validator_options options, + bool val) { + options->allow_localsizeid = val; +} diff --git a/third_party/spirv-tools/source/spirv_validator_options.h b/third_party/spirv-tools/source/spirv_validator_options.h index baaa5359e9..a357c031b5 100644 --- a/third_party/spirv-tools/source/spirv_validator_options.h +++ b/third_party/spirv-tools/source/spirv_validator_options.h @@ -47,6 +47,7 @@ struct spv_validator_options_t { scalar_block_layout(false), workgroup_scalar_block_layout(false), skip_block_layout(false), + allow_localsizeid(false), before_hlsl_legalization(false) {} validator_universal_limits_t universal_limits_; @@ -57,6 +58,7 @@ struct spv_validator_options_t { bool scalar_block_layout; bool workgroup_scalar_block_layout; bool skip_block_layout; + bool allow_localsizeid; bool before_hlsl_legalization; }; diff --git a/third_party/spirv-tools/source/text_handler.cpp b/third_party/spirv-tools/source/text_handler.cpp index c31f34a6bf..46b9845617 100644 --- a/third_party/spirv-tools/source/text_handler.cpp +++ b/third_party/spirv-tools/source/text_handler.cpp @@ -120,7 +120,8 @@ spv_result_t getWord(spv_text text, spv_position position, std::string* word) { case '\n': case '\r': if (escaping || quoting) break; - // Fall through. + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; case '\0': { // NOTE: End of word found! word->assign(text->str + start_index, text->str + position->index); return SPV_SUCCESS; diff --git a/third_party/spirv-tools/source/util/hex_float.h b/third_party/spirv-tools/source/util/hex_float.h index cfc40fa689..be28eae3ae 100644 --- a/third_party/spirv-tools/source/util/hex_float.h +++ b/third_party/spirv-tools/source/util/hex_float.h @@ -1005,6 +1005,9 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { is.get(); next_char = is.peek(); } + + // Finished reading the part preceding any '.' or 'p'. + bits_written = false; while (seen_dot && !seen_p) { // Handle only fractional parts now. @@ -1037,11 +1040,16 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { next_char = is.peek(); } + // Finished reading the part preceding 'p'. + // In hex floats syntax, the binary exponent is required. + bool seen_sign = false; int8_t exponent_sign = 1; + bool seen_written_exponent_digits = false; int_type written_exponent = 0; while (true) { - if ((next_char == '-' || next_char == '+')) { + if (!seen_written_exponent_digits && + (next_char == '-' || next_char == '+')) { if (seen_sign) { is.setstate(std::ios::failbit); return is; @@ -1049,6 +1057,7 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { seen_sign = true; exponent_sign = (next_char == '-') ? -1 : 1; } else if (::isdigit(next_char)) { + seen_written_exponent_digits = true; // Hex-floats express their exponent as decimal. written_exponent = static_cast(written_exponent * 10); written_exponent = @@ -1059,6 +1068,11 @@ std::istream& operator>>(std::istream& is, HexFloat& value) { is.get(); next_char = is.peek(); } + if (!seen_written_exponent_digits) { + // Binary exponent had no digits. + is.setstate(std::ios::failbit); + return is; + } written_exponent = static_cast(written_exponent * exponent_sign); exponent = static_cast(exponent + written_exponent); diff --git a/third_party/spirv-tools/source/val/construct.cpp b/third_party/spirv-tools/source/val/construct.cpp index 5300869062..251e2bbaee 100644 --- a/third_party/spirv-tools/source/val/construct.cpp +++ b/third_party/spirv-tools/source/val/construct.cpp @@ -181,8 +181,9 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { for (auto& use : block->label()->uses()) { if ((use.first->opcode() == SpvOpLoopMerge || use.first->opcode() == SpvOpSelectionMerge) && - use.second == 1) + use.second == 1 && use.first->block()->dominates(*block)) { return use.first->block(); + } } return block->immediate_dominator(); }; diff --git a/third_party/spirv-tools/source/val/function.cpp b/third_party/spirv-tools/source/val/function.cpp index 249c8664fe..9ad68e8672 100644 --- a/third_party/spirv-tools/source/val/function.cpp +++ b/third_party/spirv-tools/source/val/function.cpp @@ -308,6 +308,9 @@ int Function::GetBlockDepth(BasicBlock* bb) { if (block_depth_.find(bb) != block_depth_.end()) { return block_depth_[bb]; } + // Avoid recursion. Something is wrong if the same block is encountered + // multiple times. + block_depth_[bb] = 0; BasicBlock* bb_dom = bb->immediate_dominator(); if (!bb_dom || bb == bb_dom) { diff --git a/third_party/spirv-tools/source/val/validate_adjacency.cpp b/third_party/spirv-tools/source/val/validate_adjacency.cpp index 64655b0dc7..8e6c373ea9 100644 --- a/third_party/spirv-tools/source/val/validate_adjacency.cpp +++ b/third_party/spirv-tools/source/val/validate_adjacency.cpp @@ -60,7 +60,10 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) { // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, // and DebugValue. - if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) { + // NOTE: This does not apply to the non-semantic vulkan debug info. + if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) || + inst.ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { adjacency_status = PHI_AND_VAR_INVALID; } break; diff --git a/third_party/spirv-tools/source/val/validate_annotation.cpp b/third_party/spirv-tools/source/val/validate_annotation.cpp index 85d2b7515a..3a77552e18 100644 --- a/third_party/spirv-tools/source/val/validate_annotation.cpp +++ b/third_party/spirv-tools/source/val/validate_annotation.cpp @@ -138,14 +138,14 @@ std::string LogStringForDecoration(uint32_t decoration) { return "PerTaskNV"; case SpvDecorationPerVertexNV: return "PerVertexNV"; - case SpvDecorationNonUniformEXT: - return "NonUniformEXT"; - case SpvDecorationRestrictPointerEXT: - return "RestrictPointerEXT"; - case SpvDecorationAliasedPointerEXT: - return "AliasedPointerEXT"; - case SpvDecorationHlslCounterBufferGOOGLE: - return "HlslCounterBufferGOOGLE"; + case SpvDecorationNonUniform: + return "NonUniform"; + case SpvDecorationRestrictPointer: + return "RestrictPointer"; + case SpvDecorationAliasedPointer: + return "AliasedPointer"; + case SpvDecorationCounterBuffer: + return "CounterBuffer"; case SpvDecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; default: @@ -156,8 +156,8 @@ std::string LogStringForDecoration(uint32_t decoration) { // Returns true if the decoration takes ID parameters. // TODO(dneto): This can be generated from the grammar. -bool DecorationTakesIdParameters(uint32_t type) { - switch (static_cast(type)) { +bool DecorationTakesIdParameters(SpvDecoration type) { + switch (type) { case SpvDecorationUniformId: case SpvDecorationAlignmentId: case SpvDecorationMaxByteOffsetId: @@ -169,17 +169,213 @@ bool DecorationTakesIdParameters(uint32_t type) { return false; } -spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { - const auto decoration = inst->GetOperandAs(1); - if (decoration == SpvDecorationSpecId) { - const auto target_id = inst->GetOperandAs(0); - const auto target = _.FindDef(target_id); - if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpDecorate SpecId decoration target '" - << _.getIdName(target_id) - << "' is not a scalar specialization constant."; +bool IsMemberDecorationOnly(SpvDecoration dec) { + switch (dec) { + case SpvDecorationRowMajor: + case SpvDecorationColMajor: + case SpvDecorationMatrixStride: + // SPIR-V spec bug? Offset is generated on variables when dealing with + // transform feedback. + // case SpvDecorationOffset: + return true; + default: + break; + } + return false; +} + +bool IsNotMemberDecoration(SpvDecoration dec) { + switch (dec) { + case SpvDecorationSpecId: + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationArrayStride: + case SpvDecorationGLSLShared: + case SpvDecorationGLSLPacked: + case SpvDecorationCPacked: + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + // case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationConstant: + case SpvDecorationUniform: + case SpvDecorationUniformId: + case SpvDecorationSaturatedConversion: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationFuncParamAttr: + case SpvDecorationFPRoundingMode: + case SpvDecorationFPFastMathMode: + case SpvDecorationLinkageAttributes: + case SpvDecorationNoContraction: + case SpvDecorationInputAttachmentIndex: + case SpvDecorationAlignment: + case SpvDecorationMaxByteOffset: + case SpvDecorationAlignmentId: + case SpvDecorationMaxByteOffsetId: + case SpvDecorationNoSignedWrap: + case SpvDecorationNoUnsignedWrap: + case SpvDecorationNonUniform: + case SpvDecorationRestrictPointer: + case SpvDecorationAliasedPointer: + case SpvDecorationCounterBuffer: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, + const Instruction* inst, + const Instruction* target) { + auto fail = [&_, dec, inst, target](uint32_t vuid = 0) -> DiagnosticStream { + DiagnosticStream ds = std::move( + _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(vuid) << LogStringForDecoration(dec) + << " decoration on target '" << _.getIdName(target->id()) << "' "); + return ds; + }; + switch (dec) { + case SpvDecorationSpecId: + if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { + return fail() << "must be a scalar specialization constant"; + } + break; + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + case SpvDecorationGLSLShared: + case SpvDecorationGLSLPacked: + case SpvDecorationCPacked: + if (target->opcode() != SpvOpTypeStruct) { + return fail() << "must be a structure type"; + } + break; + case SpvDecorationArrayStride: + if (target->opcode() != SpvOpTypeArray && + target->opcode() != SpvOpTypeRuntimeArray && + target->opcode() != SpvOpTypePointer) { + return fail() << "must be an array or pointer type"; + } + break; + case SpvDecorationBuiltIn: + if (target->opcode() != SpvOpVariable && + !spvOpcodeIsConstant(target->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "BuiltIns can only target variables, structure members or " + "constants"; + } + if (_.HasCapability(SpvCapabilityShader) && + inst->GetOperandAs(2) == SpvBuiltInWorkgroupSize) { + if (!spvOpcodeIsConstant(target->opcode())) { + return fail() << "must be a constant for WorkgroupSize"; + } + } else if (target->opcode() != SpvOpVariable) { + return fail() << "must be a variable"; + } + break; + case SpvDecorationNoPerspective: + case SpvDecorationFlat: + case SpvDecorationPatch: + case SpvDecorationCentroid: + case SpvDecorationSample: + case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationVolatile: + case SpvDecorationCoherent: + case SpvDecorationNonWritable: + case SpvDecorationNonReadable: + case SpvDecorationXfbBuffer: + case SpvDecorationXfbStride: + case SpvDecorationComponent: + case SpvDecorationStream: + case SpvDecorationRestrictPointer: + case SpvDecorationAliasedPointer: + if (target->opcode() != SpvOpVariable && + target->opcode() != SpvOpFunctionParameter) { + return fail() << "must be a memory object declaration"; + } + if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) { + return fail() << "must be a pointer type"; + } + break; + case SpvDecorationInvariant: + case SpvDecorationConstant: + case SpvDecorationLocation: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationInputAttachmentIndex: + if (target->opcode() != SpvOpVariable) { + return fail() << "must be a variable"; + } + break; + default: + break; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + // The following were all checked as pointer types above. + SpvStorageClass sc = SpvStorageClassUniform; + const auto type = _.FindDef(target->type_id()); + if (type && type->operands().size() > 2) { + sc = type->GetOperandAs(1); } + switch (dec) { + case SpvDecorationLocation: + case SpvDecorationComponent: + // Location is used for input, output and ray tracing stages. + if (sc == SpvStorageClassStorageBuffer || + sc == SpvStorageClassUniform || + sc == SpvStorageClassUniformConstant || + sc == SpvStorageClassWorkgroup || sc == SpvStorageClassPrivate || + sc == SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_ID, target) + << LogStringForDecoration(dec) + << " decoration must not be applied to this storage class"; + } + break; + case SpvDecorationIndex: + if (sc != SpvStorageClassOutput) { + return fail() << "must be in the Output storage class"; + } + break; + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + if (sc != SpvStorageClassStorageBuffer && + sc != SpvStorageClassUniform && + sc != SpvStorageClassUniformConstant) { + return fail() << "must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class"; + } + break; + case SpvDecorationInputAttachmentIndex: + if (sc != SpvStorageClassUniformConstant) { + return fail() << "must be in the UniformConstant storage class"; + } + break; + case SpvDecorationFlat: + case SpvDecorationNoPerspective: + case SpvDecorationCentroid: + case SpvDecorationSample: + if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput) { + return fail(4670) << "storage class must be Input or Output"; + } + break; + default: + break; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + const auto target_id = inst->GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (!target) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined"; } if (spvIsVulkanEnv(_.context()->target_env)) { @@ -197,17 +393,34 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { << "Decorations taking ID parameters may not be used with " "OpDecorateId"; } + + if (target->opcode() != SpvOpDecorationGroup) { + if (IsMemberDecorationOnly(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << LogStringForDecoration(decoration) + << " can only be applied to structure members"; + } + + if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) { + return error; + } + } + // TODO: Add validations for all decorations. return SPV_SUCCESS; } spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { - const auto decoration = inst->GetOperandAs(1); + const auto decoration = inst->GetOperandAs(1); if (!DecorationTakesIdParameters(decoration)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Decorations that don't take ID parameters may not be used with " "OpDecorateId"; } + + // No member decorations take id parameters, so we don't bother checking if + // we are using a member only decoration here. + // TODO: Add validations for these decorations. // UniformId is covered elsewhere. return SPV_SUCCESS; @@ -234,6 +447,13 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _, << " members. Largest valid index is " << member_count - 1 << "."; } + const auto decoration = inst->GetOperandAs(2); + if (IsNotMemberDecoration(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << LogStringForDecoration(decoration) + << " cannot be applied to structure members"; + } + return SPV_SUCCESS; } diff --git a/third_party/spirv-tools/source/val/validate_atomics.cpp b/third_party/spirv-tools/source/val/validate_atomics.cpp index da023b80c5..cfa15d9f72 100644 --- a/third_party/spirv-tools/source/val/validate_atomics.cpp +++ b/third_party/spirv-tools/source/val/validate_atomics.cpp @@ -184,7 +184,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { } // Can't use result_type because OpAtomicStore doesn't have a result - if (_.GetBitWidth(data_type) == 64 && _.IsIntScalarType(data_type) && + if ( _.IsIntScalarType(data_type) &&_.GetBitWidth(data_type) == 64 && !_.HasCapability(SpvCapabilityInt64Atomics)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) diff --git a/third_party/spirv-tools/source/val/validate_builtins.cpp b/third_party/spirv-tools/source/val/validate_builtins.cpp index 3c9df9fc2d..57dde8ad92 100644 --- a/third_party/spirv-tools/source/val/validate_builtins.cpp +++ b/third_party/spirv-tools/source/val/validate_builtins.cpp @@ -2846,7 +2846,7 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, or TaskNV execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -2928,7 +2928,7 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, or TaskNV execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -3070,14 +3070,16 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( const Instruction& referenced_from_inst) { if (spvIsVulkanEnv(_.context()->target_env)) { for (const SpvExecutionModel execution_model : execution_models_) { - if (execution_model != SpvExecutionModelGLCompute) { + if (execution_model != SpvExecutionModelGLCompute && + execution_model != SpvExecutionModelTaskNV && + execution_model != SpvExecutionModelMeshNV) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) << _.VkErrorID(4425) << spvLogStringForEnv(_.context()->target_env) << " spec allows BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]) - << " to be used only with GLCompute execution model. " + << " to be used only with GLCompute, MeshNV, or TaskNV execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); } @@ -3991,14 +3993,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( const Decoration& decoration, const Instruction& inst) { const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); - // Builtins can only be applied to variables, structures or constants. - auto target_opcode = inst.opcode(); - if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable && - !spvOpcodeIsConstant(target_opcode)) { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << "BuiltIns can only target variables, structs or constants"; - } - if (!spvIsVulkanEnv(_.context()->target_env)) { // Early return. All currently implemented rules are based on Vulkan spec. // @@ -4190,6 +4184,7 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInMeshViewIndicesNV: case SpvBuiltInBaryCoordNV: case SpvBuiltInBaryCoordNoPerspNV: + case SpvBuiltInCurrentRayTimeNV: // No validation rules (for the moment). break; diff --git a/third_party/spirv-tools/source/val/validate_cfg.cpp b/third_party/spirv-tools/source/val/validate_cfg.cpp index 36f632a8d8..7842e56de2 100644 --- a/third_party/spirv-tools/source/val/validate_cfg.cpp +++ b/third_party/spirv-tools/source/val/validate_cfg.cpp @@ -199,6 +199,18 @@ spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) { // At least two operands (selector, default), any more than that are // literal/target. + const auto sel_type_id = _.GetOperandTypeId(inst, 0); + if (!_.IsIntScalarType(sel_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Selector type must be OpTypeInt"; + } + + const auto default_label = _.FindDef(inst->GetOperandAs(1)); + if (default_label->opcode() != SpvOpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Default must be an OpLabel instruction"; + } + // target operands must be OpLabel for (size_t i = 2; i < num_operands; i += 2) { // literal, id @@ -647,9 +659,9 @@ spv_result_t ValidateStructuredSelections( // Mark the upcoming blocks as seen now, but only error out if this block // was missing a merge instruction and both labels hadn't been seen // previously. - const bool both_unseen = - seen.insert(true_label).second && seen.insert(false_label).second; - if (!merge && both_unseen) { + const bool true_label_unseen = seen.insert(true_label).second; + const bool false_label_unseen = seen.insert(false_label).second; + if (!merge && true_label_unseen && false_label_unseen) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) << "Selection must be structured"; } diff --git a/third_party/spirv-tools/source/val/validate_decorations.cpp b/third_party/spirv-tools/source/val/validate_decorations.cpp index f076b04c76..3cdb471c70 100644 --- a/third_party/spirv-tools/source/val/validate_decorations.cpp +++ b/third_party/spirv-tools/source/val/validate_decorations.cpp @@ -129,18 +129,30 @@ std::vector getStructMembers(uint32_t struct_id, SpvOp type, // Returns whether the given structure is missing Offset decoration for any // member. Handles also nested structures. bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) { - std::vector hasOffset(getStructMembers(struct_id, vstate).size(), - false); - // Check offsets of member decorations - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (SpvDecorationOffset == decoration.dec_type() && - Decoration::kInvalidMember != decoration.struct_member_index()) { - hasOffset[decoration.struct_member_index()] = true; + const auto* inst = vstate.FindDef(struct_id); + std::vector hasOffset; + std::vector struct_members; + if (inst->opcode() == SpvOpTypeStruct) { + // Check offsets of member decorations. + struct_members = getStructMembers(struct_id, vstate); + hasOffset.resize(struct_members.size(), false); + + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (SpvDecorationOffset == decoration.dec_type() && + Decoration::kInvalidMember != decoration.struct_member_index()) { + // Offset 0xffffffff is not valid so ignore it for simplicity's sake. + if (decoration.params()[0] == 0xffffffff) return true; + hasOffset[decoration.struct_member_index()] = true; + } } + } else if (inst->opcode() == SpvOpTypeArray || + inst->opcode() == SpvOpTypeRuntimeArray) { + hasOffset.resize(1, true); + struct_members.push_back(inst->GetOperandAs(1u)); } - // Check also nested structures + // Look through nested structs (which may be in an array). bool nestedStructsMissingOffset = false; - for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) { + for (auto id : struct_members) { if (isMissingOffsetInStruct(id, vstate)) { nestedStructsMissingOffset = true; break; @@ -506,12 +518,10 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, return recursive_status; // Check matrix stride. if (SpvOpTypeMatrix == opcode) { - for (auto& decoration : vstate.id_decorations(id)) { - if (SpvDecorationMatrixStride == decoration.dec_type() && - !IsAlignedTo(decoration.params()[0], alignment)) - return fail(memberIdx) - << "is a matrix with stride " << decoration.params()[0] - << " not satisfying alignment to " << alignment; + const auto stride = constraint.matrix_stride; + if (!IsAlignedTo(stride, alignment)) { + return fail(memberIdx) << "is a matrix with stride " << stride + << " not satisfying alignment to " << alignment; } } @@ -549,17 +559,23 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, // limitation to this check if the array size is a spec constant or is a // runtime array then we will only check a single element. This means // some improper straddles might be missed. - for (uint32_t i = 0; i < num_elements; ++i) { - uint32_t next_offset = i * array_stride + offset; - if (SpvOpTypeStruct == element_inst->opcode() && - SPV_SUCCESS != (recursive_status = checkLayout( - typeId, storage_class_str, decoration_str, - blockRules, scalar_block_layout, - next_offset, constraints, vstate))) - return recursive_status; - // If offsets accumulate up to a 16-byte multiple stop checking since - // it will just repeat. - if (i > 0 && (next_offset % 16 == 0)) break; + if (SpvOpTypeStruct == element_inst->opcode()) { + std::vector seen(16, false); + for (uint32_t i = 0; i < num_elements; ++i) { + uint32_t next_offset = i * array_stride + offset; + // Stop checking if offsets repeat in terms of 16-byte multiples. + if (seen[next_offset % 16]) { + break; + } + + if (SPV_SUCCESS != + (recursive_status = checkLayout( + typeId, storage_class_str, decoration_str, blockRules, + scalar_block_layout, next_offset, constraints, vstate))) + return recursive_status; + + seen[next_offset % 16] = true; + } } // Proceed to the element in case it is an array. @@ -981,7 +997,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool phys_storage_buffer = storageClass == SpvStorageClassPhysicalStorageBufferEXT; - const bool workgroup = storageClass == SpvStorageClassWorkgroup; + const bool workgroup = + storageClass == SpvStorageClassWorkgroup && + vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR); if (uniform || push_constant || storage_buffer || phys_storage_buffer || workgroup) { const auto ptrInst = vstate.FindDef(words[1]); diff --git a/third_party/spirv-tools/source/val/validate_derivatives.cpp b/third_party/spirv-tools/source/val/validate_derivatives.cpp index 067cc964ce..25b941abaf 100644 --- a/third_party/spirv-tools/source/val/validate_derivatives.cpp +++ b/third_party/spirv-tools/source/val/validate_derivatives.cpp @@ -79,11 +79,13 @@ spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) { std::string* message) { const auto* models = state.GetExecutionModels(entry_point->id()); const auto* modes = state.GetExecutionModes(entry_point->id()); - if (models->find(SpvExecutionModelGLCompute) != models->end() && - modes->find(SpvExecutionModeDerivativeGroupLinearNV) == - modes->end() && - modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == - modes->end()) { + if (models && + models->find(SpvExecutionModelGLCompute) != models->end() && + (!modes || + (modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()))) { if (message) { *message = std::string( "Derivative instructions require " diff --git a/third_party/spirv-tools/source/val/validate_extensions.cpp b/third_party/spirv-tools/source/val/validate_extensions.cpp index b5b98783e3..dccbe14908 100644 --- a/third_party/spirv-tools/source/val/validate_extensions.cpp +++ b/third_party/spirv-tools/source/val/validate_extensions.cpp @@ -20,7 +20,9 @@ #include "spirv/unified1/NonSemanticClspvReflection.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/common_debug_info.h" #include "source/diagnostic.h" #include "source/enum_string_mapping.h" #include "source/extensions.h" @@ -45,6 +47,34 @@ uint32_t GetSizeTBitWidth(const ValidationState_t& _) { return 0; } +bool IsIntScalar(ValidationState_t& _, uint32_t id, bool must_len32, + bool must_unsigned) { + auto type = _.FindDef(id); + if (!type || type->opcode() != SpvOpTypeInt) { + return false; + } + + if (must_len32 && type->GetOperandAs(1) != 32) { + return false; + } + + return !must_unsigned || type->GetOperandAs(2) == 0; +} + +bool IsUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != SpvOpConstant) { + return false; + } + + return IsIntScalar(_, inst->type_id(), true, true); +} + +uint32_t GetUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + return inst->word(3); +} + // Check that the operand of a debug info instruction |inst| at |word_index| // is a result id of an instruction with |expected_opcode|. spv_result_t ValidateOperandForDebugInfo( @@ -68,6 +98,22 @@ spv_result_t ValidateOperandForDebugInfo( return SPV_SUCCESS; } +// For NonSemantic.Shader.DebugInfo.100 check that the operand of a debug info +// instruction |inst| at |word_index| is a result id of a 32-bit integer +// OpConstant instruction. For OpenCL.DebugInfo.100 the parameter is a literal +// word so cannot be validated. +spv_result_t ValidateUint32ConstantOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + if (!IsUint32Constant(_, inst->word(word_index))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand " << operand_name + << " must be a result id of 32-bit unsigned OpConstant"; + } + return SPV_SUCCESS; +} + #define CHECK_OPERAND(NAME, opcode, index) \ do { \ auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ @@ -75,18 +121,27 @@ spv_result_t ValidateOperandForDebugInfo( if (result != SPV_SUCCESS) return result; \ } while (0) +#define CHECK_CONST_UINT_OPERAND(NAME, index) \ + if (vulkanDebugInfo) { \ + auto result = ValidateUint32ConstantOperandForDebugInfo( \ + _, NAME, inst, index, ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } + // True if the operand of a debug info instruction |inst| at |word_index| // satisifies |expectation| that is given as a function. Otherwise, // returns false. bool DoesDebugInfoOperandMatchExpectation( const ValidationState_t& _, - const std::function& expectation, + const std::function& expectation, const Instruction* inst, uint32_t word_index) { if (inst->words().size() <= word_index) return false; auto* debug_inst = _.FindDef(inst->word(word_index)); if (debug_inst->opcode() != SpvOpExtInst || - debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || - !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) { + (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 && + debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || + !expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) { return false; } return true; @@ -97,20 +152,18 @@ bool DoesDebugInfoOperandMatchExpectation( // is |expected_debug_inst|. spv_result_t ValidateDebugInfoOperand( ValidationState_t& _, const std::string& debug_inst_name, - OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst, + CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name) { - std::function expectation = - [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) { + std::function expectation = + [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) { return dbg_inst == expected_debug_inst; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; spv_ext_inst_desc desc = nullptr; - _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, - expected_debug_inst, &desc); - if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, - expected_debug_inst, &desc) != SPV_SUCCESS || + if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst, + &desc) != SPV_SUCCESS || !desc) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -134,9 +187,8 @@ spv_result_t ValidateDebugInfoOperand( spv_result_t ValidateOperandBaseType( ValidationState_t& _, const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name) { - return ValidateDebugInfoOperand(_, "Base Type", - OpenCLDebugInfo100DebugTypeBasic, inst, - word_index, ext_inst_name); + return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic, + inst, word_index, ext_inst_name); } // Check that the operand of a debug info instruction |inst| at |word_index| @@ -147,12 +199,12 @@ spv_result_t ValidateOperandLexicalScope( ValidationState_t& _, const std::string& debug_inst_name, const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name) { - std::function expectation = - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit || - dbg_inst == OpenCLDebugInfo100DebugFunction || - dbg_inst == OpenCLDebugInfo100DebugLexicalBlock || - dbg_inst == OpenCLDebugInfo100DebugTypeComposite; + std::function expectation = + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugCompilationUnit || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugLexicalBlock || + dbg_inst == CommonDebugInfoDebugTypeComposite; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -171,16 +223,15 @@ spv_result_t ValidateOperandDebugType( const Instruction* inst, uint32_t word_index, const std::function& ext_inst_name, bool allow_template_param) { - std::function expectation = - [&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) { + std::function expectation = + [&allow_template_param](CommonDebugInfoInstructions dbg_inst) { if (allow_template_param && - (dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter || - dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) { + (dbg_inst == CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == CommonDebugInfoDebugTypeTemplateTemplateParameter)) { return true; } - return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst && - dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate; + return CommonDebugInfoDebugTypeBasic <= dbg_inst && + dbg_inst <= CommonDebugInfoDebugTypeTemplate; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -191,28 +242,6 @@ spv_result_t ValidateOperandDebugType( << " is not a valid debug type"; } -bool IsUint32Constant(ValidationState_t& _, uint32_t id) { - auto inst = _.FindDef(id); - if (!inst || inst->opcode() != SpvOpConstant) { - return false; - } - - auto type = _.FindDef(inst->type_id()); - if (!type || type->opcode() != SpvOpTypeInt) { - return false; - } - - if (type->GetOperandAs(1) != 32) { - return false; - } - - if (type->GetOperandAs(2) != 0) { - return false; - } - - return true; -} - spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, const Instruction* inst) { const auto kernel_id = inst->GetOperandAs(4); @@ -667,18 +696,26 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _, const Instruction* inst, uint32_t word_index) { auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index)); - if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == - OpenCLDebugInfo100DebugLocalVariable || - OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == - OpenCLDebugInfo100DebugGlobalVariable) { + if (CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugGlobalVariable) { auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6)); - if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) == - OpenCLDebugInfo100DebugTypeBasic && - (OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == - OpenCLDebugInfo100Signed || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == - OpenCLDebugInfo100Unsigned)) { - return true; + if (CommonDebugInfoInstructions(dbg_type->word(4)) == + CommonDebugInfoDebugTypeBasic) { + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + uint32_t encoding = dbg_type->word(7); + if (!vulkanDebugInfo || IsUint32Constant(_, encoding)) { + auto ocl_encoding = OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo ? GetUint32Constant(_, encoding) : encoding); + if (ocl_encoding == OpenCLDebugInfo100Signed || + ocl_encoding == OpenCLDebugInfo100Unsigned) { + return true; + } + } } } return false; @@ -812,7 +849,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { for (uint32_t operand_index = 4; operand_index < num_operands; ++operand_index) { const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (!_.IsIntScalarOrVectorType(operand_type)) { + if (!operand_type || !_.IsIntScalarOrVectorType(operand_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " << "expected all operands to be int scalars or vectors"; @@ -2668,7 +2705,9 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { break; } } - } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { if (!_.IsVoidType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " @@ -2676,447 +2715,552 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { << "OpTypeVoid"; } + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + auto num_words = inst->words().size(); - const OpenCLDebugInfo100Instructions ext_inst_key = - OpenCLDebugInfo100Instructions(ext_inst_index); - switch (ext_inst_key) { - case OpenCLDebugInfo100DebugInfoNone: - case OpenCLDebugInfo100DebugNoScope: - case OpenCLDebugInfo100DebugOperation: - // The binary parser validates the opcode for DebugInfoNone, - // DebugNoScope, DebugOperation, and the literal values don't need - // further checks. - break; - case OpenCLDebugInfo100DebugCompilationUnit: { - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - break; - } - case OpenCLDebugInfo100DebugSource: { - CHECK_OPERAND("File", SpvOpString, 5); - if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); - break; - } - case OpenCLDebugInfo100DebugTypeBasic: { - CHECK_OPERAND("Name", SpvOpString, 5); - CHECK_OPERAND("Size", SpvOpConstant, 6); - // "Encoding" param is already validated by the binary parsing stage. - break; - } - case OpenCLDebugInfo100DebugTypePointer: - case OpenCLDebugInfo100DebugTypeQualifier: { - auto validate_base_type = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - break; - } - case OpenCLDebugInfo100DebugTypeVector: { - auto validate_base_type = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - - uint32_t component_count = inst->word(6); - if (!component_count || component_count > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be positive " - << "integer less than or equal to 4"; + // Handle any non-common OpenCL insts, then common + if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + OpenCLDebugInfo100Instructions(ext_inst_index) != + OpenCLDebugInfo100DebugModuleINTEL) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case CommonDebugInfoDebugInfoNone: + case CommonDebugInfoDebugNoScope: + break; + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation. We just check the parameters to + // DebugOperation are properly constants for vulkan debug info. + case CommonDebugInfoDebugOperation: { + CHECK_CONST_UINT_OPERAND("Operation", 5); + for (uint32_t i = 6; i < num_words; ++i) { + CHECK_CONST_UINT_OPERAND("Operand", i); + } + break; } - break; - } - case OpenCLDebugInfo100DebugTypeArray: { - auto validate_base_type = ValidateOperandDebugType( - _, "Base Type", inst, 5, ext_inst_name, false); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - for (uint32_t i = 6; i < num_words; ++i) { - bool invalid = false; - auto* component_count = _.FindDef(inst->word(i)); - if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { - // TODO: We need a spec discussion for the bindless array. - if (!component_count->word(3)) { - invalid = true; + case CommonDebugInfoDebugCompilationUnit: { + CHECK_CONST_UINT_OPERAND("Version", 5); + CHECK_CONST_UINT_OPERAND("DWARF Version", 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Language", 8); + break; + } + case CommonDebugInfoDebugSource: { + CHECK_OPERAND("File", SpvOpString, 5); + if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); + break; + } + case CommonDebugInfoDebugTypeBasic: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_OPERAND("Size", SpvOpConstant, 6); + CHECK_CONST_UINT_OPERAND("Encoding", 7); + break; + } + case CommonDebugInfoDebugTypePointer: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Storage Class", 6); + CHECK_CONST_UINT_OPERAND("Flags", 7); + break; + } + case CommonDebugInfoDebugTypeQualifier: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Type Qualifier", 6); + break; + } + case CommonDebugInfoDebugTypeVector: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + CHECK_CONST_UINT_OPERAND("Component Count", 6); + uint32_t component_count = inst->word(6); + if (vulkanDebugInfo) { + uint64_t const_val; + if (!_.GetConstantValUint64(component_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Component Count must be 32-bit integer OpConstant"; } - } else if (component_count->words().size() > 6 && - (OpenCLDebugInfo100Instructions(component_count->word( - 4)) == OpenCLDebugInfo100DebugLocalVariable || - OpenCLDebugInfo100Instructions(component_count->word( - 4)) == OpenCLDebugInfo100DebugGlobalVariable)) { - auto* component_count_type = _.FindDef(component_count->word(6)); - if (component_count_type->words().size() > 7) { - if (OpenCLDebugInfo100Instructions(component_count_type->word( - 4)) != OpenCLDebugInfo100DebugTypeBasic || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( - component_count_type->word(7)) != - OpenCLDebugInfo100Unsigned) { + component_count = const_val & 0xffffffff; + } + + if (!component_count || component_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case CommonDebugInfoDebugTypeArray: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + bool invalid = false; + auto* component_count = _.FindDef(inst->word(i)); + if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { + // TODO: We need a spec discussion for the bindless array. + if (!component_count->word(3)) { invalid = true; - } else { - // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable - // must have Unsigned encoding and 32 or 64 as its size in bits. - Instruction* size_in_bits = - _.FindDef(component_count_type->word(6)); - if (!_.IsIntScalarType(size_in_bits->type_id()) || - (size_in_bits->word(3) != 32 && - size_in_bits->word(3) != 64)) { + } + } else if (component_count->words().size() > 6 && + (CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugGlobalVariable)) { + auto* component_count_type = _.FindDef(component_count->word(6)); + if (component_count_type->words().size() > 7) { + uint32_t encoding = component_count_type->word(7); + if (CommonDebugInfoInstructions(component_count_type->word( + 4)) != CommonDebugInfoDebugTypeBasic || + (vulkanDebugInfo && !IsUint32Constant(_, encoding)) || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo + ? GetUint32Constant(_, encoding) + : encoding) != OpenCLDebugInfo100Unsigned) { invalid = true; + } else { + // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable + // must have Unsigned encoding and 32 or 64 as its size in + // bits. + Instruction* size_in_bits = + _.FindDef(component_count_type->word(6)); + if (!_.IsIntScalarType(size_in_bits->type_id()) || + (size_in_bits->word(3) != 32 && + size_in_bits->word(3) != 64)) { + invalid = true; + } } + } else { + invalid = true; } } else { invalid = true; } + if (invalid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be " + << "OpConstant with a 32- or 64-bits integer scalar type " + "or " + << "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or " + << "64-bits unsigned integer scalar type"; + } + } + break; + } + case CommonDebugInfoDebugTypedef: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_base_type = + ValidateOperandBaseType(_, inst, 6, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case CommonDebugInfoDebugTypeFunction: { + CHECK_CONST_UINT_OPERAND("Flags", 5); + auto* return_type = _.FindDef(inst->word(6)); + // TODO: We need a spec discussion that we have to allow return and + // parameter types of a DebugTypeFunction to have template parameter. + if (return_type->opcode() != SpvOpTypeVoid) { + auto validate_return = ValidateOperandDebugType( + _, "Return Type", inst, 6, ext_inst_name, true); + if (validate_return != SPV_SUCCESS) return validate_return; + } + for (uint32_t word_index = 7; word_index < num_words; ++word_index) { + auto validate_param = ValidateOperandDebugType( + _, "Parameter Types", inst, word_index, ext_inst_name, true); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; + } + case CommonDebugInfoDebugTypeEnum: { + CHECK_OPERAND("Name", SpvOpString, 5); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 6)) { + auto validate_underlying_type = ValidateOperandDebugType( + _, "Underlying Types", inst, 6, ext_inst_name, false); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Size", SpvOpConstant, 11); + auto* size = _.FindDef(inst->word(11)); + if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Size is a " + << "positive integer"; + } + CHECK_CONST_UINT_OPERAND("Flags", 12); + for (uint32_t word_index = 13; word_index + 1 < num_words; + word_index += 2) { + CHECK_OPERAND("Value", SpvOpConstant, word_index); + CHECK_OPERAND("Name", SpvOpString, word_index + 1); + } + break; + } + case CommonDebugInfoDebugTypeComposite: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + CHECK_OPERAND("Size", SpvOpConstant, 12); + } + CHECK_CONST_UINT_OPERAND("Flags", 13); + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeMember || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case CommonDebugInfoDebugTypeMember: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow member types + // to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + // NonSemantic.Shader.DebugInfo doesn't have the Parent operand + if (vulkanDebugInfo) { + CHECK_OPERAND("Offset", SpvOpConstant, 10); + CHECK_OPERAND("Size", SpvOpConstant, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + if (num_words == 14) CHECK_OPERAND("Value", SpvOpConstant, 13); } else { - invalid = true; - } - if (invalid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be " - << "OpConstant with a 32- or 64-bits integer scalar type or " - << "DebugGlobalVariable or DebugLocalVariable with a 32- or " - << "64-bits unsigned integer scalar type"; + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, + 10); + CHECK_OPERAND("Offset", SpvOpConstant, 11); + CHECK_OPERAND("Size", SpvOpConstant, 12); + CHECK_CONST_UINT_OPERAND("Flags", 13); + if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); } + break; } - break; - } - case OpenCLDebugInfo100DebugTypedef: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_base_type = - ValidateOperandBaseType(_, inst, 6, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - break; - } - case OpenCLDebugInfo100DebugTypeFunction: { - auto* return_type = _.FindDef(inst->word(6)); - // TODO: We need a spec discussion that we have to allow return and - // parameter types of a DebugTypeFunction to have template parameter. - if (return_type->opcode() != SpvOpTypeVoid) { - auto validate_return = ValidateOperandDebugType( - _, "Return Type", inst, 6, ext_inst_name, true); - if (validate_return != SPV_SUCCESS) return validate_return; - } - for (uint32_t word_index = 7; word_index < num_words; ++word_index) { - auto validate_param = ValidateOperandDebugType( - _, "Parameter Types", inst, word_index, ext_inst_name, true); - if (validate_param != SPV_SUCCESS) return validate_param; - } - break; - } - case OpenCLDebugInfo100DebugTypeEnum: { - CHECK_OPERAND("Name", SpvOpString, 5); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 6)) { - auto validate_underlying_type = ValidateOperandDebugType( - _, "Underlying Types", inst, 6, ext_inst_name, false); - if (validate_underlying_type != SPV_SUCCESS) - return validate_underlying_type; - } - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Size", SpvOpConstant, 11); - auto* size = _.FindDef(inst->word(11)); - if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Size is a " - << "positive integer"; - } - for (uint32_t word_index = 13; word_index + 1 < num_words; - word_index += 2) { - CHECK_OPERAND("Value", SpvOpConstant, word_index); - CHECK_OPERAND("Name", SpvOpString, word_index + 1); - } - break; - } - case OpenCLDebugInfo100DebugTypeComposite: { - CHECK_OPERAND("Name", SpvOpString, 5); - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 12)) { - CHECK_OPERAND("Size", SpvOpConstant, 12); - } - for (uint32_t word_index = 14; word_index < num_words; ++word_index) { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugTypeMember || - dbg_inst == OpenCLDebugInfo100DebugFunction || - dbg_inst == OpenCLDebugInfo100DebugTypeInheritance; - }, - inst, word_index)) { + case CommonDebugInfoDebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5); + auto* debug_inst = _.FindDef(inst->word(5)); + auto composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " - << "expected operand Members " - << "must be DebugTypeMember, DebugFunction, or " - "DebugTypeInheritance"; + << "expected operand Child must be class or struct debug " + "type"; } - } - break; - } - case OpenCLDebugInfo100DebugTypeMember: { - CHECK_OPERAND("Name", SpvOpString, 5); - // TODO: We need a spec discussion that we have to allow member types - // to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10); - CHECK_OPERAND("Offset", SpvOpConstant, 11); - CHECK_OPERAND("Size", SpvOpConstant, 12); - if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); - break; - } - case OpenCLDebugInfo100DebugTypeInheritance: { - CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5); - auto* debug_inst = _.FindDef(inst->word(5)); - auto composite_type = - OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); - if (composite_type != OpenCLDebugInfo100Class && - composite_type != OpenCLDebugInfo100Structure) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Child must be class or struct debug type"; - } - CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6); - debug_inst = _.FindDef(inst->word(6)); - composite_type = - OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); - if (composite_type != OpenCLDebugInfo100Class && - composite_type != OpenCLDebugInfo100Structure) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Parent must be class or struct debug " - "type"; - } - CHECK_OPERAND("Offset", SpvOpConstant, 7); - CHECK_OPERAND("Size", SpvOpConstant, 8); - break; - } - case OpenCLDebugInfo100DebugFunction: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 14)) { - CHECK_OPERAND("Function", SpvOpFunction, 14); - } - if (num_words == 16) { - CHECK_DEBUG_OPERAND("Declaration", - OpenCLDebugInfo100DebugFunctionDeclaration, 15); - } - break; - } - case OpenCLDebugInfo100DebugFunctionDeclaration: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - break; - } - case OpenCLDebugInfo100DebugLexicalBlock: { - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9); - break; - } - case OpenCLDebugInfo100DebugScope: { - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 7) { - CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt, - 6); - } - break; - } - case OpenCLDebugInfo100DebugLocalVariable: { - CHECK_OPERAND("Name", SpvOpString, 5); - // TODO: We need a spec discussion that we have to allow local variable - // types to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - break; - } - case OpenCLDebugInfo100DebugDeclare: { - CHECK_DEBUG_OPERAND("Local Variable", - OpenCLDebugInfo100DebugLocalVariable, 5); - auto* operand = _.FindDef(inst->word(6)); - if (operand->opcode() != SpvOpVariable && - operand->opcode() != SpvOpFunctionParameter) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Variable must be a result id of " - "OpVariable or OpFunctionParameter"; - } - - CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); - break; - } - case OpenCLDebugInfo100DebugExpression: { - for (uint32_t word_index = 5; word_index < num_words; ++word_index) { - CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation, - word_index); - } - break; - } - case OpenCLDebugInfo100DebugTypeTemplate: { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugTypeComposite || - dbg_inst == OpenCLDebugInfo100DebugFunction; - }, - inst, 5)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Target must be DebugTypeComposite " - << "or DebugFunction"; - } - for (uint32_t word_index = 6; word_index < num_words; ++word_index) { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateParameter || - dbg_inst == - OpenCLDebugInfo100DebugTypeTemplateTemplateParameter; - }, - inst, word_index)) { + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6); + debug_inst = _.FindDef(inst->word(6)); + composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " - << "expected operand Parameters must be " - << "DebugTypeTemplateParameter or " - << "DebugTypeTemplateTemplateParameter"; + << "expected operand Parent must be class or struct debug " + "type"; } + CHECK_OPERAND("Offset", SpvOpConstant, 7); + CHECK_OPERAND("Size", SpvOpConstant, 8); + CHECK_CONST_UINT_OPERAND("Flags", 9); + break; } - break; - } - case OpenCLDebugInfo100DebugTypeTemplateParameter: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_actual_type = ValidateOperandDebugType( - _, "Actual Type", inst, 6, ext_inst_name, false); - if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 7)) { - CHECK_OPERAND("Value", SpvOpConstant, 7); + case CommonDebugInfoDebugFunction: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + CHECK_CONST_UINT_OPERAND("Scope Line", 13); + // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the + // OpFunction + if (vulkanDebugInfo) { + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 14); + } + } else { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", SpvOpFunction, 14); + } + if (num_words == 16) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 15); + } + } + break; } - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8); - break; - } - case OpenCLDebugInfo100DebugGlobalVariable: { - CHECK_OPERAND("Name", SpvOpString, 5); - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - CHECK_OPERAND("Linkage Name", SpvOpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](OpenCLDebugInfo100Instructions dbg_inst) { - return dbg_inst == OpenCLDebugInfo100DebugInfoNone; - }, - inst, 12)) { - auto* operand = _.FindDef(inst->word(12)); + case CommonDebugInfoDebugFunctionDeclaration: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + break; + } + case CommonDebugInfoDebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line", 6); + CHECK_CONST_UINT_OPERAND("Column", 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9); + break; + } + case CommonDebugInfoDebugScope: { + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 7) { + CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6); + } + break; + } + case CommonDebugInfoDebugLocalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow local + // variable types to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_CONST_UINT_OPERAND("Flags", 11); + if (num_words == 13) { + CHECK_CONST_UINT_OPERAND("ArgNumber", 12); + } + break; + } + case CommonDebugInfoDebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + auto* operand = _.FindDef(inst->word(6)); if (operand->opcode() != SpvOpVariable && - operand->opcode() != SpvOpConstant) { + operand->opcode() != SpvOpFunctionParameter) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << ext_inst_name() << ": " << "expected operand Variable must be a result id of " - "OpVariable or OpConstant or DebugInfoNone"; + "OpVariable or OpFunctionParameter"; } - } - if (num_words == 15) { - CHECK_DEBUG_OPERAND("Static Member Declaration", - OpenCLDebugInfo100DebugTypeMember, 14); - } - break; - } - case OpenCLDebugInfo100DebugInlinedAt: { - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 8) { - CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7); - } - break; - } - case OpenCLDebugInfo100DebugValue: { - CHECK_DEBUG_OPERAND("Local Variable", - OpenCLDebugInfo100DebugLocalVariable, 5); - CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); - for (uint32_t word_index = 8; word_index < num_words; ++word_index) { - // TODO: The following code simply checks if it is a const int scalar - // or a DebugLocalVariable or DebugGlobalVariable, but we have to - // check it using the same validation for Indexes of OpAccessChain. - if (!IsConstWithIntScalarType(_, inst, word_index) && - !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + + if (vulkanDebugInfo) { + for (uint32_t word_index = 8; word_index < num_words; + ++word_index) { + auto index_inst = _.FindDef(inst->word(word_index)); + auto type_id = index_inst != nullptr ? index_inst->type_id() : 0; + if (type_id == 0 || !IsIntScalar(_, type_id, false, false)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected index must be scalar integer"; + } + } + break; + } + case CommonDebugInfoDebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation, + word_index); + } + break; + } + case CommonDebugInfoDebugTypeTemplate: { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeComposite || + dbg_inst == CommonDebugInfoDebugFunction; + }, + inst, 5)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Indexes is " - << "OpConstant, DebugGlobalVariable, or " - << "type is OpConstant with an integer scalar type"; + << ext_inst_name() << ": " + << "expected operand Target must be DebugTypeComposite " + << "or DebugFunction"; } + for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == + CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == + CommonDebugInfoDebugTypeTemplateTemplateParameter; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parameters must be " + << "DebugTypeTemplateParameter or " + << "DebugTypeTemplateTemplateParameter"; + } + } + break; } - break; - } + case CommonDebugInfoDebugTypeTemplateParameter: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_actual_type = ValidateOperandDebugType( + _, "Actual Type", inst, 6, ext_inst_name, false); + if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 7)) { + CHECK_OPERAND("Value", SpvOpConstant, 7); + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8); + CHECK_CONST_UINT_OPERAND("Line", 9); + CHECK_CONST_UINT_OPERAND("Column", 10); + break; + } + case CommonDebugInfoDebugGlobalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + auto* operand = _.FindDef(inst->word(12)); + if (operand->opcode() != SpvOpVariable && + operand->opcode() != SpvOpConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpConstant or DebugInfoNone"; + } + } + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Static Member Declaration", + CommonDebugInfoDebugTypeMember, 14); + } + break; + } + case CommonDebugInfoDebugInlinedAt: { + CHECK_CONST_UINT_OPERAND("Line", 5); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 8) { + CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7); + } + break; + } + case CommonDebugInfoDebugValue: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); - // TODO: Add validation rules for remaining cases as well. - case OpenCLDebugInfo100DebugTypePtrToMember: - case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter: - case OpenCLDebugInfo100DebugTypeTemplateParameterPack: - case OpenCLDebugInfo100DebugLexicalBlockDiscriminator: - case OpenCLDebugInfo100DebugInlinedVariable: - case OpenCLDebugInfo100DebugMacroDef: - case OpenCLDebugInfo100DebugMacroUndef: - case OpenCLDebugInfo100DebugImportedEntity: - case OpenCLDebugInfo100DebugModuleINTEL: - break; - case OpenCLDebugInfo100InstructionsMax: - assert(0); - break; + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + // TODO: The following code simply checks if it is a const int + // scalar or a DebugLocalVariable or DebugGlobalVariable, but we + // have to check it using the same validation for Indexes of + // OpAccessChain. + if (!IsConstWithIntScalarType(_, inst, word_index) && + !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Indexes is " + << "OpConstant, DebugGlobalVariable, or " + << "type is OpConstant with an integer scalar type"; + } + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case CommonDebugInfoDebugTypePtrToMember: + case CommonDebugInfoDebugTypeTemplateTemplateParameter: + case CommonDebugInfoDebugTypeTemplateParameterPack: + case CommonDebugInfoDebugLexicalBlockDiscriminator: + case CommonDebugInfoDebugInlinedVariable: + case CommonDebugInfoDebugMacroDef: + case CommonDebugInfoDebugMacroUndef: + case CommonDebugInfoDebugImportedEntity: + break; + case CommonDebugInfoInstructionsMax: + assert(0); + break; + } } } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { auto import_inst = _.FindDef(inst->GetOperandAs(2)); diff --git a/third_party/spirv-tools/source/val/validate_image.cpp b/third_party/spirv-tools/source/val/validate_image.cpp index e5968d06e7..64f6ba7bde 100644 --- a/third_party/spirv-tools/source/val/validate_image.cpp +++ b/third_party/spirv-tools/source/val/validate_image.cpp @@ -66,6 +66,11 @@ bool CheckAllImageOperandsHandled() { case SpvImageOperandsVolatileTexelKHRMask: case SpvImageOperandsSignExtendMask: case SpvImageOperandsZeroExtendMask: + // TODO(jaebaek): Move this line properly after handling image offsets + // operand. This line temporarily fixes CI failure that + // blocks other PRs. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 + case SpvImageOperandsOffsetsMask: return true; } return false; @@ -281,13 +286,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // the module to be invalid. if (mask == 0) return SPV_SUCCESS; - if (spvtools::utils::CountSetBits( - mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask | - SpvImageOperandsConstOffsetsMask)) > 1) { + if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask | + SpvImageOperandsConstOffsetMask | + SpvImageOperandsConstOffsetsMask | + SpvImageOperandsOffsetsMask)) > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4662) - << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used " - << "together"; + << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together"; } const bool is_implicit_lod = IsImplicitLod(opcode); @@ -620,6 +626,10 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // setup. } + if (mask & SpvImageOperandsOffsetsMask) { + // TODO: add validation + } + return SPV_SUCCESS; } @@ -2058,11 +2068,13 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) { std::string* message) { const auto* models = state.GetExecutionModels(entry_point->id()); const auto* modes = state.GetExecutionModes(entry_point->id()); - if (models->find(SpvExecutionModelGLCompute) != models->end() && - modes->find(SpvExecutionModeDerivativeGroupLinearNV) == - modes->end() && - modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == - modes->end()) { + if (models && + models->find(SpvExecutionModelGLCompute) != models->end() && + (!modes || + (modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()))) { if (message) { *message = std::string( diff --git a/third_party/spirv-tools/source/val/validate_instruction.cpp b/third_party/spirv-tools/source/val/validate_instruction.cpp index 9d395fb46b..dad98673b1 100644 --- a/third_party/spirv-tools/source/val/validate_instruction.cpp +++ b/third_party/spirv-tools/source/val/validate_instruction.cpp @@ -318,10 +318,9 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { if (module_version < min_version) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) - << spvOpcodeString(opcode) << " requires " - << spvTargetEnvDescription( - static_cast(min_version)) - << " at minimum."; + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum."; } } else if (!_.HasAnyOfExtensions(exts)) { // Otherwise, we only error out when no enabling extensions are diff --git a/third_party/spirv-tools/source/val/validate_interfaces.cpp b/third_party/spirv-tools/source/val/validate_interfaces.cpp index d16d48e269..7ccb6371bb 100644 --- a/third_party/spirv-tools/source/val/validate_interfaces.cpp +++ b/third_party/spirv-tools/source/val/validate_interfaces.cpp @@ -27,6 +27,10 @@ namespace spvtools { namespace val { namespace { +// Limit the number of checked locations to 4096. Multiplied by 4 to represent +// all the components. This limit is set to be well beyond practical use cases. +const uint32_t kMaxLocations = 4096 * 4; + // Returns true if \c inst is an input or output variable. bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) { if (is_spv_1_4) { @@ -195,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) { NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); num_components *= type->GetOperandAs(2); break; + case SpvOpTypeArray: + // Skip the array. + return NumConsumedComponents(_, + _.FindDef(type->GetOperandAs(1))); default: // This is an error that is validated elsewhere. break; @@ -347,6 +355,11 @@ spv_result_t GetLocationsForVariable( uint32_t num_components = NumConsumedComponents(_, sub_type); uint32_t array_location = location + (num_locations * array_idx); uint32_t start = array_location * 4; + if (kMaxLocations <= start) { + // Too many locations, give up. + break; + } + uint32_t end = (array_location + num_locations) * 4; if (num_components != 0) { start += component; @@ -416,17 +429,41 @@ spv_result_t GetLocationsForVariable( } uint32_t start = location * 4; - uint32_t end = (location + num_locations) * 4; - if (num_components != 0) { - start += component; - end = location * 4 + component + num_components; + if (kMaxLocations <= start) { + // Too many locations, give up. + continue; } - for (uint32_t l = start; l < end; ++l) { - if (!locations->insert(l).second) { - return _.diag(SPV_ERROR_INVALID_DATA, entry_point) - << "Entry-point has conflicting " << storage_class - << " location assignment at location " << l / 4 - << ", component " << l % 4; + + if (member->opcode() == SpvOpTypeArray && num_components >= 1 && + num_components < 4) { + // When an array has an element that takes less than a location in + // size, calculate the used locations in a strided manner. + for (uint32_t l = location; l < num_locations + location; ++l) { + for (uint32_t c = component; c < component + num_components; ++c) { + uint32_t check = 4 * l + c; + if (!locations->insert(check).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l + << ", component " << c; + } + } + } + } else { + // TODO: There is a hole here is the member is an array of 3- or + // 4-element vectors of 64-bit types. + uint32_t end = (location + num_locations) * 4; + if (num_components != 0) { + start += component; + end = location * 4 + component + num_components; + } + for (uint32_t l = start; l < end; ++l) { + if (!locations->insert(l).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l / 4 + << ", component " << l % 4; + } } } } @@ -454,6 +491,7 @@ spv_result_t ValidateLocations(ValidationState_t& _, std::unordered_set input_locations; std::unordered_set output_locations_index0; std::unordered_set output_locations_index1; + std::unordered_set seen; for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { auto interface_id = entry_point->GetOperandAs(i); auto interface_var = _.FindDef(interface_id); @@ -462,6 +500,11 @@ spv_result_t ValidateLocations(ValidationState_t& _, storage_class != SpvStorageClassOutput) { continue; } + if (!seen.insert(interface_id).second) { + // Pre-1.4 an interface variable could be listed multiple times in an + // entry point. Validation for 1.4 or later is done elsewhere. + continue; + } auto locations = (storage_class == SpvStorageClassInput) ? &input_locations diff --git a/third_party/spirv-tools/source/val/validate_layout.cpp b/third_party/spirv-tools/source/val/validate_layout.cpp index b53f991e87..d5823219d9 100644 --- a/third_party/spirv-tools/source/val/validate_layout.cpp +++ b/third_party/spirv-tools/source/val/validate_layout.cpp @@ -17,6 +17,7 @@ #include #include "DebugInfo.h" +#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/diagnostic.h" #include "source/opcode.h" @@ -37,17 +38,7 @@ 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 if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { const uint32_t ext_inst_index = inst->word(4); bool local_debug_info = false; if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { @@ -59,6 +50,20 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, ext_inst_key == OpenCLDebugInfo100DebugValue) { local_debug_info = true; } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + ext_inst_key == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } } else { const DebugInfoInstructions ext_inst_key = DebugInfoInstructions(ext_inst_index); @@ -94,6 +99,16 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _, << "declarations)"; } } + } else 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) { @@ -230,20 +245,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, 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 if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { const uint32_t ext_inst_index = inst->word(4); bool local_debug_info = false; if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { @@ -255,6 +257,20 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, ext_inst_key == OpenCLDebugInfo100DebugValue) { local_debug_info = true; } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + ext_inst_key == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } } else { const DebugInfoInstructions ext_inst_key = DebugInfoInstructions(ext_inst_index); @@ -290,6 +306,19 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, << "declarations)"; } } + } else 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) { diff --git a/third_party/spirv-tools/source/val/validate_memory.cpp b/third_party/spirv-tools/source/val/validate_memory.cpp index a4bc0faba8..a7b0f82d5d 100644 --- a/third_party/spirv-tools/source/val/validate_memory.cpp +++ b/third_party/spirv-tools/source/val/validate_memory.cpp @@ -893,6 +893,12 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { << "'s type."; } + if (!_.options()->before_hlsl_legalization && + _.ContainsRuntimeArray(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot load a runtime-sized array"; + } + if (auto error = CheckMemoryAccess(_, inst, 3)) return error; if (_.HasCapability(SpvCapabilityShader) && diff --git a/third_party/spirv-tools/source/val/validate_mode_setting.cpp b/third_party/spirv-tools/source/val/validate_mode_setting.cpp index 79f82d8d76..9635268708 100644 --- a/third_party/spirv-tools/source/val/validate_mode_setting.cpp +++ b/third_party/spirv-tools/source/val/validate_mode_setting.cpp @@ -225,14 +225,21 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { } } } + if (i.opcode() == SpvOpExecutionModeId) { + const auto mode = i.GetOperandAs(1); + if (mode == SpvExecutionModeLocalSizeId) { + ok = true; + break; + } + } } if (!ok) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4683) + << _.VkErrorID(6426) << "In the Vulkan environment, GLCompute execution model " - "entry points require either the LocalSize execution " - "mode or an object decorated with WorkgroupSize must be " - "specified."; + "entry points require either the LocalSize or " + "LocalSizeId execution mode or an object decorated with " + "WorkgroupSize must be specified."; } } break; @@ -429,6 +436,10 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, break; case SpvExecutionModeLocalSize: case SpvExecutionModeLocalSizeId: + if (mode == SpvExecutionModeLocalSizeId && !_.IsLocalSizeIdAllowed()) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "LocalSizeId mode is not allowed by the current environment."; + if (!std::all_of(models->begin(), models->end(), [&_](const SpvExecutionModel& model) { switch (model) { diff --git a/third_party/spirv-tools/source/val/validate_type.cpp b/third_party/spirv-tools/source/val/validate_type.cpp index 612fc5c2ce..4376b52c6d 100644 --- a/third_party/spirv-tools/source/val/validate_type.cpp +++ b/third_party/spirv-tools/source/val/validate_type.cpp @@ -596,7 +596,7 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, if (!cols || !_.IsIntScalarType(cols->type_id()) || !spvOpcodeIsConstant(cols->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Cols '" << _.getIdName(rows_id) + << "OpTypeCooperativeMatrixNV Cols '" << _.getIdName(cols_id) << "' is not a constant instruction with scalar integer type."; } diff --git a/third_party/spirv-tools/source/val/validation_state.cpp b/third_party/spirv-tools/source/val/validation_state.cpp index 52821636bf..8d1a0d3f4e 100644 --- a/third_party/spirv-tools/source/val/validation_state.cpp +++ b/third_party/spirv-tools/source/val/validation_state.cpp @@ -175,6 +175,9 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } } + // LocalSizeId is always allowed in non-Vulkan environments. + features_.env_allow_localsizeid = !spvIsVulkanEnv(env); + // Only attempt to count if we have words, otherwise let the other validation // fail and generate an error. if (num_words > 0) { @@ -729,19 +732,19 @@ uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { bool ValidationState_t::IsVoidType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeVoid; + return inst && inst->opcode() == SpvOpTypeVoid; } bool ValidationState_t::IsFloatScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeFloat; + return inst && inst->opcode() == SpvOpTypeFloat; } bool ValidationState_t::IsFloatVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsFloatScalarType(GetComponentType(id)); @@ -752,7 +755,9 @@ bool ValidationState_t::IsFloatVectorType(uint32_t id) const { bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeFloat) { return true; @@ -767,13 +772,14 @@ bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt; + return inst && inst->opcode() == SpvOpTypeInt; } bool ValidationState_t::IsIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsIntScalarType(GetComponentType(id)); @@ -784,7 +790,9 @@ bool ValidationState_t::IsIntVectorType(uint32_t id) const { bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeInt) { return true; @@ -799,13 +807,14 @@ bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; + return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; } bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsUnsignedIntScalarType(GetComponentType(id)); @@ -816,13 +825,14 @@ bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; + return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; } bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsSignedIntScalarType(GetComponentType(id)); @@ -833,13 +843,14 @@ bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { bool ValidationState_t::IsBoolScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeBool; + return inst && inst->opcode() == SpvOpTypeBool; } bool ValidationState_t::IsBoolVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeVector) { return IsBoolScalarType(GetComponentType(id)); @@ -850,7 +861,9 @@ bool ValidationState_t::IsBoolVectorType(uint32_t id) const { bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeBool) { return true; @@ -865,7 +878,9 @@ bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); + if (!inst) { + return false; + } if (inst->opcode() == SpvOpTypeMatrix) { return IsFloatScalarType(GetComponentType(id)); @@ -920,8 +935,7 @@ bool ValidationState_t::GetStructMemberTypes( bool ValidationState_t::IsPointerType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypePointer; + return inst && inst->opcode() == SpvOpTypePointer; } bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, @@ -939,8 +953,7 @@ bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); - assert(inst); - return inst->opcode() == SpvOpTypeCooperativeMatrixNV; + return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV; } bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { @@ -1262,16 +1275,13 @@ const Instruction* ValidationState_t::TracePointer( return base_ptr; } -bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, - uint32_t width) const { - if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; - +bool ValidationState_t::ContainsType( + uint32_t id, const std::function& f, + bool traverse_all_types) const { const auto inst = FindDef(id); if (!inst) return false; - if (inst->opcode() == type) { - return inst->GetOperandAs(1u) == width; - } + if (f(inst)) return true; switch (inst->opcode()) { case SpvOpTypeArray: @@ -1281,24 +1291,45 @@ bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, case SpvOpTypeImage: case SpvOpTypeSampledImage: case SpvOpTypeCooperativeMatrixNV: - return ContainsSizedIntOrFloatType(inst->GetOperandAs(1u), type, - width); + return ContainsType(inst->GetOperandAs(1u), f, + traverse_all_types); case SpvOpTypePointer: if (IsForwardPointer(id)) return false; - return ContainsSizedIntOrFloatType(inst->GetOperandAs(2u), type, - width); - case SpvOpTypeFunction: - case SpvOpTypeStruct: { - for (uint32_t i = 1; i < inst->operands().size(); ++i) { - if (ContainsSizedIntOrFloatType(inst->GetOperandAs(i), type, - width)) - return true; + if (traverse_all_types) { + return ContainsType(inst->GetOperandAs(2u), f, + traverse_all_types); } - return false; - } + break; + case SpvOpTypeFunction: + case SpvOpTypeStruct: + if (inst->opcode() == SpvOpTypeFunction && !traverse_all_types) { + return false; + } + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + if (ContainsType(inst->GetOperandAs(i), f, + traverse_all_types)) { + return true; + } + } + break; default: - return false; + break; } + + return false; +} + +bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, + uint32_t width) const { + if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; + + const auto f = [type, width](const Instruction* inst) { + if (inst->opcode() == type) { + return inst->GetOperandAs(1u) == width; + } + return false; + }; + return ContainsType(id, f); } bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { @@ -1313,6 +1344,13 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { return false; } +bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const { + const auto f = [](const Instruction* inst) { + return inst->opcode() == SpvOpTypeRuntimeArray; + }; + return ContainsType(id, f, /* traverse_all_types = */ false); +} + bool ValidationState_t::IsValidStorageClass( SpvStorageClass storage_class) const { if (spvIsVulkanEnv(context()->target_env)) { @@ -1801,14 +1839,16 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-None-04667); case 4669: return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); + case 4670: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04670); case 4675: return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); case 4677: return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); case 4682: return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); - case 4683: - return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683); + case 6426: + return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683 case 4685: return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); case 4686: diff --git a/third_party/spirv-tools/source/val/validation_state.h b/third_party/spirv-tools/source/val/validation_state.h index 57634bf4a0..2ddfa4a938 100644 --- a/third_party/spirv-tools/source/val/validation_state.h +++ b/third_party/spirv-tools/source/val/validation_state.h @@ -90,23 +90,6 @@ class ValidationState_t { // conversion opcodes bool use_int8_type = false; - // Use scalar block layout. See VK_EXT_scalar_block_layout: - // Defines scalar alignment: - // - scalar alignment equals the scalar size in bytes - // - array alignment is same as its element alignment - // - array alignment is max alignment of any of its members - // - vector alignment is same as component alignment - // - matrix alignment is same as component alignment - // For struct in Uniform, StorageBuffer, PushConstant: - // - Offset of a member is multiple of scalar alignment of that member - // - ArrayStride and MatrixStride are multiples of scalar alignment - // Members need not be listed in offset order - bool scalar_block_layout = false; - - // Use scalar block layout (as defined above) for Workgroup block - // variables. See VK_KHR_workgroup_memory_explicit_layout. - bool workgroup_scalar_block_layout = false; - // SPIR-V 1.4 allows us to select between any two composite values // of the same type. bool select_between_composites = false; @@ -121,6 +104,9 @@ class ValidationState_t { // SPIR-V 1.4 allows Function and Private variables to be NonWritable bool nonwritable_var_in_function_or_private = false; + + // Whether LocalSizeId execution mode is allowed by the environment. + bool env_allow_localsizeid = false; }; ValidationState_t(const spv_const_context context, @@ -493,6 +479,12 @@ class ValidationState_t { return features_.env_relaxed_block_layout || options()->relax_block_layout; } + // Returns true if allowing localsizeid, either because the environment always + // allows it, or because it is enabled from the command-line. + bool IsLocalSizeIdAllowed() const { + return features_.env_allow_localsizeid || options()->allow_localsizeid; + } + /// Sets the struct nesting depth for a given struct ID void set_struct_nesting_depth(uint32_t id, uint32_t depth) { struct_nesting_depth_[id] = depth; @@ -595,6 +587,17 @@ class ValidationState_t { // 16-bit float that is not generally enabled for use. bool ContainsLimitedUseIntOrFloatType(uint32_t id) const; + // Returns true if |id| is a type that contains a runtime-sized array. + // Does not consider a pointers as contains the array. + bool ContainsRuntimeArray(uint32_t id) const; + + // Generic type traversal. + // Only traverse pointers and functions if |traverse_all_types| is true. + // Recursively tests |f| against the type hierarchy headed by |id|. + bool ContainsType(uint32_t id, + const std::function& f, + bool traverse_all_types = true) const; + // Gets value from OpConstant and OpSpecConstant as uint64. // Returns false on failure (no instruction, wrong instruction, not int). bool GetConstantValUint64(uint32_t id, uint64_t* val) const; diff --git a/third_party/spirv-tools/source/wasm/README.md b/third_party/spirv-tools/source/wasm/README.md new file mode 100644 index 0000000000..aca0f70cee --- /dev/null +++ b/third_party/spirv-tools/source/wasm/README.md @@ -0,0 +1,43 @@ +# SPIRV-Tools + +Wasm (WebAssembly) build of https://github.com/KhronosGroup/SPIRV-Tools + +## Usage + +```js +const spirvTools = require("spirv-tools"); + +const test = async () => { + // Load the library + const spv = await spirvTools(); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); +``` diff --git a/third_party/spirv-tools/source/wasm/build.sh b/third_party/spirv-tools/source/wasm/build.sh new file mode 100755 index 0000000000..f02ae525c3 --- /dev/null +++ b/third_party/spirv-tools/source/wasm/build.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Copyright (c) 2020 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. + +set -e + +NUM_CORES=$(nproc) +echo "Detected $NUM_CORES cores for building" + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +VERSION=$(sed -n '0,/^v20/ s/^v\(20[0-9.]*\).*/\1/p' $DIR/../../CHANGES).${GITHUB_RUN_NUMBER:-0} +echo "Version: $VERSION" + +build() { + type=$1 + shift + args=$@ + mkdir -p build/$type + pushd build/$type + echo $args + emcmake cmake \ + -DCMAKE_BUILD_TYPE=Release \ + $args \ + ../.. + emmake make -j $(( $NUM_CORES )) SPIRV-Tools-static + + echo Building js interface + emcc \ + --bind \ + -I../../include \ + -std=c++11 \ + ../../source/wasm/spirv-tools.cpp \ + source/libSPIRV-Tools.a \ + -o spirv-tools.js \ + -s MODULARIZE \ + -Oz + + popd + mkdir -p out/$type + + # copy other js files + cp source/wasm/spirv-tools.d.ts out/$type/ + sed -e 's/\("version"\s*:\s*\).*/\1"'$VERSION'",/' source/wasm/package.json > out/$type/package.json + cp source/wasm/README.md out/$type/ + cp LICENSE out/$type/ + + cp build/$type/spirv-tools.js out/$type/ + gzip -9 -k -f out/$type/spirv-tools.js + if [ -e build/$type/spirv-tools.wasm ] ; then + cp build/$type/spirv-tools.wasm out/$type/ + gzip -9 -k -f out/$type/spirv-tools.wasm + fi +} + +if [ ! -d external/spirv-headers ] ; then + echo "Fetching SPIRV-headers" + git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers +fi + +echo Building ${BASH_REMATCH[1]} +build web\ + -DSPIRV_COLOR_TERMINAL=OFF\ + -DSPIRV_SKIP_TESTS=ON\ + -DSPIRV_SKIP_EXECUTABLES=ON + +wc -c out/*/* diff --git a/third_party/spirv-tools/source/wasm/package.json b/third_party/spirv-tools/source/wasm/package.json new file mode 100644 index 0000000000..7827353810 --- /dev/null +++ b/third_party/spirv-tools/source/wasm/package.json @@ -0,0 +1,17 @@ +{ + "name": "spirv-tools", + "version": "VERSION", + "license": "Apache-2.0", + "main": "spirv-tools", + "types": "spirv-tools.d.ts", + "files": [ + "*.wasm", + "*.js", + "*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/KhronosGroup/SPIRV-Tools" + }, + "homepage": "https://github.com/KhronosGroup/SPIRV-Tools" +} diff --git a/third_party/spirv-tools/source/wasm/spirv-tools.cpp b/third_party/spirv-tools/source/wasm/spirv-tools.cpp new file mode 100644 index 0000000000..90407f328a --- /dev/null +++ b/third_party/spirv-tools/source/wasm/spirv-tools.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.hpp" + +#include +#include +#include + +#include +#include +using namespace emscripten; + +void print_msg_to_stderr (spv_message_level_t, const char*, + const spv_position_t&, const char* m) { + std::cerr << "error: " << m << std::endl; +}; + +std::string dis(std::string const& buffer, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + const uint32_t* ptr = reinterpret_cast(buffer.data()); + spirv.assign(ptr, ptr + buffer.size() / 4); + std::string disassembly; + if (!core.Disassemble(spirv, &disassembly, options)) return "Error"; + return disassembly; +} + +emscripten::val as(std::string const& source, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + if (!core.Assemble(source, &spirv, options)) spirv.clear(); + const uint8_t* ptr = reinterpret_cast(spirv.data()); + return emscripten::val(emscripten::typed_memory_view(spirv.size() * 4, + ptr)); +} + +EMSCRIPTEN_BINDINGS(my_module) { + function("dis", &dis); + function("as", &as); + + constant("SPV_ENV_UNIVERSAL_1_0", static_cast(SPV_ENV_UNIVERSAL_1_0)); + constant("SPV_ENV_VULKAN_1_0", static_cast(SPV_ENV_VULKAN_1_0)); + constant("SPV_ENV_UNIVERSAL_1_1", static_cast(SPV_ENV_UNIVERSAL_1_1)); + constant("SPV_ENV_OPENCL_2_1", static_cast(SPV_ENV_OPENCL_2_1)); + constant("SPV_ENV_OPENCL_2_2", static_cast(SPV_ENV_OPENCL_2_2)); + constant("SPV_ENV_OPENGL_4_0", static_cast(SPV_ENV_OPENGL_4_0)); + constant("SPV_ENV_OPENGL_4_1", static_cast(SPV_ENV_OPENGL_4_1)); + constant("SPV_ENV_OPENGL_4_2", static_cast(SPV_ENV_OPENGL_4_2)); + constant("SPV_ENV_OPENGL_4_3", static_cast(SPV_ENV_OPENGL_4_3)); + constant("SPV_ENV_OPENGL_4_5", static_cast(SPV_ENV_OPENGL_4_5)); + constant("SPV_ENV_UNIVERSAL_1_2", static_cast(SPV_ENV_UNIVERSAL_1_2)); + constant("SPV_ENV_OPENCL_1_2", static_cast(SPV_ENV_OPENCL_1_2)); + constant("SPV_ENV_OPENCL_EMBEDDED_1_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_1_2)); + constant("SPV_ENV_OPENCL_2_0", static_cast(SPV_ENV_OPENCL_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_0", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_1", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_1)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_2)); + constant("SPV_ENV_UNIVERSAL_1_3", static_cast(SPV_ENV_UNIVERSAL_1_3)); + constant("SPV_ENV_VULKAN_1_1", static_cast(SPV_ENV_VULKAN_1_1)); + constant("SPV_ENV_WEBGPU_0", static_cast(SPV_ENV_WEBGPU_0)); + constant("SPV_ENV_UNIVERSAL_1_4", static_cast(SPV_ENV_UNIVERSAL_1_4)); + constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + constant("SPV_ENV_UNIVERSAL_1_5", static_cast(SPV_ENV_UNIVERSAL_1_5)); + constant("SPV_ENV_VULKAN_1_2", static_cast(SPV_ENV_VULKAN_1_2)); + + + constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast(SPV_BINARY_TO_TEXT_OPTION_NONE)); + constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast(SPV_BINARY_TO_TEXT_OPTION_PRINT)); + constant("SPV_BINARY_TO_TEXT_OPTION_COLOR", static_cast(SPV_BINARY_TO_TEXT_OPTION_COLOR)); + constant("SPV_BINARY_TO_TEXT_OPTION_INDENT", static_cast(SPV_BINARY_TO_TEXT_OPTION_INDENT)); + constant("SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET", static_cast(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET)); + constant("SPV_BINARY_TO_TEXT_OPTION_NO_HEADER", static_cast(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)); + constant("SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES", static_cast(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)); + + constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast(SPV_TEXT_TO_BINARY_OPTION_NONE)); + constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)); +} \ No newline at end of file diff --git a/third_party/spirv-tools/source/wasm/spirv-tools.d.ts b/third_party/spirv-tools/source/wasm/spirv-tools.d.ts new file mode 100644 index 0000000000..9c197973d5 --- /dev/null +++ b/third_party/spirv-tools/source/wasm/spirv-tools.d.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2020 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. + +declare interface SpirvTools { + as(input: string, env: number, options: number): Uint8Array; + dis(input: Uint8Array, env: number, options: number): string; + + SPV_ENV_UNIVERSAL_1_0: number; + SPV_ENV_VULKAN_1_0: number; + SPV_ENV_UNIVERSAL_1_1: number; + SPV_ENV_OPENCL_2_1: number; + SPV_ENV_OPENCL_2_2: number; + SPV_ENV_OPENGL_4_0: number; + SPV_ENV_OPENGL_4_1: number; + SPV_ENV_OPENGL_4_2: number; + SPV_ENV_OPENGL_4_3: number; + SPV_ENV_OPENGL_4_5: number; + SPV_ENV_UNIVERSAL_1_2: number; + SPV_ENV_OPENCL_1_2: number; + SPV_ENV_OPENCL_EMBEDDED_1_2: number; + SPV_ENV_OPENCL_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_1: number; + SPV_ENV_OPENCL_EMBEDDED_2_2: number; + SPV_ENV_UNIVERSAL_1_3: number; + SPV_ENV_VULKAN_1_1: number; + SPV_ENV_WEBGPU_0: number; + SPV_ENV_UNIVERSAL_1_4: number; + SPV_ENV_VULKAN_1_1_SPIRV_1_4: number; + SPV_ENV_UNIVERSAL_1_5: number; + SPV_ENV_VULKAN_1_2: number; + + SPV_TEXT_TO_BINARY_OPTION_NONE: number; + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number; + + SPV_BINARY_TO_TEXT_OPTION_NONE: number; + SPV_BINARY_TO_TEXT_OPTION_PRINT: number; + SPV_BINARY_TO_TEXT_OPTION_COLOR: number; + SPV_BINARY_TO_TEXT_OPTION_INDENT: number; + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET: number; + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER: number; + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES: number; +} + +export default function (): Promise; diff --git a/third_party/spirv-tools/test/CMakeLists.txt b/third_party/spirv-tools/test/CMakeLists.txt index 8ede58bfa6..e88df04d79 100644 --- a/third_party/spirv-tools/test/CMakeLists.txt +++ b/third_party/spirv-tools/test/CMakeLists.txt @@ -186,9 +186,11 @@ endif() add_subdirectory(link) +add_subdirectory(lint) add_subdirectory(opt) add_subdirectory(reduce) add_subdirectory(fuzz) add_subdirectory(tools) add_subdirectory(util) add_subdirectory(val) +add_subdirectory(fuzzers) diff --git a/third_party/spirv-tools/test/binary_to_text_test.cpp b/third_party/spirv-tools/test/binary_to_text_test.cpp index 9cad9661d1..df703e5df4 100644 --- a/third_party/spirv-tools/test/binary_to_text_test.cpp +++ b/third_party/spirv-tools/test/binary_to_text_test.cpp @@ -386,7 +386,7 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(std::vector{ "OpExecutionModeId %1 SubgroupsPerWorkgroupId %2\n", "OpExecutionModeId %1 LocalSizeId %2 %3 %4\n", - "OpExecutionModeId %1 LocalSizeHintId %2\n", + "OpExecutionModeId %1 LocalSizeHintId %2 %3 %4\n", "OpDecorateId %1 AlignmentId %2\n", "OpDecorateId %1 MaxByteOffsetId %2\n", }))); diff --git a/third_party/spirv-tools/test/fuzz/CMakeLists.txt b/third_party/spirv-tools/test/fuzz/CMakeLists.txt index 8acebde6e7..56af0b9dd6 100644 --- a/third_party/spirv-tools/test/fuzz/CMakeLists.txt +++ b/third_party/spirv-tools/test/fuzz/CMakeLists.txt @@ -122,6 +122,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_vector_shuffle_test.cpp transformation_wrap_early_terminator_in_function_test.cpp transformation_wrap_region_in_selection_test.cpp + transformation_wrap_vector_synonym_test.cpp uniform_buffer_element_descriptor_test.cpp) if (${SPIRV_ENABLE_LONG_FUZZER_TESTS}) diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp index 734f47afd8..1045f8a157 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp @@ -134,7 +134,7 @@ TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) { FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); SetUpIdSynonyms(transformation_context.GetFactManager()); diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp index a02176b215..a858e4ce60 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp @@ -92,7 +92,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { FuzzerPassConstructComposites fuzzer_pass( context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); fuzzer_pass.Apply(); @@ -173,7 +173,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { FuzzerPassConstructComposites fuzzer_pass( context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); fuzzer_pass.Apply(); diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp index f11885d4e6..fe8e671d1b 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -210,7 +210,7 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -291,7 +291,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -422,7 +422,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -517,7 +517,7 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -531,6 +531,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { std::string recipient_shader = R"( OpCapability Shader OpCapability ImageQuery + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -548,6 +549,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { std::string donor_shader = R"( OpCapability Shader OpCapability ImageQuery + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -587,7 +589,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -715,7 +717,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -811,7 +813,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -943,7 +945,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1079,7 +1081,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1161,7 +1163,7 @@ TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1248,7 +1250,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1352,7 +1354,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1424,7 +1426,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1534,7 +1536,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); @@ -1718,7 +1720,7 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1790,7 +1792,7 @@ TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -1947,7 +1949,7 @@ TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), false); @@ -2020,7 +2022,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability( SpvCapabilityVariablePointersStorageBuffer)); @@ -2253,7 +2255,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) { FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence, {}); + &transformation_sequence, false, {}); fuzzer_pass.DonateSingleModule(donor_context.get(), true); diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp index 0d2c5bf745..a088e17b15 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp @@ -130,7 +130,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 28 auto suitable_entry_block = @@ -173,7 +173,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 20 auto suitable_entry_block = @@ -297,7 +297,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 21 auto suitable_entry_block = @@ -464,7 +464,7 @@ TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) { FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, &fuzzer_context, - &transformation_sequence); + &transformation_sequence, false); // Block 39 is not a merge block, so it is already suitable. auto suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp index b035de7444..31b8582a5a 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp @@ -29,7 +29,7 @@ class FuzzerPassMock : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations) : FuzzerPass(ir_context, transformation_context, fuzzer_context, - transformations) {} + transformations, false) {} ~FuzzerPassMock() override = default; 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 6dc7ffb11c..2a22e6a5ea 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp @@ -1596,6 +1596,23 @@ const std::string kTestShader6 = R"( OpFunctionEnd )"; +// A virtually empty piece of SPIR-V. + +const std::string kTestShader7 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + void AddConstantUniformFact(protobufs::FactSequence* facts, uint32_t descriptor_set, uint32_t binding, std::vector&& indices, uint32_t value) { @@ -1668,7 +1685,7 @@ void RunFuzzerAndReplayer(const std::string& shader, Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), kConsoleMessageConsumer, donor_suppliers, enable_all_passes, - strategies[strategy_index], true, validator_options); + strategies[strategy_index], true, validator_options, false); auto fuzzer_result = fuzzer.Run(0); // Cycle the repeated pass strategy so that we try a different one next time @@ -1760,6 +1777,13 @@ TEST(FuzzerReplayerTest, Miscellaneous6) { kNumFuzzerRuns); } +TEST(FuzzerReplayerTest, Miscellaneous7) { + // Do some fuzzer runs, starting from an initial seed of 1 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader7, protobufs::FactSequence(), 1, + kNumFuzzerRuns); +} + } // namespace } // namespace fuzz } // namespace spvtools 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 e792116980..acee03c7a7 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp @@ -1068,7 +1068,7 @@ void RunFuzzerAndShrinker(const std::string& shader, Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), kConsoleMessageConsumer, donor_suppliers, enable_all_passes, repeated_pass_strategy, - true, validator_options); + true, validator_options, false); auto fuzzer_result = fuzzer.Run(0); ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule, fuzzer_result.status); diff --git a/third_party/spirv-tools/test/fuzz/fuzzerutil_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzerutil_test.cpp index 7ff9b4d8d7..0ad3e742b1 100644 --- a/third_party/spirv-tools/test/fuzz/fuzzerutil_test.cpp +++ b/third_party/spirv-tools/test/fuzz/fuzzerutil_test.cpp @@ -196,8 +196,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -507,8 +505,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -799,8 +795,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -894,8 +888,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -1165,8 +1157,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetStructTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -1261,8 +1251,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetVectorTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -1362,8 +1350,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetVoidTypeTest) { %50 = OpTypePointer Private %7 %34 = OpTypeBool %35 = OpConstantFalse %34 - %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -1563,6 +1549,259 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) { float_ids.end()); } +TEST(FuzzerutilTest, TypesAreCompatible) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeInt 32 0 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %9 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup + %89 = OpTypePointer Workgroup %9 + %19 = OpConstant %9 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 2 + %16 = OpConstant %6 7 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 + %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %9 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %9 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 + OpAtomicStore %14 %15 %20 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const uint32_t int_type = 6; // The id of OpTypeInt 32 1 + const uint32_t uint_type = 9; // The id of OpTypeInt 32 0 + + // OpAtomicLoad +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 2, + int_type, uint_type)); + + // OpAtomicExchange +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange, + 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange, + 2, int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicExchange, 3, int_type, uint_type)); + + // OpAtomicStore +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 3, int_type, uint_type)); + + // OpAtomicCompareExchange +#ifndef NDEBUG + ASSERT_DEATH( + fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicCompareExchange, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type)); + + // OpAtomicIIncrement +#ifndef NDEBUG + ASSERT_DEATH( + fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIIncrement, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible( + context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type)); + +// OpAtomicIDecrement +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, + 0, int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2, + int_type, uint_type)); + +// OpAtomicIAdd +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 3, + int_type, uint_type)); + +// OpAtomicISub +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 3, + int_type, uint_type)); + +// OpAtomicSMin +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 3, + int_type, uint_type)); + +// OpAtomicUMin +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 3, + int_type, uint_type)); + +// OpAtomicSMax +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 3, + int_type, uint_type)); + +// OpAtomicUMax +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 3, + int_type, uint_type)); + +// OpAtomicAnd +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 3, + int_type, uint_type)); + +// OpAtomicOr +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 3, + int_type, uint_type)); + +// OpAtomicXor +#ifndef NDEBUG + ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 0, + int_type, uint_type), + "Signedness check should not occur on a pointer operand."); +#endif + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 1, + int_type, uint_type)); + ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 2, + int_type, uint_type)); + ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 3, + int_type, uint_type)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/shrinker_test.cpp b/third_party/spirv-tools/test/fuzz/shrinker_test.cpp index 447ebecd74..942de29a31 100644 --- a/third_party/spirv-tools/test/fuzz/shrinker_test.cpp +++ b/third_party/spirv-tools/test/fuzz/shrinker_test.cpp @@ -171,7 +171,7 @@ TEST(ShrinkerTest, ReduceAddedFunctions) { protobufs::TransformationSequence transformations; FuzzerPassDonateModules pass(variant_ir_context.get(), &transformation_context, &fuzzer_context, - &transformations, {}); + &transformations, false, {}); pass.DonateSingleModule(donor_ir_context.get(), true); protobufs::FactSequence no_facts; @@ -349,7 +349,7 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) { protobufs::TransformationSequence transformations; FuzzerPassDonateModules pass(variant_ir_context.get(), &transformation_context, &fuzzer_context, - &transformations, {}); + &transformations, false, {}); pass.DonateSingleModule(donor_ir_context.get(), true); protobufs::FactSequence no_facts; diff --git a/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp index e7919816d9..bddcf5fe53 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationAccessChainTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %48 %54 @@ -63,7 +64,6 @@ TEST(TransformationAccessChainTest, BasicTest) { %85 = OpConstant %10 5 %52 = OpTypeArray %50 %51 %53 = OpTypePointer Private %52 - %45 = OpUndef %9 %46 = OpConstantNull %9 %47 = OpTypePointer Private %8 %48 = OpVariable %47 Private @@ -203,15 +203,6 @@ TEST(TransformationAccessChainTest, BasicTest) { #ifndef NDEBUG // Bad: pointer is null - ASSERT_DEATH( - TransformationAccessChain(100, 45, {80}, - MakeInstructionDescriptor(24, SpvOpLoad, 0)) - .IsApplicable(context.get(), transformation_context), - "Access chains should not be created from null/undefined pointers"); -#endif - -#ifndef NDEBUG - // Bad: pointer is undef ASSERT_DEATH( TransformationAccessChain(100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) @@ -331,6 +322,7 @@ TEST(TransformationAccessChainTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %48 %54 @@ -368,7 +360,6 @@ TEST(TransformationAccessChainTest, BasicTest) { %85 = OpConstant %10 5 %52 = OpTypeArray %50 %51 %53 = OpTypePointer Private %52 - %45 = OpUndef %9 %46 = OpConstantNull %9 %47 = OpTypePointer Private %8 %48 = OpVariable %47 Private diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp index 642a5561c1..ff8ac72e0b 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationAddCopyMemoryTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -64,7 +65,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { %67 = OpTypePointer Function %66 %83 = OpTypePointer Private %66 %86 = OpVariable %79 Private %20 - %87 = OpUndef %79 %88 = OpConstantNull %79 %4 = OpFunction %2 None %3 %5 = OpLabel @@ -182,12 +182,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { 90, 40, SpvStorageClassPrivate, 0) .IsApplicable(context.get(), transformation_context)); - // Source instruction is OpUndef. - ASSERT_FALSE( - TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), - 90, 87, SpvStorageClassPrivate, 0) - .IsApplicable(context.get(), transformation_context)); - // Source instruction is OpConstantNull. ASSERT_FALSE( TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), @@ -255,6 +249,7 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { std::string expected = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -293,7 +288,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) { %67 = OpTypePointer Function %66 %83 = OpTypePointer Private %66 %86 = OpVariable %79 Private %20 - %87 = OpUndef %79 %88 = OpConstantNull %79 %90 = OpVariable %79 Private %20 %92 = OpVariable %78 Private %25 diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp index 314b0039e9..ffcf1c9bd0 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp @@ -1242,9 +1242,10 @@ TEST(TransformationAddSynonymTest, MiscellaneousCopies) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { +TEST(TransformationAddSynonymTest, DoNotCopyNullPointers) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -1255,7 +1256,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %8 = OpConstantNull %7 - %9 = OpUndef %7 %4 = OpFunction %2 None %3 %5 = OpLabel OpReturn @@ -1275,12 +1275,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, MakeInstructionDescriptor(5, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); - - // Illegal to copy an OpUndef of pointer type. - ASSERT_FALSE(TransformationAddSynonym( - 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, - MakeInstructionDescriptor(5, SpvOpReturn, 0)) - .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) { diff --git a/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp index a03ffdd65d..370826eb82 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationLoadTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -49,7 +50,6 @@ TEST(TransformationLoadTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %4 = OpFunction %2 None %3 @@ -129,65 +129,69 @@ TEST(TransformationLoadTest, BasicTest) { // Pointers that cannot be used: // 60 - null - // 61 - undefined // Bad: id is not fresh - ASSERT_FALSE(TransformationLoad( - 33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(33, 33, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to load from 11 from outside its function - ASSERT_FALSE(TransformationLoad( - 100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 11, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available - ASSERT_FALSE(TransformationLoad( - 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 33, false, 0, 0, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable - ASSERT_FALSE(TransformationLoad( - 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationLoad(100, 27, false, 0, 0, + MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist ASSERT_FALSE( - TransformationLoad(100, 1000, + TransformationLoad(100, 1000, false, 0, 0, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists but does not have a type - ASSERT_FALSE(TransformationLoad( - 100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: pointer id exists and has a type, but is not a pointer - ASSERT_FALSE(TransformationLoad( - 100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: attempt to load from null pointer - ASSERT_FALSE(TransformationLoad( - 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - - // Bad: attempt to load from undefined pointer - ASSERT_FALSE(TransformationLoad( - 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); - // Bad: %40 is not available at the program point ASSERT_FALSE( - TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + TransformationLoad(100, 5, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) .IsApplicable(context.get(), transformation_context)); - // Bad: The described instruction does not exist - ASSERT_FALSE(TransformationLoad( - 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + // Bad: pointer id exists and has a type, but is not a pointer + ASSERT_FALSE( + TransformationLoad(100, 24, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to load from null pointer + ASSERT_FALSE( + TransformationLoad(100, 60, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: %40 is not available at the program point + ASSERT_FALSE(TransformationLoad(100, 40, false, 0, 0, + MakeInstructionDescriptor(37, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationLoad(100, 33, false, 0, 0, + MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + { TransformationLoad transformation( - 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 100, 33, false, 0, 0, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -198,7 +202,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + 101, 46, false, 0, 0, + MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -209,7 +214,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + 102, 16, false, 0, 0, + MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -220,7 +226,8 @@ TEST(TransformationLoadTest, BasicTest) { { TransformationLoad transformation( - 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); + 103, 40, false, 0, 0, + MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -231,6 +238,7 @@ TEST(TransformationLoadTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -254,7 +262,6 @@ TEST(TransformationLoadTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %4 = OpFunction %2 None %3 @@ -293,6 +300,296 @@ TEST(TransformationLoadTest, BasicTest) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationLoadTest, AtomicLoadTestCase) { + const std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: id is not fresh. + ASSERT_FALSE( + TransformationLoad(14, 14, true, 15, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: id 100 of memory scope instruction does not exist. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 100, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: id 100 of memory semantics instruction does not exist. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 100, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory scope should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 5, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory semantics should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 5, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The memory scope instruction must have an Integer operand. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 19, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The memory memory semantics instruction must have an Integer operand. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 19, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of the memory scope must be equal to 32 bits. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 17, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of memory semantics must be equal to 32 bits. + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 17, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory scope value must be 4 (SpvScopeInvocation). + ASSERT_FALSE( + TransformationLoad(21, 14, true, 16, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory semantics value must be either: + // 64 (SpvMemorySemanticsUniformMemoryMask) + // 256 (SpvMemorySemanticsWorkgroupMemoryMask) + ASSERT_FALSE( + TransformationLoad(21, 14, true, 15, 16, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationLoad(21, 14, false, 15, 20, + MakeInstructionDescriptor(150, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationLoad transformation( + 21, 14, true, 15, 20, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %21 = OpAtomicLoad %6 %14 %15 %20 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationLoadTest, AtomicLoadTestCaseForWorkgroupMemory) { + std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %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 + %26 = OpTypeFloat 32 + %27 = OpTypeInt 8 1 + %7 = OpTypeInt 32 0 ; 0 means unsigned + %8 = OpConstant %7 0 + %17 = OpConstant %27 4 + %19 = OpConstant %26 0 + %9 = OpTypePointer Function %6 + %13 = OpTypeStruct %6 + %12 = OpTypePointer Workgroup %13 + %11 = OpVariable %12 Workgroup + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 4 + %23 = OpConstant %6 256 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Workgroup %6 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %50 %11 %14 + %40 = OpAccessChain %50 %11 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: Can't insert OpAccessChain before the id 23 of memory scope. + ASSERT_FALSE( + TransformationLoad(60, 38, true, 21, 23, + MakeInstructionDescriptor(23, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 23 of memory semantics. + ASSERT_FALSE( + TransformationLoad(60, 38, true, 21, 23, + MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationLoad transformation( + 60, 38, true, 21, 23, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %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 + %26 = OpTypeFloat 32 + %27 = OpTypeInt 8 1 + %7 = OpTypeInt 32 0 ; 0 means unsigned + %8 = OpConstant %7 0 + %17 = OpConstant %27 4 + %19 = OpConstant %26 0 + %9 = OpTypePointer Function %6 + %13 = OpTypeStruct %6 + %12 = OpTypePointer Workgroup %13 + %11 = OpVariable %12 Workgroup + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 4 + %23 = OpConstant %6 256 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Workgroup %6 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %50 %11 %14 + %60 = OpAtomicLoad %6 %38 %21 %23 + %40 = OpAccessChain %50 %11 %14 + OpBranch %37 + %37 = 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_mutate_pointer_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp index ae42310004..e869efa6f0 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationMutatePointerTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -60,7 +61,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { %23 = OpTypePointer Output %6 %24 = OpVariable %23 Output %27 = OpTypeFunction %2 %13 - %32 = OpUndef %16 %33 = OpConstantNull %16 %4 = OpFunction %2 None %3 %5 = OpLabel @@ -110,10 +110,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before) .IsApplicable(context.get(), transformation_context)); - // |pointer_id| is a result id of OpUndef. - ASSERT_FALSE(TransformationMutatePointer(32, 70, insert_before) - .IsApplicable(context.get(), transformation_context)); - // |pointer_id| is a result id of OpConstantNull. ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before) .IsApplicable(context.get(), transformation_context)); @@ -163,6 +159,7 @@ TEST(TransformationMutatePointerTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" @@ -197,7 +194,6 @@ TEST(TransformationMutatePointerTest, BasicTest) { %23 = OpTypePointer Output %6 %24 = OpVariable %23 Output %27 = OpTypeFunction %2 %13 - %32 = OpUndef %16 %33 = OpConstantNull %16 %4 = OpFunction %2 None %3 %5 = OpLabel diff --git a/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp index 7487cab9df..b0fff58873 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationPushIdThroughVariableTest, IsApplicable) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -50,7 +51,6 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -257,6 +257,7 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) { TEST(TransformationPushIdThroughVariableTest, Apply) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -281,7 +282,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -419,6 +419,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { std::string variant_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111 @@ -443,7 +444,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -519,6 +519,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -543,7 +544,6 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -620,6 +620,7 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { std::string reference_shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -644,7 +645,6 @@ TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 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 c0739c3bae..b33dd48ca2 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 @@ -1796,27 +1796,8 @@ TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) { .IsApplicable(context.get(), transformation_context)); } -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4346): This test -// should be updated to cover more atomic operations, and once the issue is -// fixed the test should be enabled. TEST(TransformationReplaceIdWithSynonymTest, - DISABLED_AtomicScopeAndMemorySemanticsMustBeConstant) { - // The following SPIR-V came from this GLSL, edited to add some synonyms: - // - // #version 320 es - // - // #extension GL_KHR_memory_scope_semantics : enable - // - // layout(set = 0, binding = 0) buffer Buf { - // int x; - // }; - // - // void main() { - // int tmp = atomicLoad(x, - // gl_ScopeWorkgroup, - // gl_StorageSemanticsBuffer, - // gl_SemanticsRelaxed); - // } + AtomicScopeAndMemorySemanticsMustBeConstant) { const std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -1832,15 +1813,19 @@ TEST(TransformationReplaceIdWithSynonymTest, %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 + %17 = OpTypeInt 32 0 %7 = OpTypePointer Function %6 %9 = OpTypeStruct %6 %10 = OpTypePointer StorageBuffer %9 %11 = OpVariable %10 StorageBuffer + %86 = OpTypeStruct %17 + %87 = OpTypePointer Workgroup %86 + %88 = OpVariable %87 Workgroup %12 = OpConstant %6 0 %13 = OpTypePointer StorageBuffer %6 %15 = OpConstant %6 2 %16 = OpConstant %6 64 - %17 = OpTypeInt 32 0 + %89 = OpTypePointer Workgroup %17 %18 = OpConstant %17 1 %19 = OpConstant %17 0 %20 = OpConstant %17 64 @@ -1850,8 +1835,23 @@ TEST(TransformationReplaceIdWithSynonymTest, %100 = OpCopyObject %6 %15 ; A non-constant version of %15 %101 = OpCopyObject %17 %20 ; A non-constant version of %20 %14 = OpAccessChain %13 %11 %12 + %90 = OpAccessChain %89 %88 %19 %21 = OpAtomicLoad %6 %14 %15 %20 + %22 = OpAtomicExchange %6 %14 %15 %20 %16 + %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15 + %24 = OpAtomicIIncrement %6 %14 %15 %20 + %25 = OpAtomicIDecrement %6 %14 %15 %20 + %26 = OpAtomicIAdd %6 %14 %15 %20 %16 + %27 = OpAtomicISub %6 %14 %15 %20 %16 + %28 = OpAtomicSMin %6 %14 %15 %20 %16 + %29 = OpAtomicUMin %17 %90 %15 %20 %18 + %30 = OpAtomicSMax %6 %14 %15 %20 %15 + %31 = OpAtomicUMax %17 %90 %15 %20 %18 + %32 = OpAtomicAnd %6 %14 %15 %20 %16 + %33 = OpAtomicOr %6 %14 %15 %20 %16 + %34 = OpAtomicXor %6 %14 %15 %20 %16 OpStore %8 %21 + OpAtomicStore %14 %15 %20 %12 OpReturn OpFunctionEnd )"; @@ -1872,7 +1872,7 @@ TEST(TransformationReplaceIdWithSynonymTest, // Tell the fact manager that %101 and %20 are synonymous transformation_context.GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {})); - + // OpAtomicLoad const auto& scope_operand = MakeIdUseDescriptorFromUse( context.get(), context->get_def_use_mgr()->GetDef(21), 1); ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand, 100) @@ -1882,6 +1882,148 @@ TEST(TransformationReplaceIdWithSynonymTest, context.get(), context->get_def_use_mgr()->GetDef(21), 2); ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand, 101) .IsApplicable(context.get(), transformation_context)); + // OpAtomicExchange. + const auto& scope_operand2 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(22), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand2, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand2 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(22), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand2, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicCompareExchange. + const auto& scope_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand3, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_equal_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_equal_operand3, 101) + .IsApplicable(context.get(), transformation_context)); + const auto& semantics_unequal_operand3 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(23), 3); + ASSERT_FALSE( + TransformationReplaceIdWithSynonym(semantics_unequal_operand3, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicIIncrement. + const auto& scope_operand4 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(24), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand4, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand4 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(24), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand4, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicIDecrement. + const auto& scope_operand5 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(25), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand5, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand5 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(25), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand5, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicIAdd. + const auto& scope_operand6 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(26), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand6, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand6 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(26), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand6, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicISub + const auto& scope_operand8 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(27), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand8, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand8 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(27), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand8, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicSMin + const auto& scope_operand9 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(28), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand9, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand9 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(28), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand9, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicUMin + const auto& scope_operand10 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(29), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand10, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand10 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(29), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand10, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicSMax + const auto& scope_operand11 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(30), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand11, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand11 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(30), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand11, 101) + .IsApplicable(context.get(), transformation_context)); + // OpAtomicUMax + const auto& scope_operand12 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(31), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand12, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand12 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(31), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand12, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicAnd + const auto& scope_operand13 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(32), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand13, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand13 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(32), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand13, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicOr + const auto& scope_operand14 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(33), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand14, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand14 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(33), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand14, 101) + .IsApplicable(context.get(), transformation_context)); + + // OpAtomicXor + const auto& scope_operand15 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(34), 1); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand15, 100) + .IsApplicable(context.get(), transformation_context)); + + const auto& semantics_operand15 = MakeIdUseDescriptorFromUse( + context.get(), context->get_def_use_mgr()->GetDef(34), 2); + ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand15, 101) + .IsApplicable(context.get(), transformation_context)); } // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this @@ -2031,65 +2173,6 @@ TEST(TransformationReplaceIdWithSynonymTest, ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } -// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this -// test so that it covers more atomic operations, and enable the test once the -// issue is fixed. -TEST(TransformationReplaceIdWithSynonymTest, DISABLED_TypesAreCompatible) { - const std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 320 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeInt 32 1 - %9 = OpTypeInt 32 0 - %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); - spvtools::ValidatorOptions validator_options; - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); - - const uint32_t int_type = 6; // The id of OpTypeInt 32 1 - const uint32_t uint_type = 9; // The id of OpTypeInt 32 0 - - // OpAtomicLoad -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicLoad, 0, int_type, uint_type), - "Invalid operand index"); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicLoad, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicLoad, 2, int_type, uint_type)); - - // OpAtomicExchange -#ifndef NDEBUG - ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 0, int_type, uint_type), - "Invalid operand index"); -#endif - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 1, int_type, uint_type)); - ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); - ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible( - context.get(), SpvOpAtomicExchange, 2, int_type, uint_type)); - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Similar for - // other atomic instructions -} - } // namespace } // namespace fuzz } // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp index 93257d0d0e..dd653e28d9 100644 --- a/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp +++ b/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp @@ -26,6 +26,7 @@ namespace { TEST(TransformationStoreTest, BasicTest) { std::string shader = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -50,7 +51,6 @@ TEST(TransformationStoreTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -148,90 +148,107 @@ TEST(TransformationStoreTest, BasicTest) { // 61 - undefined // Bad: attempt to store to 11 from outside its function - ASSERT_FALSE(TransformationStore( - 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(11, false, 0, 0, 80, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer is not available - ASSERT_FALSE(TransformationStore( - 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(81, false, 0, 0, 80, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to insert before OpVariable - ASSERT_FALSE(TransformationStore( - 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(52, false, 0, 0, 24, + MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id does not exist - ASSERT_FALSE(TransformationStore( - 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(1000, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists but does not have a type - ASSERT_FALSE(TransformationStore( - 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(5, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: pointer id exists and has a type, but is not a pointer - ASSERT_FALSE(TransformationStore( - 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(24, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to a null pointer - ASSERT_FALSE(TransformationStore( - 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(60, false, 0, 0, 24, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to an undefined pointer - ASSERT_FALSE(TransformationStore( - 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(61, false, 0, 0, 21, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: %82 is not available at the program point ASSERT_FALSE( - TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + TransformationStore(82, false, 0, 0, 80, + MakeInstructionDescriptor(37, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: value id does not exist - ASSERT_FALSE(TransformationStore( - 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 1000, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value id exists but does not have a type - ASSERT_FALSE(TransformationStore( - 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 15, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value id exists but has the wrong type - ASSERT_FALSE(TransformationStore( - 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 14, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: attempt to store to read-only variable - ASSERT_FALSE(TransformationStore( - 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(92, false, 0, 0, 93, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: value is not available - ASSERT_FALSE(TransformationStore( - 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 95, + MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); // Bad: variable being stored to does not have an irrelevant pointee value, // and the store is not in a dead block. - ASSERT_FALSE(TransformationStore( - 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(20, false, 0, 0, 95, + MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); // The described instruction does not exist. - ASSERT_FALSE(TransformationStore( - 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, false, 0, 0, 80, + MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); { // Store to irrelevant variable from dead block. TransformationStore transformation( - 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 27, false, 0, 0, 80, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -243,7 +260,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 11, false, 0, 0, 95, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -255,7 +273,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 46, false, 0, 0, 80, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -267,7 +286,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to irrelevant variable from live block. TransformationStore transformation( - 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + 16, false, 0, 0, 21, + MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -279,7 +299,8 @@ TEST(TransformationStoreTest, BasicTest) { { // Store to non-irrelevant variable from dead block. TransformationStore transformation( - 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + 53, false, 0, 0, 21, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -290,6 +311,7 @@ TEST(TransformationStoreTest, BasicTest) { std::string after_transformation = R"( OpCapability Shader + OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %92 %52 %53 @@ -314,7 +336,6 @@ TEST(TransformationStoreTest, BasicTest) { %34 = OpTypeBool %35 = OpConstantFalse %34 %60 = OpConstantNull %50 - %61 = OpUndef %51 %52 = OpVariable %50 Private %53 = OpVariable %51 Private %80 = OpConstantComposite %8 %21 %24 @@ -414,16 +435,190 @@ TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) { transformation_context.GetFactManager()->AddFactBlockIsDead(5); ASSERT_FALSE( - TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(15, false, 0, 0, 13, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( - TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(19, false, 0, 0, 50, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( - TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + TransformationStore(27, false, 0, 0, 50, + MakeInstructionDescriptor(27, SpvOpReturn, 0)) .IsApplicable(context.get(), transformation_context)); } +TEST(TransformationStoreTest, SupportAtomicStore) { + const std::string shader = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %21 = OpConstant %6 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 14); + + // Bad: id 100 of memory scope instruction does not exist. + ASSERT_FALSE( + TransformationStore(14, true, 100, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: id 100 of memory semantics instruction does not exist. + ASSERT_FALSE( + TransformationStore(14, true, 15, 100, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory scope should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationStore(14, true, 5, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: memory semantics should be |OpConstant| opcode. + ASSERT_FALSE( + TransformationStore(14, true, 15, 5, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The memory scope instruction must have an Integer operand. + ASSERT_FALSE( + TransformationStore(14, true, 15, 19, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The memory memory semantics instruction must have an Integer operand. + ASSERT_FALSE( + TransformationStore(14, true, 19, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of the memory scope must be equal to 32 bits. + ASSERT_FALSE( + TransformationStore(14, true, 17, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Integer size of memory semantics must be equal to 32 bits. + ASSERT_FALSE( + TransformationStore(14, true, 15, 17, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory scope value must be 4 (SpvScopeInvocation). + ASSERT_FALSE( + TransformationStore(14, true, 16, 20, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: memory semantics value must be either: + // 64 (SpvMemorySemanticsUniformMemoryMask) + // 256 (SpvMemorySemanticsWorkgroupMemoryMask) + ASSERT_FALSE( + TransformationStore(14, true, 15, 16, 21, + MakeInstructionDescriptor(24, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: The described instruction does not exist + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(150, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 15 of memory scope. + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(15, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Can't insert OpAccessChain before the id 20 of memory semantics. + ASSERT_FALSE( + TransformationStore(14, true, 15, 20, 21, + MakeInstructionDescriptor(20, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations. + { + TransformationStore transformation( + 14, true, 15, 20, 21, MakeInstructionDescriptor(24, SpvOpReturn, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + const std::string after_transformation = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 8 1 + %9 = OpTypeInt 32 0 + %26 = OpTypeFloat 32 + %8 = OpTypeStruct %6 + %10 = OpTypePointer StorageBuffer %8 + %11 = OpVariable %10 StorageBuffer + %19 = OpConstant %26 0 + %18 = OpConstant %9 1 + %12 = OpConstant %6 0 + %13 = OpTypePointer StorageBuffer %6 + %15 = OpConstant %6 4 + %16 = OpConstant %6 7 + %17 = OpConstant %7 4 + %20 = OpConstant %9 64 + %21 = OpConstant %6 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpAccessChain %13 %11 %12 + %24 = OpAccessChain %13 %11 %12 + OpAtomicStore %14 %15 %20 %21 + 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_wrap_vector_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_wrap_vector_synonym_test.cpp new file mode 100644 index 0000000000..0d1009b313 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_wrap_vector_synonym_test.cpp @@ -0,0 +1,1553 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationWrapVectorSynonym, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Vec Type Id | Vector Type | Element Type id | Element Type | + // ------------+----------------+------------------+-----------------+ + // 12 | vec2 | 6 | int32 | + // 24 | vec3 | 18 | uint32 | + // 37 | vec4 | 31 | float | + + // Instruction Id | Opcode | Type Id | constant id 1 | constant id 2 | + // ---------------+---------+---------+---------------+---------------+ + // 50 | OpIAdd | 6 | 48 | 49 | + // 54 | OpISub | 6 | 52 | 53 | + // 58 | OpIMul | 6 | 56 | 57 | + // 62 | OpSDiv | 6 | 60 | 61 | + // 66 | OpIAdd | 18 | 64 | 65 | + // 70 | OpISub | 18 | 68 | 69 | + // 74 | OpIMul | 18 | 72 | 73 | + // 78 | OpUDiv | 18 | 76 | 77 | + // 82 | OpFAdd | 31 | 80 | 81 | + // 86 | OpFSub | 31 | 84 | 85 | + // 90 | OpFMul | 31 | 88 | 89 | + // 94 | OpFDiv | 31 | 92 | 93 | + + // Assert that the target scalar instruction result id is relevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {})); + + // The following are all invalid use. + { + // Bad: Instruction id does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad1(103, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad1.IsApplicable(context.get(), transformation_context)); + + // Bad: Instruction id given is not of a valid arithmetic operation typed + // instruction. + TransformationWrapVectorSynonym wrap_add_int_bad2(80, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad1.IsApplicable(context.get(), transformation_context)); + + // Bad: the id for the first vector does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad3(50, 105, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad3.IsApplicable(context.get(), transformation_context)); + + // Bad: the id for the second vector does not exist. + TransformationWrapVectorSynonym wrap_add_int_bad4(50, 100, 105, 102, 1); + ASSERT_FALSE( + wrap_add_int_bad4.IsApplicable(context.get(), transformation_context)); + + // Bad: vector id is not fresh. + TransformationWrapVectorSynonym wrap_add_int_bad6(50, 100, 101, 94, 1); + ASSERT_FALSE( + wrap_add_int_bad6.IsApplicable(context.get(), transformation_context)); + + // Bad: The position goes out of bound for the given vector type. + TransformationWrapVectorSynonym wrap_add_int_bad8(50, 100, 101, 102, 2); + ASSERT_FALSE( + wrap_add_int_bad8.IsApplicable(context.get(), transformation_context)); + + // Bad: The original instruction is not a valid scalar operation + // instruction. + TransformationWrapVectorSynonym wrap_add_int(27, 100, 101, 102, 1); + ASSERT_FALSE( + wrap_add_int.IsApplicable(context.get(), transformation_context)); + } + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_int(50, 100, 101, 102, 1); + ASSERT_TRUE(wrap_add_int.IsApplicable(context.get(), transformation_context)); + // Insert an arithmetic instruction of the same type to add two vectors. + ApplyAndCheckFreshIds(wrap_add_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should be + // synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {}))); + + // After applying transformations, the instruction: + // + // %102 = OpIAdd %12 %100 %101 + // + // should be added before: + // + // %50 = OpIAdd %6 %48 %49 + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %102 = OpIAdd %12 %100 %101 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, OperationSupportTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %52 %52 + %101 = OpCompositeConstruct %12 %53 %53 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %103 = OpCompositeConstruct %12 %56 %56 + %104 = OpCompositeConstruct %12 %57 %57 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %106 = OpCompositeConstruct %24 %64 %64 %64 + %107 = OpCompositeConstruct %24 %65 %65 %65 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %109 = OpCompositeConstruct %24 %68 %68 %68 + %110 = OpCompositeConstruct %24 %69 %69 %69 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %112 = OpCompositeConstruct %24 %72 %72 %72 + %113 = OpCompositeConstruct %24 %73 %73 %73 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %115 = OpCompositeConstruct %37 %80 %80 %80 %80 + %116 = OpCompositeConstruct %37 %81 %81 %81 %81 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %118 = OpCompositeConstruct %37 %84 %84 %84 %84 + %119 = OpCompositeConstruct %37 %85 %85 %85 %85 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %121 = OpCompositeConstruct %37 %88 %88 %88 %88 + %122 = OpCompositeConstruct %37 %89 %89 %89 %89 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + // Add synonym facts between the vector operands at pos and the operands to + // the scalar instruction. + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(52, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(53, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {0}), MakeDataDescriptor(56, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {0}), MakeDataDescriptor(57, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {2}), MakeDataDescriptor(64, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {2}), MakeDataDescriptor(65, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(109, {2}), MakeDataDescriptor(68, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(110, {2}), MakeDataDescriptor(69, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(112, {1}), MakeDataDescriptor(72, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(113, {1}), MakeDataDescriptor(73, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(115, {2}), MakeDataDescriptor(80, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(116, {2}), MakeDataDescriptor(81, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(118, {3}), MakeDataDescriptor(84, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(119, {3}), MakeDataDescriptor(85, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(121, {1}), MakeDataDescriptor(88, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(122, {1}), MakeDataDescriptor(89, {})); + } + + // Test OpISub for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_int(54, 100, 101, 102, 1); + ASSERT_TRUE( + wrap_sub_int.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_sub_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(54, {}))); + } + + // Test OpIMul for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_int(58, 103, 104, 105, 0); + ASSERT_TRUE( + wrap_mul_int.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_int, context.get(), &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(105, {0}), MakeDataDescriptor(58, {}))); + } + + // Test OpIAdd for unsigned integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_uint(66, 106, 107, 108, 2); + ASSERT_TRUE( + wrap_add_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {2}), MakeDataDescriptor(66, {}))); + } + + // Test OpISub for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_uint(70, 109, 110, 111, 2); + ASSERT_TRUE( + wrap_sub_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_sub_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(111, {2}), MakeDataDescriptor(70, {}))); + } + + // Test OpIMul for signed integer. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_uint(74, 112, 113, 114, 1); + ASSERT_TRUE( + wrap_mul_uint.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_uint, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(114, {1}), MakeDataDescriptor(74, {}))); + } + + // Test OpFAdd for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_float(82, 115, 116, 117, 2); + ASSERT_TRUE( + wrap_add_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {2}), MakeDataDescriptor(82, {}))); + } + + // Test OpFSub for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_float(86, 118, 119, 120, 3); + ASSERT_TRUE( + wrap_add_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_add_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(120, {3}), MakeDataDescriptor(86, {}))); + } + + // Test OpFMul for float. + { + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_float(90, 121, 122, 123, 1); + ASSERT_TRUE( + wrap_mul_float.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(wrap_mul_float, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(123, {1}), MakeDataDescriptor(90, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %52 %52 + %101 = OpCompositeConstruct %12 %53 %53 + %102 = OpISub %12 %100 %101 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %103 = OpCompositeConstruct %12 %56 %56 + %104 = OpCompositeConstruct %12 %57 %57 + %105 = OpIMul %12 %103 %104 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %106 = OpCompositeConstruct %24 %64 %64 %64 + %107 = OpCompositeConstruct %24 %65 %65 %65 + %108 = OpIAdd %24 %106 %107 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %109 = OpCompositeConstruct %24 %68 %68 %68 + %110 = OpCompositeConstruct %24 %69 %69 %69 + %111 = OpISub %24 %109 %110 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %112 = OpCompositeConstruct %24 %72 %72 %72 + %113 = OpCompositeConstruct %24 %73 %73 %73 + %114 = OpIMul %24 %112 %113 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %115 = OpCompositeConstruct %37 %80 %80 %80 %80 + %116 = OpCompositeConstruct %37 %81 %81 %81 %81 + %117 = OpFAdd %37 %115 %116 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %118 = OpCompositeConstruct %37 %84 %84 %84 %84 + %119 = OpCompositeConstruct %37 %85 %85 %85 %85 + %120 = OpFSub %37 %118 %119 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %121 = OpCompositeConstruct %37 %88 %88 %88 %88 + %122 = OpCompositeConstruct %37 %89 %89 %89 %89 + %123 = OpFMul %37 %121 %122 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, DivSupportTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 32 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 32 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %60 %60 + %101 = OpCompositeConstruct %12 %61 %61 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %102 = OpCompositeConstruct %24 %76 %76 %76 + %103 = OpCompositeConstruct %24 %77 %77 %77 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %104 = OpCompositeConstruct %37 %92 %92 %92 %92 + %105 = OpCompositeConstruct %37 %93 %93 %93 %93 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(60, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(61, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(76, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {1}), MakeDataDescriptor(77, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {1}), MakeDataDescriptor(92, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(105, {1}), MakeDataDescriptor(93, {})); + + // Div operations are not currently supported. + { + TransformationWrapVectorSynonym wrap_div_bad1(62, 100, 101, 106, 1); + ASSERT_FALSE( + wrap_div_bad1.IsApplicable(context.get(), transformation_context)); + + TransformationWrapVectorSynonym wrap_div_bad2(78, 102, 103, 106, 1); + ASSERT_FALSE( + wrap_div_bad2.IsApplicable(context.get(), transformation_context)); + + TransformationWrapVectorSynonym wrap_div_bad3(94, 104, 105, 106, 1); + ASSERT_FALSE( + wrap_div_bad3.IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) { + std::string shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 64 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 64 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 64 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %103 = OpCompositeConstruct %24 %68 %68 %68 + %104 = OpCompositeConstruct %24 %69 %69 %69 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %106 = OpCompositeConstruct %37 %88 %88 %88 %88 + %107 = OpCompositeConstruct %37 %89 %89 %89 %89 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Vec Type Id | Vector Type | Element Type id | Element Type | + // ------------+----------------+------------------+-----------------+ + // 12 | vec2 | 6 | int64 | + // 24 | vec3 | 18 | uint64 | + // 37 | vec4 | 31 | float64 | + + // Test support for 64-bit signed int. + { + // Assert that the target scalar instruction result id is relevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_add_int64(50, 100, 101, 102, 1); + ASSERT_TRUE( + wrap_add_int64.IsApplicable(context.get(), transformation_context)); + // Insert an arithmetic instruction of the same type to add two vectors. + ApplyAndCheckFreshIds(wrap_add_int64, context.get(), + &transformation_context); + + // |instruction_id| and id at |scalar_position of the result vector should + // be synonyms. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {}))); + } + + // Test support for 64-bit unsigned int. + { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(70)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(103, {2}), MakeDataDescriptor(68, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(104, {2}), MakeDataDescriptor(69, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_sub_uint64(70, 103, 104, 105, 2); + ASSERT_TRUE( + wrap_sub_uint64.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(wrap_sub_uint64, context.get(), + &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(105, {2}), MakeDataDescriptor(70, {}))); + } + + // Test support for 64-bit float. + { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(90)); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {3}), MakeDataDescriptor(88, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {3}), MakeDataDescriptor(89, {})); + + // Good: The following transformation should be applicable. + TransformationWrapVectorSynonym wrap_mul_float64(90, 106, 107, 108, 3); + ASSERT_TRUE( + wrap_mul_float64.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(wrap_mul_float64, context.get(), + &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {3}), MakeDataDescriptor(90, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %97 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 64 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 -5 + %12 = OpTypeVector %6 2 + %13 = OpTypePointer Function %12 + %18 = OpTypeInt 64 0 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 8 + %23 = OpConstant %18 2 + %24 = OpTypeVector %18 3 + %25 = OpTypePointer Function %24 + %31 = OpTypeFloat 64 + %32 = OpTypePointer Function %31 + %34 = OpConstant %31 3.29999995 + %36 = OpConstant %31 1.10000002 + %37 = OpTypeVector %31 4 + %38 = OpTypePointer Function %37 + %96 = OpTypePointer Input %31 + %97 = OpVariable %96 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %33 = OpVariable %32 Function + %35 = OpVariable %32 Function + %39 = OpVariable %38 Function + %47 = OpVariable %7 Function + %51 = OpVariable %7 Function + %55 = OpVariable %7 Function + %59 = OpVariable %7 Function + %63 = OpVariable %19 Function + %67 = OpVariable %19 Function + %71 = OpVariable %19 Function + %75 = OpVariable %19 Function + %79 = OpVariable %32 Function + %83 = OpVariable %32 Function + %87 = OpVariable %32 Function + %91 = OpVariable %32 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + OpStore %20 %21 + OpStore %22 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %22 + %30 = OpCompositeConstruct %24 %27 %28 %29 + OpStore %26 %30 + OpStore %33 %34 + OpStore %35 %36 + %40 = OpLoad %31 %33 + %41 = OpLoad %31 %33 + %42 = OpLoad %31 %35 + %43 = OpLoad %31 %35 + %44 = OpCompositeConstruct %37 %40 %41 %42 %43 + %45 = OpLoad %37 %39 + %46 = OpVectorShuffle %37 %45 %44 5 6 7 4 + OpStore %39 %46 + %48 = OpLoad %6 %8 + %49 = OpLoad %6 %10 + %100 = OpCompositeConstruct %12 %48 %48 + %101 = OpCompositeConstruct %12 %49 %49 + %102 = OpIAdd %12 %100 %101 + %50 = OpIAdd %6 %48 %49 + OpStore %47 %50 + %52 = OpLoad %6 %8 + %53 = OpLoad %6 %10 + %54 = OpISub %6 %52 %53 + OpStore %51 %54 + %56 = OpLoad %6 %8 + %57 = OpLoad %6 %10 + %58 = OpIMul %6 %56 %57 + OpStore %55 %58 + %60 = OpLoad %6 %8 + %61 = OpLoad %6 %10 + %62 = OpSDiv %6 %60 %61 + OpStore %59 %62 + %64 = OpLoad %18 %20 + %65 = OpLoad %18 %22 + %66 = OpIAdd %18 %64 %65 + OpStore %63 %66 + %68 = OpLoad %18 %20 + %69 = OpLoad %18 %22 + %103 = OpCompositeConstruct %24 %68 %68 %68 + %104 = OpCompositeConstruct %24 %69 %69 %69 + %105 = OpISub %24 %103 %104 + %70 = OpISub %18 %68 %69 + OpStore %67 %70 + %72 = OpLoad %18 %20 + %73 = OpLoad %18 %22 + %74 = OpIMul %18 %72 %73 + OpStore %71 %74 + %76 = OpLoad %18 %20 + %77 = OpLoad %18 %22 + %78 = OpUDiv %18 %76 %77 + OpStore %75 %78 + %80 = OpLoad %31 %33 + %81 = OpLoad %31 %35 + %82 = OpFAdd %31 %80 %81 + OpStore %79 %82 + %84 = OpLoad %31 %33 + %85 = OpLoad %31 %35 + %86 = OpFSub %31 %84 %85 + OpStore %83 %86 + %88 = OpLoad %31 %33 + %89 = OpLoad %31 %35 + %106 = OpCompositeConstruct %37 %88 %88 %88 %88 + %107 = OpCompositeConstruct %37 %89 %89 %89 %89 + %108 = OpFMul %37 %106 %107 + %90 = OpFMul %31 %88 %89 + OpStore %87 %90 + %92 = OpLoad %31 %33 + %93 = OpLoad %31 %35 + %94 = OpFDiv %31 %92 %93 + OpStore %91 %94 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, DifferentVectorSignedness) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 2 + %18 = OpConstant %14 3 + %19 = OpConstant %14 0 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpConstantComposite %15 %19 %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpIAdd %14 %10 %18 + %101 = OpIAdd %6 %10 %18 + %102 = OpIAdd %6 %18 %19 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(18, {}), MakeDataDescriptor(20, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(19, {}), MakeDataDescriptor(21, {0})); + + { + TransformationWrapVectorSynonym transformation1(100, 12, 20, 200, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {0}), MakeDataDescriptor(100, {}))); + } + + { + TransformationWrapVectorSynonym transformation2(101, 12, 20, 201, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {0}), MakeDataDescriptor(101, {}))); + } + + { + TransformationWrapVectorSynonym transformation3(102, 20, 21, 202, 0); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {0}), MakeDataDescriptor(102, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 2 + %18 = OpConstant %14 3 + %19 = OpConstant %14 0 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpConstantComposite %15 %19 %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %200 = OpIAdd %15 %12 %20 + %100 = OpIAdd %14 %10 %18 + %201 = OpIAdd %7 %12 %20 + %101 = OpIAdd %6 %10 %18 + %202 = OpIAdd %7 %20 %21 + %102 = OpIAdd %6 %18 %19 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationWrapVectorSynonym, SignednessDoesNotMatchResultType) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpConstantComposite %7 %11 %10 + %14 = OpTypeInt 32 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpIAdd %14 %10 %11 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(13, {0})); + + ASSERT_FALSE(TransformationWrapVectorSynonym(100, 12, 13, 200, 0) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzzers/BUILD.gn b/third_party/spirv-tools/test/fuzzers/BUILD.gn index ec09b2b067..2aef4e8f22 100644 --- a/third_party/spirv-tools/test/fuzzers/BUILD.gn +++ b/third_party/spirv-tools/test/fuzzers/BUILD.gn @@ -48,6 +48,7 @@ template("spvtools_fuzzer") { source_set(target_name) { testonly = true sources = invoker.sources + sources += [ "random_generator.cpp" ] deps = [ "../..:spvtools", "../..:spvtools_opt", diff --git a/third_party/spirv-tools/test/fuzzers/CMakeLists.txt b/third_party/spirv-tools/test/fuzzers/CMakeLists.txt new file mode 100644 index 0000000000..50c458957e --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright (c) 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function(add_spvtools_libfuzzer_target) + set(one_value_args TARGET) + set(multi_value_args SRCS LIBS) + cmake_parse_arguments( + ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + add_executable(${ARG_TARGET} ${ARG_SRCS}) + spvtools_default_compile_options(${ARG_TARGET}) + target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS}) + target_include_directories(${ARG_TARGET} PRIVATE + ${spirv-tools_SOURCE_DIR} + ${spirv-tools_BINARY_DIR} + ) + set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools libFuzzer targets") + if(NOT ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS} STREQUAL "") + # This is set when the fuzzers are being built by OSS-Fuzz. In this case the + # variable provides the necessary linker flags, and OSS-Fuzz will take care + # of passing suitable compiler flags. + target_link_options(${ARG_TARGET} PRIVATE ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS}) + else() + # When the fuzzers are being built outside of OSS-Fuzz, standard libFuzzer + # arguments to enable fuzzing are used. + target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer") + endif() +endfunction() + +if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) + if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + message(FATAL_ERROR "The libFuzzer targets are only supported with the Clang compiler. Compiler '${CMAKE_CXX_COMPILER_ID}' is not supported!") + endif() + add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + if (${SPIRV_BUILD_FUZZER}) + add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY}) + endif() +endif() diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_000.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_000.spv new file mode 100644 index 0000000000..c203cec416 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_000.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_001.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_001.spv new file mode 100644 index 0000000000..c3dfbd9a07 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_001.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_002.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_002.spv new file mode 100644 index 0000000000..5a3c145f4f Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_002.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_003.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_003.spv new file mode 100644 index 0000000000..c0e0a298b2 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_003.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_004.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_004.spv new file mode 100644 index 0000000000..f1afc72502 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_004.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_005.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_005.spv new file mode 100644 index 0000000000..c6bf437de8 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_005.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_006.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_006.spv new file mode 100644 index 0000000000..d0d4a70056 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_006.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_007.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_007.spv new file mode 100644 index 0000000000..7f2f982d28 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_007.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_008.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_008.spv new file mode 100644 index 0000000000..c67dc97618 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_008.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_009.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_009.spv new file mode 100644 index 0000000000..68740b39c0 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_009.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_010.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_010.spv new file mode 100644 index 0000000000..fedc8b4c46 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_010.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_011.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_011.spv new file mode 100644 index 0000000000..882bfe6bf0 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_011.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_012.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_012.spv new file mode 100644 index 0000000000..bd3c114430 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_012.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_013.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_013.spv new file mode 100644 index 0000000000..d7e35b4b00 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_013.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_014.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_014.spv new file mode 100644 index 0000000000..abf5488963 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_014.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_015.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_015.spv new file mode 100644 index 0000000000..868ab0468d Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_015.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_016.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_016.spv new file mode 100644 index 0000000000..a7cef28a15 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_016.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_017.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_017.spv new file mode 100644 index 0000000000..6d338a91eb Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_017.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_018.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_018.spv new file mode 100644 index 0000000000..c7bf440c2e Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_018.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_019.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_019.spv new file mode 100644 index 0000000000..99d7e2dfc3 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_019.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_020.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_020.spv new file mode 100644 index 0000000000..9e241246ca Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_020.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_021.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_021.spv new file mode 100644 index 0000000000..02734af1ce Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_021.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_022.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_022.spv new file mode 100644 index 0000000000..cd9ab76b4d Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_022.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_023.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_023.spv new file mode 100644 index 0000000000..d0ec5d3161 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_023.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_024.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_024.spv new file mode 100644 index 0000000000..503627f621 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_024.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_025.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_025.spv new file mode 100644 index 0000000000..c4e0aa359f Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_025.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_026.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_026.spv new file mode 100644 index 0000000000..c5b3cedd33 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_026.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_027.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_027.spv new file mode 100644 index 0000000000..626e606f00 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_027.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_028.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_028.spv new file mode 100644 index 0000000000..a9a4357791 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_028.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_029.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_029.spv new file mode 100644 index 0000000000..18e49a5ad4 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_029.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_030.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_030.spv new file mode 100644 index 0000000000..ac4ea364a1 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_030.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_031.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_031.spv new file mode 100644 index 0000000000..2f8eff690e Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_031.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_032.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_032.spv new file mode 100644 index 0000000000..a5808ecf65 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_032.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_033.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_033.spv new file mode 100644 index 0000000000..98cf02e296 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_033.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_034.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_034.spv new file mode 100644 index 0000000000..306fd84511 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_034.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_035.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_035.spv new file mode 100644 index 0000000000..7ed19e09f6 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_035.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_036.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_036.spv new file mode 100644 index 0000000000..22e6783904 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_036.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_037.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_037.spv new file mode 100644 index 0000000000..19fbd20f01 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_037.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_038.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_038.spv new file mode 100644 index 0000000000..7bc5489080 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_038.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_039.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_039.spv new file mode 100644 index 0000000000..01b908defc Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_039.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_040.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_040.spv new file mode 100644 index 0000000000..bd131571f2 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_040.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_041.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_041.spv new file mode 100644 index 0000000000..6e355c204f Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_041.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_042.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_042.spv new file mode 100644 index 0000000000..d356cb19d7 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_042.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_043.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_043.spv new file mode 100644 index 0000000000..49c87ed4d4 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_043.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_044.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_044.spv new file mode 100644 index 0000000000..c7e36d5ccb Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_044.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_045.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_045.spv new file mode 100644 index 0000000000..ad1972636e Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_045.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_046.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_046.spv new file mode 100644 index 0000000000..3b3678ada6 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_046.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_047.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_047.spv new file mode 100644 index 0000000000..7253247b2b Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_047.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_048.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_048.spv new file mode 100644 index 0000000000..5c05c1c068 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_048.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_049.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_049.spv new file mode 100644 index 0000000000..22bc9c21ab Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_049.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_050.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_050.spv new file mode 100644 index 0000000000..8f847880b4 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_050.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_051.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_051.spv new file mode 100644 index 0000000000..727c64d2d6 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_051.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_052.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_052.spv new file mode 100644 index 0000000000..d9ba741800 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_052.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_053.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_053.spv new file mode 100644 index 0000000000..d827004963 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_053.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_054.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_054.spv new file mode 100644 index 0000000000..a3aec7b62b Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_054.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_055.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_055.spv new file mode 100644 index 0000000000..2da1375f5c Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_055.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_056.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_056.spv new file mode 100644 index 0000000000..515d4f1c58 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_056.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_057.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_057.spv new file mode 100644 index 0000000000..2a0f4897c3 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_057.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_058.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_058.spv new file mode 100644 index 0000000000..3211ec203c Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_058.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_059.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_059.spv new file mode 100644 index 0000000000..89e34ecfe7 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_059.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_060.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_060.spv new file mode 100644 index 0000000000..d1fdaeccda Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_060.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_061.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_061.spv new file mode 100644 index 0000000000..de10f68b28 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_061.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_062.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_062.spv new file mode 100644 index 0000000000..503304c3ae Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_062.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_063.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_063.spv new file mode 100644 index 0000000000..75d9b4c47b Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_063.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_064.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_064.spv new file mode 100644 index 0000000000..0b902cb2c9 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_064.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_065.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_065.spv new file mode 100644 index 0000000000..437e45b06e Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_065.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_066.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_066.spv new file mode 100644 index 0000000000..5585a6f8d4 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_066.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_067.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_067.spv new file mode 100644 index 0000000000..763fd34f60 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_067.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_068.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_068.spv new file mode 100644 index 0000000000..1ad0bd5070 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_068.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_069.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_069.spv new file mode 100644 index 0000000000..f1e7950b63 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_069.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_070.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_070.spv new file mode 100644 index 0000000000..47f4c82909 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_070.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_071.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_071.spv new file mode 100644 index 0000000000..3eac74aa43 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_071.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_072.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_072.spv new file mode 100644 index 0000000000..e24611579c Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_072.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_073.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_073.spv new file mode 100644 index 0000000000..b7e915be4a Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_073.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_074.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_074.spv new file mode 100644 index 0000000000..3588757811 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_074.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_075.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_075.spv new file mode 100644 index 0000000000..d3dc46dc32 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_075.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_076.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_076.spv new file mode 100644 index 0000000000..11bee6843b Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_076.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_077.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_077.spv new file mode 100644 index 0000000000..d1b8c76a95 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_077.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_078.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_078.spv new file mode 100644 index 0000000000..4739a9ac4e Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_078.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_079.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_079.spv new file mode 100644 index 0000000000..6553ecf4f1 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_079.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_080.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_080.spv new file mode 100644 index 0000000000..802375b404 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_080.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_081.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_081.spv new file mode 100644 index 0000000000..72d1027f07 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_081.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_082.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_082.spv new file mode 100644 index 0000000000..619c742c4c Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_082.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_083.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_083.spv new file mode 100644 index 0000000000..fa27c33a17 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_083.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_084.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_084.spv new file mode 100644 index 0000000000..14f7bfddae Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_084.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_085.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_085.spv new file mode 100644 index 0000000000..5f5fc03dac Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_085.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_086.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_086.spv new file mode 100644 index 0000000000..a3119927b8 Binary files /dev/null and b/third_party/spirv-tools/test/fuzzers/corpora/spv/graphicsfuzz_086.spv differ diff --git a/third_party/spirv-tools/test/fuzzers/random_generator.cpp b/third_party/spirv-tools/test/fuzzers/random_generator.cpp new file mode 100644 index 0000000000..801a9ffed2 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/random_generator.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/fuzzers/random_generator.h" + +#include +#include +#include + +namespace spvtools { +namespace fuzzers { + +namespace { +/// Generate integer from uniform distribution +/// @tparam I - integer type +/// @param engine - random number engine to use +/// @param lower - Lower bound of integer generated +/// @param upper - Upper bound of integer generated +/// @returns i, where lower <= i < upper +template +I RandomUInt(std::mt19937_64* engine, I lower, I upper) { + assert(lower < upper && "|lower| must be stictly less than |upper|"); + return std::uniform_int_distribution(lower, upper - 1)(*engine); +} + +/// Helper for obtaining a seed bias value for HashCombine with a bit-width +/// dependent on the size of size_t. +template +struct HashCombineOffset {}; +/// Specialization of HashCombineOffset for size_t == 4. +template <> +struct HashCombineOffset<4> { + /// @returns the seed bias value for HashCombine() + static constexpr inline uint32_t value() { + return 0x9e3779b9; // Fractional portion of Golden Ratio, suggested by + // Linux Kernel and Knuth's Art of Computer Programming + } +}; +/// Specialization of HashCombineOffset for size_t == 8. +template <> +struct HashCombineOffset<8> { + /// @returns the seed bias value for HashCombine() + static constexpr inline uint64_t value() { + return 0x9e3779b97f4a7c16; // Fractional portion of Golden Ratio, suggested + // by Linux Kernel and Knuth's Art of Computer + // Programming + } +}; + +/// HashCombine "hashes" together an existing hash and hashable values. +template +void HashCombine(size_t* hash, const T& value) { + constexpr size_t offset = HashCombineOffset::value(); + *hash ^= std::hash()(value) + offset + (*hash << 6) + (*hash >> 2); +} + +/// Calculate the hash for the contents of a C-style data buffer +/// @param data - pointer to buffer to be hashed +/// @param size - number of elements in buffer +/// @returns hash of the data in the buffer +size_t HashBuffer(const uint8_t* data, const size_t size) { + size_t hash = + static_cast(0xCA8945571519E991); // seed with an arbitrary prime + HashCombine(&hash, size); + for (size_t i = 0; i < size; i++) { + HashCombine(&hash, data[i]); + } + return hash; +} + +} // namespace + +RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {} + +RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) { + RandomGenerator(RandomGenerator::CalculateSeed(data, size)); +} + +spv_target_env RandomGenerator::GetTargetEnv() { + spv_target_env result; + + // Need to check that the generated value isn't for a deprecated target env. + do { + result = static_cast( + RandomUInt(&engine_, 0u, static_cast(SPV_ENV_MAX))); + } while (!spvIsValidEnv(result)); + + return result; +} + +uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) { + return RandomUInt(&engine_, lower, upper); +} + +uint32_t RandomGenerator::GetUInt32(uint32_t bound) { + assert(bound > 0 && "|bound| must be greater than 0"); + return RandomUInt(&engine_, 0u, bound); +} + +uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { + assert(data != nullptr && "|data| must be !nullptr"); + + // Number of bytes we want to skip at the start of data for the hash. + // Fewer bytes may be skipped when `size` is small. + // Has lower precedence than kHashDesiredMinBytes. + static const int64_t kHashDesiredLeadingSkipBytes = 5; + // Minimum number of bytes we want to use in the hash. + // Used for short buffers. + static const int64_t kHashDesiredMinBytes = 4; + // Maximum number of bytes we want to use in the hash. + static const int64_t kHashDesiredMaxBytes = 32; + int64_t size_i64 = static_cast(size); + int64_t hash_begin_i64 = + std::min(kHashDesiredLeadingSkipBytes, + std::max(size_i64 - kHashDesiredMinBytes, 0)); + int64_t hash_end_i64 = + std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64); + size_t hash_begin = static_cast(hash_begin_i64); + size_t hash_size = static_cast(hash_end_i64) - hash_begin; + return HashBuffer(data + hash_begin, hash_size); +} + +} // namespace fuzzers +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzzers/random_generator.h b/third_party/spirv-tools/test/fuzzers/random_generator.h new file mode 100644 index 0000000000..b121fe8c67 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/random_generator.h @@ -0,0 +1,68 @@ +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_FUZZERS_RANDOM_GENERATOR_H_ +#define TEST_FUZZERS_RANDOM_GENERATOR_H_ + +#include +#include + +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace fuzzers { + +/// Pseudo random generator utility class for fuzzing +class RandomGenerator { + public: + /// @brief Initializes the internal engine + /// @param seed - seed value passed to engine + explicit RandomGenerator(uint64_t seed); + + /// @brief Initializes the internal engine + /// @param data - data to calculate the seed from + /// @param size - size of the data + explicit RandomGenerator(const uint8_t* data, size_t size); + + ~RandomGenerator() {} + + /// Calculate a seed value based on a blob of data. + /// Currently hashes bytes near the front of the buffer, after skipping N + /// bytes. + /// @param data - pointer to data to base calculation off of, must be !nullptr + /// @param size - number of elements in |data|, must be > 0 + static uint64_t CalculateSeed(const uint8_t* data, size_t size); + + /// Get random valid target env. + spv_target_env GetTargetEnv(); + + /// Get uint32_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + uint32_t GetUInt32(uint32_t lower, uint32_t upper); + + /// Get uint32_t value from uniform distribution. + /// @param bound - Upper bound of integer generated + /// @returns i, where 0 <= i < bound + uint32_t GetUInt32(uint32_t bound); + + private: + std::mt19937_64 engine_; +}; // class RandomGenerator + +} // namespace fuzzers +} // namespace spvtools + +#endif // TEST_FUZZERS_RANDOM_GENERATOR_UTILS_H_ 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 8cecb05f5d..ba3f5b97d1 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp @@ -18,18 +18,27 @@ #include "source/spirv_target_env.h" #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; + if (size > 0) { + spvtools::fuzzers::RandomGenerator random_gen(data, size); + target_env = random_gen.GetTargetEnv(); + } - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; - - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + const spv_context context = spvContextCreate(target_env); + if (context == nullptr) { + return 0; + } std::vector input; + input.resize(size >> 2); + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } std::vector input_str; size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp index 76ba4d9e95..3a97db4159 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp @@ -16,16 +16,18 @@ #include #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; + if (size < 1) { + return 0; + } - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; - - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + spvtools::fuzzers::RandomGenerator random_gen(data, size); + const spv_context context = spvContextCreate(random_gen.GetTargetEnv()); + if (context == nullptr) { + return 0; + } std::vector input; input.resize(size >> 2); diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp index ca9a52d869..0cb0eff57e 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp @@ -18,16 +18,20 @@ #include "source/spirv_target_env.h" #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < sizeof(spv_target_env) + 1) return 0; + if (size < 4) { + // There are not enough bytes to constitute a binary that can be + // disassembled. + return 0; + } - const spv_context context = - spvContextCreate(*reinterpret_cast(data)); - if (context == nullptr) return 0; - - data += sizeof(spv_target_env); - size -= sizeof(spv_target_env); + spvtools::fuzzers::RandomGenerator random_gen(data, size); + const spv_context context = spvContextCreate(random_gen.GetTargetEnv()); + if (context == nullptr) { + return 0; + } std::vector input; input.resize(size >> 2); diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_fuzz_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_fuzz_fuzzer.cpp new file mode 100644 index 0000000000..d43920cf51 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_fuzz_fuzzer.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/fuzz/fuzzer.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size == 0 || (size % sizeof(uint32_t)) != 0) { + // An empty binary, or a binary whose size is not a multiple of word-size, + // cannot be valid, so can be rejected immediately. + return 0; + } + + std::vector initial_binary(size / sizeof(uint32_t)); + memcpy(initial_binary.data(), data, size); + + spvtools::ValidatorOptions validator_options; + + spvtools::MessageConsumer message_consumer = + [](spv_message_level_t, const char*, const spv_position_t&, const char*) { + }; + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + auto target_env = random_gen.GetTargetEnv(); + std::unique_ptr ir_context; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext( + target_env, message_consumer, initial_binary, validator_options, + &ir_context)) { + // The input is invalid - give up. + return 0; + } + + std::vector donor_suppliers = { + [&initial_binary, message_consumer, target_env, + &validator_options]() -> std::unique_ptr { + std::unique_ptr result; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext( + target_env, message_consumer, initial_binary, validator_options, + &result)) { + // The input was successfully parsed and validated first time around, + // so something is wrong if it is now invalid. + abort(); + } + return result; + }}; + + uint32_t seed = random_gen.GetUInt32(std::numeric_limits::max()); + auto fuzzer_context = spvtools::MakeUnique( + spvtools::MakeUnique(seed), + spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), false); + + auto transformation_context = + spvtools::MakeUnique( + spvtools::MakeUnique(ir_context.get()), + validator_options); + + spvtools::fuzz::Fuzzer fuzzer( + std::move(ir_context), std::move(transformation_context), + std::move(fuzzer_context), message_consumer, donor_suppliers, false, + spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations, true, + validator_options); + fuzzer.Run(0); + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp index b45a98c370..6f4d7e8b74 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); optimizer.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp index 6c3bd6aba8..9c47d7d7af 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); optimizer.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp index 68c7974774..10fac42ac6 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/optimizer.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); optimizer.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp index 5dc4303b45..fd6396cda0 100644 --- a/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp +++ b/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp @@ -16,9 +16,15 @@ #include #include "spirv-tools/libspirv.hpp" +#include "test/fuzzers/random_generator.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3); + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + spvtools::SpirvTools tools(random_gen.GetTargetEnv()); tools.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, const char*) {}); diff --git a/third_party/spirv-tools/test/hex_float_test.cpp b/third_party/spirv-tools/test/hex_float_test.cpp index c422f75638..ffdb8bda61 100644 --- a/third_party/spirv-tools/test/hex_float_test.cpp +++ b/third_party/spirv-tools/test/hex_float_test.cpp @@ -1325,6 +1325,76 @@ TEST(FloatProxy, Lowest) { Eq(std::numeric_limits::lowest())); } +template +struct StreamParseCase { + StreamParseCase(const std::string& lit, bool succ, const std::string& suffix, + T value) + : literal(lit), + expect_success(succ), + expected_suffix(suffix), + expected_value(HexFloat>(value)) {} + + std::string literal; + bool expect_success; + std::string expected_suffix; + HexFloat> expected_value; +}; + +template +std::ostream& operator<<(std::ostream& os, const StreamParseCase& fspc) { + os << "StreamParseCase(" << fspc.literal + << ", expect_succes:" << int(fspc.expect_success) << "," + << fspc.expected_suffix << "," << fspc.expected_value << ")"; + return os; +} + +using FloatStreamParseTest = ::testing::TestWithParam>; + +TEST_P(FloatStreamParseTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0.0f); + // Hex floats must be read with the stream input operator. + input >> parsed_value; + if (GetParam().expect_success) { + EXPECT_FALSE(input.fail()); + std::string suffix; + input >> suffix; + // EXPECT_EQ(suffix, GetParam().expected_suffix); + EXPECT_EQ(parsed_value.value().getAsFloat(), + GetParam().expected_value.value().getAsFloat()); + } else { + EXPECT_TRUE(input.fail()); + } +} + +INSTANTIATE_TEST_SUITE_P( + HexFloatExponentMissingDigits, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + {"0x1.0p1", true, "", 2.0f}, + {"0x1.0p1a", true, "a", 2.0f}, + {"-0x1.0p1f", true, "f", -2.0f}, + {"0x1.0p", false, "", 0.0f}, + {"0x1.0pa", false, "", 0.0f}, + {"0x1.0p!", false, "", 0.0f}, + {"0x1.0p+", false, "", 0.0f}, + {"0x1.0p+a", false, "", 0.0f}, + {"0x1.0p+!", false, "", 0.0f}, + {"0x1.0p-", false, "", 0.0f}, + {"0x1.0p-a", false, "", 0.0f}, + {"0x1.0p-!", false, "", 0.0f}, + {"0x1.0p++", false, "", 0.0f}, + {"0x1.0p+-", false, "", 0.0f}, + {"0x1.0p-+", false, "", 0.0f}, + {"0x1.0p--", false, "", 0.0f}})); + +INSTANTIATE_TEST_SUITE_P( + HexFloatExponentTrailingSign, FloatStreamParseTest, + ::testing::ValuesIn(std::vector>{ + // Don't consume a sign after the binary exponent digits. + {"0x1.0p1", true, "", 2.0f}, + {"0x1.0p1+", true, "+", 2.0f}, + {"0x1.0p1-", true, "-", 2.0f}})); + // TODO(awoloszyn): Add fp16 tests and HexFloatTraits. } // namespace } // namespace utils diff --git a/third_party/spirv-tools/test/lint/CMakeLists.txt b/third_party/spirv-tools/test/lint/CMakeLists.txt new file mode 100644 index 0000000000..09bc6d3994 --- /dev/null +++ b/third_party/spirv-tools/test/lint/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_spvtools_unittest(TARGET lint + SRCS divergence_analysis_test.cpp + LIBS SPIRV-Tools-lint SPIRV-Tools-opt +) diff --git a/third_party/spirv-tools/test/lint/divergence_analysis_test.cpp b/third_party/spirv-tools/test/lint/divergence_analysis_test.cpp new file mode 100644 index 0000000000..36cd32dd0a --- /dev/null +++ b/third_party/spirv-tools/test/lint/divergence_analysis_test.cpp @@ -0,0 +1,700 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/lint/divergence_analysis.h" + +#include + +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace lint { +namespace { + +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + +class DivergenceTest : public ::testing::Test { + protected: + std::unique_ptr context_; + std::unique_ptr divergence_; + + void Build(std::string text, uint32_t function_id = 1) { + context_ = BuildModule(SPV_ENV_UNIVERSAL_1_0, CLIMessageConsumer, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context_.get()); + opt::Module* module = context_->module(); + ASSERT_NE(nullptr, module); + // First function should have the given ID. + ASSERT_NE(module->begin(), module->end()); + opt::Function* function = &*module->begin(); + ASSERT_EQ(function->result_id(), function_id); + divergence_.reset(new DivergenceAnalysis(*context_)); + divergence_->Run(function); + } +}; + +// Makes assertions a bit shorter. +using Level = DivergenceAnalysis::DivergenceLevel; + +namespace { +std::string Preamble() { + return R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" %x %y + OpExecutionMode %1 OriginLowerLeft + OpDecorate %y Flat + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %bool = OpTypeBool + %float = OpTypeFloat 32 + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %zero = OpConstant %float 0 + %one = OpConstant %float 1 + %x_t = OpTypePointer Input %float + %x = OpVariable %x_t Input + %y = OpVariable %x_t Input + %1 = OpFunction %void None %void_f + )"; +} +} // namespace + +TEST_F(DivergenceTest, SimpleTest) { + // pseudocode: + // %10: + // %11 = load x + // if (%12 = (%11 < 0)) { + // %13: + // // do nothing + // } + // %14: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )")); + // Control flow divergence. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(13)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + // Value divergence. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); +} + +TEST_F(DivergenceTest, FlowTypesTest) { + // pseudocode: + // %10: + // %11 = load x + // %12 = x < 0 // data -> data + // if (%12) { + // %13: // data -> control + // if (true) { + // %14: // control -> control + // } + // %15: + // %16 = 1 + // } else { + // %17: + // %18 = 2 + // } + // %19: + // %19 = phi(%16 from %15, %18 from %17) // control -> data + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %19 None + OpBranchConditional %12 %13 %17 + %13 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %true %14 %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %16 = OpFAdd %float %zero %zero + OpBranch %19 + %17 = OpLabel + %18 = OpFAdd %float %zero %one + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %float %16 %15 %18 %17 + OpReturn + OpFunctionEnd + )")); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(13)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(13)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(13, divergence_->GetDivergenceSource(14)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(15)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(17)); + EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(17)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(20)); + EXPECT_TRUE(divergence_->GetDivergenceSource(20) == 15 || + divergence_->GetDivergenceDependenceSource(20) == 17) + << "Got: " << divergence_->GetDivergenceDependenceSource(20); +} + +TEST_F(DivergenceTest, ExitDependenceTest) { + // pseudocode: + // %10: + // %11 = load x + // %12 = %11 < 0 + // %13: + // do { + // %14: + // if (%12) { + // %15: + // continue; + // } + // %16: + // %17: + // continue; + // } %18: while(false); + // %19: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero ; data -> data + OpBranch %13 + %13 = OpLabel + OpLoopMerge %19 %18 None + OpBranch %14 + %14 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %12 %15 %16 + %15 = OpLabel + OpBranch %18 ; continue + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 ; continue + %18 = OpLabel + OpBranchConditional %false %13 %19 + %19 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(0, divergence_->GetDivergenceSource(11)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(11, divergence_->GetDivergenceSource(12)); + + // Since both branches continue, there's no divergent control dependence + // to 13. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(15)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(15)); + + // These two blocks are outside the if but are still control dependent. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(16)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(16)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(12, divergence_->GetDivergenceSource(17)); + EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(17)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18)); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19)); +} + +TEST_F(DivergenceTest, ReconvergencePromotionTest) { + // pseudocode: + // %10: + // %11 = load y + // %12 = %11 < 0 + // if (%12) { + // %13: + // %14: + // %15: + // if (true) { + // %16: + // } + // // Reconvergence *not* guaranteed as + // // control is not uniform on the IG level + // // at %15. + // %17: + // %18: + // %19: + // %20 = load x + // } + // %21: + // %22 = phi(%11, %20) + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %y + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %21 None + OpBranchConditional %12 %13 %21 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %true %16 %17 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpLoad %float %y + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %float %11 %10 %20 %19 + OpReturn + OpFunctionEnd + )")); + ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(21)); + + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11)); + ASSERT_EQ(0, divergence_->GetDivergenceSource(11)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12)); + ASSERT_EQ(11, divergence_->GetDivergenceSource(12)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(13)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(13)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(14)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(14)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(15)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16)); + ASSERT_EQ(15, divergence_->GetDivergenceSource(16)); + + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(17)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(18)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(18)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(19)); + ASSERT_EQ(12, divergence_->GetDivergenceSource(19)); + + ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(20)); + ASSERT_EQ(0, divergence_->GetDivergenceSource(20)); + ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(22)); + ASSERT_EQ(19, divergence_->GetDivergenceSource(22)); + ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15)); +} + +TEST_F(DivergenceTest, FunctionCallTest) { + // pseudocode: + // %2() { + // %20: + // %21 = load x + // %22 = %21 < 0 + // if (%22) { + // %23: + // return + // } + // %24: + // return + // } + // + // main() { + // %10: + // %11 = %2(); + // // Reconvergence *not* guaranteed. + // %12: + // return + // } + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpFunctionCall %void %2 + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + + %2 = OpFunction %void None %void_f + %20 = OpLabel + %21 = OpLoad %float %x + %22 = OpFOrdLessThan %bool %21 %zero + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpReturn + %24 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + // Conservatively assume function return value is uniform. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11)); + // TODO(dongja): blocks reachable from diverging function calls should be + // divergent. + // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); // Wrong! +} + +TEST_F(DivergenceTest, LateMergeTest) { + // pseudocode: + // %10: + // %11 = load y + // %12 = %11 < 0 + // [merge: %15] + // if (%12) { + // %13: + // } + // %14: // Reconvergence hasn't happened by here. + // %15: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %11 = OpLoad %float %x + %12 = OpFOrdLessThan %bool %11 %zero + OpSelectionMerge %15 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + // TODO(dongja): + // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); // Wrong! + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15)); +} + +// The following series of tests makes sure that we find the least fixpoint. +TEST_F(DivergenceTest, UniformFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < 1 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %one + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, PartiallyUniformFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %21 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %21 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, DivergentFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %11: + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %20 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %20 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, DivergentOverridesPartiallyUniformTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // %11: + // do { + // %12: + // %13 = phi(%21 from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %20 + // } %16: while (%15) + // %17: + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %10 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %20 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )")); + + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); +} + +TEST_F(DivergenceTest, NestedFixpointTest) { + // pseudocode: + // %10: + // %20 = load x + // %21 = load y + // do { + // %22: + // %23: + // %24 = phi(%zero from %22, %25 from %26) + // %11: + // do { + // %12: + // %13 = phi(%zero from %11, %14 from %16) + // %14 = %13 + 1 + // %15 = %13 < %24 + // } %16: while (%15) + // %17: + // %25 = load x + // } %26: while (false) + // %27: + // return + ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"( + %10 = OpLabel + %20 = OpLoad %float %x + %21 = OpLoad %float %y + OpBranch %22 + %22 = OpLabel + %24 = OpPhi %float %zero %10 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %23 + %23 = OpLabel + OpBranch %11 + %11 = OpLabel + %13 = OpPhi %float %zero %23 %14 %16 + OpLoopMerge %17 %16 None + OpBranch %12 + %12 = OpLabel + %14 = OpFAdd %float %13 %one + %15 = OpFOrdLessThan %bool %13 %24 + OpBranch %16 + %16 = OpLabel + OpBranchConditional %15 %11 %17 + %17 = OpLabel + %25 = OpLoad %float %x + OpBranch %26 + %26 = OpLabel + OpBranchConditional %false %22 %27 + %27 = OpLabel + OpReturn + OpFunctionEnd + )")); + // This test makes sure that divergent values flowing upward can influence the + // fixpoint of a loop. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16)); + // Control of the outer loop is still uniform. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(22)); + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(23)); + // Seed divergent values. + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(24)); + EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(25)); + // Outer loop control. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(26)); + // Merged. + EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(27)); +} + +} // namespace +} // namespace lint +} // namespace spvtools diff --git a/third_party/spirv-tools/test/operand_capabilities_test.cpp b/third_party/spirv-tools/test/operand_capabilities_test.cpp index 6f83dfeb8e..bc0ee05519 100644 --- a/third_party/spirv-tools/test/operand_capabilities_test.cpp +++ b/third_party/spirv-tools/test/operand_capabilities_test.cpp @@ -368,19 +368,6 @@ INSTANTIATE_TEST_SUITE_P( // clang-format on }))); -// See SPIR-V Section 3.15 FP Fast Math Mode -INSTANTIATE_TEST_SUITE_P( - FPFastMathMode, EnumCapabilityTest, - Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), - ValuesIn(std::vector{ - CASE0(FP_FAST_MATH_MODE, FPFastMathModeMaskNone), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotNaNMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotInfMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeNSZMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeAllowRecipMask, Kernel), - CASE1(FP_FAST_MATH_MODE, FPFastMathModeFastMask, Kernel), - }))); - // See SPIR-V Section 3.17 Linkage Type INSTANTIATE_TEST_SUITE_P( LinkageType, EnumCapabilityTest, diff --git a/third_party/spirv-tools/test/opt/CMakeLists.txt b/third_party/spirv-tools/test/opt/CMakeLists.txt index 621a6aa46d..bc44e8d6e6 100644 --- a/third_party/spirv-tools/test/opt/CMakeLists.txt +++ b/third_party/spirv-tools/test/opt/CMakeLists.txt @@ -28,8 +28,11 @@ add_spvtools_unittest(TARGET opt compact_ids_test.cpp constants_test.cpp constant_manager_test.cpp + control_dependence.cpp convert_relaxed_to_half_test.cpp + convert_to_sampled_image_test.cpp copy_prop_array_test.cpp + dataflow.cpp dead_branch_elim_test.cpp dead_insert_elim_test.cpp dead_variable_elim_test.cpp @@ -82,6 +85,7 @@ add_spvtools_unittest(TARGET opt remove_unused_interface_variables_test.cpp register_liveness.cpp relax_float_ops_test.cpp + replace_desc_array_access_using_var_index_test.cpp replace_invalid_opc_test.cpp scalar_analysis.cpp scalar_replacement_test.cpp 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 5b4291dacd..f228c8cb24 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 @@ -7063,6 +7063,287 @@ TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) { SinglePassRunAndMatch(text, true); } +TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) { + // Verify that dead local variable tc and store eliminated but all + // in-function NonSemantic Shader debuginfo kept. + + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %7 = OpString "foo.frag" + %8 = OpString "PS_OUTPUT" + %9 = OpString "float" + %10 = OpString "vColor" + %11 = OpString "PS_INPUT" + %12 = OpString "vTextureCoords" + %13 = OpString "@type.2d.image" + %14 = OpString "type.2d.image" + %15 = OpString "Texture2D.TemplateParam" + %16 = OpString "src.MainPs" + %17 = OpString "tc" + %18 = OpString "ps_output" + %19 = OpString "i" + %20 = OpString "@type.sampler" + %21 = OpString "type.sampler" + %22 = OpString "g_sAniso" + %23 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_7 = OpConstant %uint 7 + %uint_8 = OpConstant %uint 8 + %uint_10 = OpConstant %uint 10 + %uint_11 = OpConstant %uint 11 + %uint_12 = OpConstant %uint 12 + %uint_14 = OpConstant %uint 14 + %uint_15 = OpConstant %uint 15 + %uint_16 = OpConstant %uint 16 + %uint_17 = OpConstant %uint 17 + %uint_19 = OpConstant %uint 19 + %uint_20 = OpConstant %uint 20 + %uint_21 = OpConstant %uint 21 + %uint_25 = OpConstant %uint 25 + %uint_29 = OpConstant %uint 29 + %uint_30 = OpConstant %uint 30 + %uint_35 = OpConstant %uint 35 + %uint_41 = OpConstant %uint 41 + %uint_48 = OpConstant %uint 48 + %uint_53 = OpConstant %uint 53 + %uint_64 = OpConstant %uint 64 + %45 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %51 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %53 = OpExtInst %void %1 DebugOperation %uint_0 + %54 = OpExtInst %void %1 DebugExpression %53 + %55 = OpExtInst %void %1 DebugSource %7 + %56 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %55 %uint_5 + %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0 + %60 = OpExtInst %void %1 DebugTypeVector %59 %uint_4 + %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3 + %57 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %55 %uint_10 %uint_1 %56 %8 %uint_128 %uint_3 %58 + %63 = OpExtInst %void %1 DebugTypeVector %59 %uint_2 + %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3 + %61 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %55 %uint_5 %uint_1 %56 %11 %uint_64 %uint_3 %62 + %64 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %55 %uint_0 %uint_0 %56 %14 %51 %uint_3 + %67 = OpExtInst %void %1 DebugTypeFunction %uint_3 %57 %61 + %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 %uint_15 %uint_1 %56 %16 %uint_3 %uint_16 + %69 = OpExtInst %void %1 DebugLexicalBlock %55 %uint_16 %uint_1 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 %uint_19 %uint_12 %69 %uint_4 + %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 %uint_17 %uint_15 %69 %uint_4 + %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 %uint_15 %uint_29 %68 %uint_4 %uint_1 + %73 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %55 %uint_0 %uint_0 %56 %21 %51 %uint_3 + %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 %uint_3 %uint_14 %56 %22 %g_sAniso %uint_8 + %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 %uint_1 %uint_11 %56 %23 %g_tColor %uint_8 + %MainPs = OpFunction %void None %45 + %76 = OpLabel + %78 = OpVariable %_ptr_Function_PS_OUTPUT Function + %79 = OpVariable %_ptr_Function_v2float Function + %81 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %82 = OpLoad %v2float %in_var_TEXCOORD2 + %83 = OpCompositeConstruct %PS_INPUT %82 + OpStore %param_var_i %83 + %112 = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs + %109 = OpExtInst %void %1 DebugScope %68 + %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 + %110 = OpExtInst %void %1 DebugScope %69 + %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52 + %300 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30 + %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %89 = OpLoad %v2float %88 + %301 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 + OpStore %79 %89 +;CHECK-NOT: OpStore %79 %89 + %302 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35 + %106 = OpExtInst %void %1 DebugValue %70 %89 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52 + %303 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_32 + %91 = OpLoad %type_2d_image %g_tColor + %304 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_41 %uint_48 + %92 = OpLoad %type_sampler %g_sAniso + %305 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_53 + %94 = OpSampledImage %type_sampled_image %91 %92 + %95 = OpImageSampleImplicitLod %v4float %94 %89 None + %306 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_5 %uint_53 + %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 + OpStore %96 %95 + %307 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_12 %uint_20 + %97 = OpLoad %PS_OUTPUT %78 + %308 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_5 %uint_20 + OpStore %81 %97 + %309 = OpExtInst %void %1 DebugNoLine +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine + %111 = OpExtInst %void %1 DebugNoScope +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope + %100 = OpCompositeExtract %v4float %97 0 + OpStore %out_var_SV_Target0 %100 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, ShaderDebugInfoGlobalDCE) { + // Verify that DebugGlobalVariable for eliminated private variable has + // variable operand replaced with DebugInfoNone. + + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %out_var_SV_Target0 %a +OpExecutionMode %MainPs OriginUpperLeft +%5 = OpString "source2.hlsl" +%24 = OpString "float" +%29 = OpString "vColor" +%33 = OpString "PS_OUTPUT" +%37 = OpString "MainPs" +%38 = OpString "" +%42 = OpString "ps_output" +%46 = OpString "a" +OpName %a "a" +OpName %out_var_SV_Target0 "out.var.SV_Target0" +OpName %MainPs "MainPs" +OpName %PS_OUTPUT "PS_OUTPUT" +OpMemberName %PS_OUTPUT 0 "vColor" +OpDecorate %out_var_SV_Target0 Location 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%8 = OpConstantNull %v4float +%float_0 = OpConstant %float 0 +%10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_1 = OpConstant %uint 1 +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%uint_0 = OpConstant %uint 0 +%uint_128 = OpConstant %uint 128 +%uint_12 = OpConstant %uint 12 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_10 = OpConstant %uint 10 +%uint_15 = OpConstant %uint 15 +%48 = OpTypeFunction %void +%PS_OUTPUT = OpTypeStruct %v4float +%54 = OpTypeFunction %PS_OUTPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%a = OpVariable %_ptr_Private_v4float Private +;CHECK-NOT: %a = OpVariable %_ptr_Private_v4float Private +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +;CHECK: [[dbg_none:%\w+]] = OpExtInst %void %1 DebugInfoNone +%18 = OpExtInst %void %1 DebugExpression +%19 = OpExtInst %void %1 DebugSource %5 +%20 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %19 %uint_5 +%25 = OpExtInst %void %1 DebugTypeBasic %24 %uint_32 %uint_3 %uint_0 +%28 = OpExtInst %void %1 DebugTypeVector %25 %uint_4 +%31 = OpExtInst %void %1 DebugTypeMember %29 %28 %19 %uint_5 %uint_12 %uint_0 %uint_128 %uint_3 +%34 = OpExtInst %void %1 DebugTypeComposite %33 %uint_1 %19 %uint_3 %uint_8 %20 %33 %uint_128 %uint_3 %31 +%36 = OpExtInst %void %1 DebugTypeFunction %uint_3 %34 +%39 = OpExtInst %void %1 DebugFunction %37 %36 %19 %uint_8 %uint_1 %20 %38 %uint_3 %uint_9 +%41 = OpExtInst %void %1 DebugLexicalBlock %19 %uint_9 %uint_1 %39 +%43 = OpExtInst %void %1 DebugLocalVariable %42 %34 %19 %uint_10 %uint_15 %41 %uint_4 +%47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 %a %uint_8 +;CHECK: %47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 [[dbg_none]] %uint_8 +%MainPs = OpFunction %void None %48 +%49 = OpLabel +%65 = OpVariable %_ptr_Function_PS_OUTPUT Function +%66 = OpVariable %_ptr_Function_PS_OUTPUT Function +OpStore %a %8 +%72 = OpExtInst %void %1 DebugScope %41 +%69 = OpExtInst %void %1 DebugDeclare %43 %65 %18 +OpLine %5 11 5 +%70 = OpAccessChain %_ptr_Function_v4float %65 %int_0 +OpStore %70 %10 +OpLine %5 12 12 +%71 = OpLoad %PS_OUTPUT %65 +OpLine %5 12 5 +OpStore %66 %71 +%73 = OpExtInst %void %1 DebugNoLine +%74 = OpExtInst %void %1 DebugNoScope +%51 = OpLoad %PS_OUTPUT %66 +%53 = OpCompositeExtract %v4float %51 0 +OpStore %out_var_SV_Target0 %53 +OpLine %5 13 1 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) { // Verify that local variable tc and its store are kept by DebugDeclare. // @@ -7584,11 +7865,178 @@ TEST_F(AggressiveDCETest, KeepDebugScopeParent) { SinglePassRunAndMatch(text, true); } -// TODO(greg-lunarg): Add tests to verify handling of these cases: -// -// Check that logical addressing required -// Check that function calls inhibit optimization -// Others? +TEST_F(AggressiveDCETest, KeepExportFunctions) { + // All functions are reachable. In particular, ExportedFunc and Constant are + // reachable because ExportedFunc is exported. Nothing should be removed. + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Linkage", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %ExportedFunc \"ExportedFunc\"", + "OpName %Live \"Live\"", + "OpDecorate %ExportedFunc LinkageAttributes \"ExportedFunc\" Export", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "OpReturn", + "OpFunctionEnd", +"%ExportedFunc = OpFunction %void None %7", + "%19 = OpLabel", + "%16 = OpFunctionCall %void %Live", + "OpReturn", + "OpFunctionEnd", + "%Live = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::string assembly = JoinAllInsts(text); + auto result = SinglePassRunAndDisassemble( + assembly, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(assembly, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, KeepPrivateVarInExportFunctions) { + // The loads and stores from the private variable should not be removed + // because the functions are exported and could be called. + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %privateVar "privateVar" +OpName %ReadPrivate "ReadPrivate" +OpName %WritePrivate "WritePrivate" +OpName %value "value" +OpDecorate %ReadPrivate LinkageAttributes "ReadPrivate" Export +OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export +%int = OpTypeInt 32 1 +%_ptr_Private_int = OpTypePointer Private %int +%6 = OpTypeFunction %int +%void = OpTypeVoid +%_ptr_Function_int = OpTypePointer Function %int +%10 = OpTypeFunction %void %_ptr_Function_int +%privateVar = OpVariable %_ptr_Private_int Private +%ReadPrivate = OpFunction %int None %6 +%12 = OpLabel +%8 = OpLoad %int %privateVar +OpReturnValue %8 +OpFunctionEnd +%WritePrivate = OpFunction %void None %10 +%value = OpFunctionParameter %_ptr_Function_int +%13 = OpLabel +%14 = OpLoad %int %value +OpStore %privateVar %14 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, KeepLableNames) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %WritePrivate "WritePrivate" +OpName %entry "entry" +OpName %target "target" +OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export +%void = OpTypeVoid +%3 = OpTypeFunction %void +%WritePrivate = OpFunction %void None %3 +%entry = OpLabel +OpBranch %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, PreserveInterface) { + // Set preserve_interface to true. Verify that unused uniform + // constant in entry point interface is not eliminated. + const std::string text = R"(OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationNV %2 "main" %3 %4 +OpDecorate %3 Location 0 +OpDecorate %4 DescriptorSet 2 +OpDecorate %4 Binding 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%float = OpTypeFloat 32 +%_ptr_CallableDataNV_float = OpTypePointer CallableDataNV %float +%3 = OpVariable %_ptr_CallableDataNV_float CallableDataNV +%13 = OpTypeAccelerationStructureKHR +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%4 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%2 = OpFunction %void None %6 +%15 = OpLabel +OpExecuteCallableKHR %uint_0 %3 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false, + /* preserve_interface */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(text, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, EmptyContinueWithConditionalBranch) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%2 = OpFunction %void None %4 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpKill +%12 = OpLabel +OpBranchConditional %false %10 %10 +%11 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt 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 140a5c09ee..6903c4e70f 100644 --- a/third_party/spirv-tools/test/opt/block_merge_test.cpp +++ b/third_party/spirv-tools/test/opt/block_merge_test.cpp @@ -90,6 +90,63 @@ OpFunctionEnd true); } +TEST_F(BlockMergeTest, BlockMergeForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%8 = OpTypeFunction %v4float %_ptr_Function_v4float +%main = OpFunction %v4float None %8 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%9 = OpLoad %v4float %BaseColor +OpStore %v %9 +OpBranch %10 +%10 = OpLabel +%11 = OpLoad %v4float %v +OpBranch %12 +%12 = OpLabel +OpReturnValue %11 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%8 = OpTypeFunction %v4float %_ptr_Function_v4float +%main = OpFunction %v4float None %8 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%9 = OpLoad %v4float %BaseColor +OpStore %v %9 +%11 = OpLoad %v4float %v +OpReturnValue %11 +OpFunctionEnd +)"; + SinglePassRunAndCheck(before, after, true, true); +} + TEST_F(BlockMergeTest, EmptyBlock) { // Note: SPIR-V hand edited to insert empty block // after two statements in main. @@ -1038,6 +1095,112 @@ OpFunctionEnd SinglePassRunAndCheck(spirv, spirv, true, true); } +TEST_F(BlockMergeTest, DebugMerge) { + // Verify merge can be done completely, cleanly and validly in presence of + // NonSemantic.Shader.DebugInfo.100 instructions + const std::string text = R"( +; CHECK: OpLoopMerge +; CHECK-NEXT: OpBranch +; CHECK-NOT: OpBranch +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%5 = OpString "lexblock.hlsl" +%20 = OpString "float" +%32 = OpString "main" +%33 = OpString "" +%46 = OpString "b" +%49 = OpString "a" +%58 = OpString "c" +%63 = OpString "color" +OpName %in_var_COLOR "in.var.COLOR" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpName %main "main" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%9 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%13 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_3 = OpConstant %uint 3 +%uint_0 = OpConstant %uint 0 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_12 = OpConstant %uint 12 +%uint_13 = OpConstant %uint 13 +%uint_20 = OpConstant %uint 20 +%uint_15 = OpConstant %uint 15 +%uint_17 = OpConstant %uint 17 +%uint_16 = OpConstant %uint 16 +%uint_14 = OpConstant %uint 14 +%uint_10 = OpConstant %uint 10 +%65 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%62 = OpExtInst %void %1 DebugExpression +%22 = OpExtInst %void %1 DebugTypeBasic %20 %uint_32 %uint_3 %uint_0 +%25 = OpExtInst %void %1 DebugTypeVector %22 %uint_4 +%27 = OpExtInst %void %1 DebugTypeFunction %uint_3 %25 %25 +%28 = OpExtInst %void %1 DebugSource %5 +%29 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %28 %uint_5 +%34 = OpExtInst %void %1 DebugFunction %32 %27 %28 %uint_12 %uint_1 %29 %33 %uint_3 %uint_13 +%37 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_13 %uint_1 %34 +%52 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_15 %uint_12 %37 +%54 = OpExtInst %void %1 DebugLocalVariable %46 %25 %28 %uint_17 %uint_12 %52 %uint_4 +%56 = OpExtInst %void %1 DebugLocalVariable %49 %25 %28 %uint_16 %uint_12 %52 %uint_4 +%59 = OpExtInst %void %1 DebugLocalVariable %58 %25 %28 %uint_14 %uint_10 %37 %uint_4 +%64 = OpExtInst %void %1 DebugLocalVariable %63 %25 %28 %uint_12 %uint_20 %34 %uint_4 %uint_1 +%main = OpFunction %void None %65 +%66 = OpLabel +%69 = OpLoad %v4float %in_var_COLOR +%168 = OpExtInst %void %1 DebugValue %64 %69 %62 +%169 = OpExtInst %void %1 DebugScope %37 +OpLine %5 14 10 +%164 = OpExtInst %void %1 DebugValue %59 %9 %62 +OpLine %5 15 3 +OpBranch %150 +%150 = OpLabel +%165 = OpPhi %v4float %9 %66 %158 %159 +%167 = OpExtInst %void %1 DebugValue %59 %165 %62 +%170 = OpExtInst %void %1 DebugScope %37 +OpLine %5 15 12 +%171 = OpExtInst %void %1 DebugNoScope +OpLoopMerge %160 %159 None +OpBranch %151 +%151 = OpLabel +OpLine %5 16 12 +%162 = OpExtInst %void %1 DebugValue %56 %9 %62 +OpLine %5 17 12 +%163 = OpExtInst %void %1 DebugValue %54 %13 %62 +OpLine %5 18 15 +%158 = OpFAdd %v4float %165 %13 +OpLine %5 18 5 +%166 = OpExtInst %void %1 DebugValue %59 %158 %62 +%172 = OpExtInst %void %1 DebugScope %37 +OpLine %5 19 3 +OpBranch %159 +%159 = OpLabel +OpLine %5 19 3 +OpBranch %150 +%160 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow diff --git a/third_party/spirv-tools/test/opt/ccp_test.cpp b/third_party/spirv-tools/test/opt/ccp_test.cpp index ef73435335..212e05140b 100644 --- a/third_party/spirv-tools/test/opt/ccp_test.cpp +++ b/third_party/spirv-tools/test/opt/ccp_test.cpp @@ -1208,6 +1208,32 @@ TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) { auto result = SinglePassRunAndMatch(text, true); EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); } + +TEST_F(CCPTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp b/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp index aed14c9deb..5be3ba6384 100644 --- a/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp +++ b/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp @@ -768,6 +768,32 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(CombineAccessChainsTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/control_dependence.cpp b/third_party/spirv-tools/test/opt/control_dependence.cpp new file mode 100644 index 0000000000..4665547222 --- /dev/null +++ b/third_party/spirv-tools/test/opt/control_dependence.cpp @@ -0,0 +1,306 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/control_dependence.h" + +#include +#include + +#include "gmock/gmock-matchers.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/cfg.h" +#include "test/opt/function_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +void GatherEdges(const ControlDependenceAnalysis& cdg, + std::vector& ret) { + cdg.ForEachBlockLabel([&](uint32_t label) { + ret.reserve(ret.size() + cdg.GetDependenceTargets(label).size()); + ret.insert(ret.end(), cdg.GetDependenceTargets(label).begin(), + cdg.GetDependenceTargets(label).end()); + }); + std::sort(ret.begin(), ret.end()); + // Verify that reverse graph is the same. + std::vector reverse_edges; + reverse_edges.reserve(ret.size()); + cdg.ForEachBlockLabel([&](uint32_t label) { + reverse_edges.insert(reverse_edges.end(), + cdg.GetDependenceSources(label).begin(), + cdg.GetDependenceSources(label).end()); + }); + std::sort(reverse_edges.begin(), reverse_edges.end()); + ASSERT_THAT(reverse_edges, testing::ElementsAreArray(ret)); +} + +using ControlDependenceTest = ::testing::Test; + +TEST(ControlDependenceTest, DependenceSimpleCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %13 + %12 = OpLabel + OpBranch %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranchConditional %8 %15 %16 + %15 = OpLabel + OpBranch %19 + %16 = OpLabel + OpBranchConditional %8 %17 %18 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // CFG: (all edges pointing downward) + // %10 + // | + // %11 + // / \ (R: %6 == 1, L: default) + // %12 %13 + // \ / + // %14 + // T/ \F + // %15 %16 + // | T/ |F + // | %17| + // | \ | + // | %18 + // | / + // %19 + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, 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; + const Function* fn = spvtest::GetFunction(module, 1); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + { + PostDominatorAnalysis pdom; + const CFG& cfg = *context->cfg(); + pdom.InitializeTree(cfg, fn); + ControlDependenceAnalysis cdg; + cdg.ComputeControlDependenceGraph(cfg, pdom); + + // Test HasBlock. + for (uint32_t id = 10; id <= 19; id++) { + EXPECT_TRUE(cdg.HasBlock(id)); + } + EXPECT_TRUE(cdg.HasBlock(ControlDependenceAnalysis::kPseudoEntryBlock)); + // Check blocks before/after valid range. + EXPECT_FALSE(cdg.HasBlock(5)); + EXPECT_FALSE(cdg.HasBlock(25)); + EXPECT_FALSE(cdg.HasBlock(UINT32_MAX)); + + // Test ForEachBlockLabel. + std::set block_labels; + cdg.ForEachBlockLabel([&block_labels](uint32_t id) { + bool inserted = block_labels.insert(id).second; + EXPECT_TRUE(inserted); // Should have no duplicates. + }); + EXPECT_THAT(block_labels, testing::ElementsAre(0, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19)); + + { + // Test WhileEachBlockLabel. + uint32_t iters = 0; + EXPECT_TRUE(cdg.WhileEachBlockLabel([&iters](uint32_t) { + ++iters; + return true; + })); + EXPECT_EQ((uint32_t)block_labels.size(), iters); + iters = 0; + EXPECT_FALSE(cdg.WhileEachBlockLabel([&iters](uint32_t) { + ++iters; + return false; + })); + EXPECT_EQ(1, iters); + } + + // Test IsDependent. + EXPECT_TRUE(cdg.IsDependent(12, 11)); + EXPECT_TRUE(cdg.IsDependent(13, 11)); + EXPECT_TRUE(cdg.IsDependent(15, 14)); + EXPECT_TRUE(cdg.IsDependent(16, 14)); + EXPECT_TRUE(cdg.IsDependent(18, 14)); + EXPECT_TRUE(cdg.IsDependent(17, 16)); + EXPECT_TRUE(cdg.IsDependent(10, 0)); + EXPECT_TRUE(cdg.IsDependent(11, 0)); + EXPECT_TRUE(cdg.IsDependent(14, 0)); + EXPECT_TRUE(cdg.IsDependent(19, 0)); + EXPECT_FALSE(cdg.IsDependent(14, 11)); + EXPECT_FALSE(cdg.IsDependent(17, 14)); + EXPECT_FALSE(cdg.IsDependent(19, 14)); + EXPECT_FALSE(cdg.IsDependent(12, 0)); + + // Test GetDependenceSources/Targets. + std::vector edges; + GatherEdges(cdg, edges); + EXPECT_THAT(edges, + testing::ElementsAre( + ControlDependence(0, 10), ControlDependence(0, 11, 10), + ControlDependence(0, 14, 10), ControlDependence(0, 19, 10), + ControlDependence(11, 12), ControlDependence(11, 13), + ControlDependence(14, 15), ControlDependence(14, 16), + ControlDependence(14, 18, 16), ControlDependence(16, 17))); + + const uint32_t expected_condition_ids[] = { + 0, 0, 0, 0, 6, 6, 8, 8, 8, 8, + }; + + for (uint32_t i = 0; i < edges.size(); i++) { + EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); + } + } +} + +TEST(ControlDependenceTest, DependencePaperCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %101 "main" + %102 = OpTypeVoid + %103 = OpTypeFunction %102 + %104 = OpTypeBool + %108 = OpConstantTrue %104 + %101 = OpFunction %102 None %103 + %1 = OpLabel + OpBranch %2 + %2 = OpLabel + OpBranchConditional %108 %3 %7 + %3 = OpLabel + OpBranchConditional %108 %4 %5 + %4 = OpLabel + OpBranch %6 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %108 %10 %11 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %108 %12 %9 + %12 = OpLabel + OpBranchConditional %108 %13 %2 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // CFG: (edges pointing downward if no arrow) + // %1 + // | + // %2 <----+ + // T/ \F | + // %3 \ | + // T/ \F \ | + // %4 %5 %7 | + // \ / / | + // %6 / | + // \ / | + // %8 | + // | | + // %9 <-+ | + // T/ | | | + // %10 | | | + // \ | | | + // %11-F+ | + // T| | + // %12-F---+ + // T| + // %13 + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, 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; + const Function* fn = spvtest::GetFunction(module, 101); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 1); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + { + PostDominatorAnalysis pdom; + const CFG& cfg = *context->cfg(); + pdom.InitializeTree(cfg, fn); + ControlDependenceAnalysis cdg; + cdg.ComputeControlDependenceGraph(cfg, pdom); + + std::vector edges; + GatherEdges(cdg, edges); + EXPECT_THAT( + edges, testing::ElementsAre( + ControlDependence(0, 1), ControlDependence(0, 2, 1), + ControlDependence(0, 8, 1), ControlDependence(0, 9, 1), + ControlDependence(0, 11, 1), ControlDependence(0, 12, 1), + ControlDependence(0, 13, 1), ControlDependence(2, 3), + ControlDependence(2, 6, 3), ControlDependence(2, 7), + ControlDependence(3, 4), ControlDependence(3, 5), + ControlDependence(9, 10), ControlDependence(11, 9), + ControlDependence(11, 11, 9), ControlDependence(12, 2), + ControlDependence(12, 8, 2), ControlDependence(12, 9, 2), + ControlDependence(12, 11, 2), ControlDependence(12, 12, 2))); + + const uint32_t expected_condition_ids[] = { + 0, 0, 0, 0, 0, 0, 0, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + }; + + for (uint32_t i = 0; i < edges.size(); i++) { + EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg)); + } + } +} + +} // namespace +} // namespace opt +} // namespace spvtools 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 ca6ee5838e..6a06de84f7 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 @@ -204,6 +204,98 @@ OpFunctionEnd defs_after + func_after, true, true); } +TEST_F(ConvertToHalfTest, ConvertToHalfForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_cbuff "type.cbuff" +OpMemberName %type_cbuff 0 "c" +OpName %cbuff "cbuff" +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %cbuff DescriptorSet 0 +OpDecorate %cbuff Binding 0 +OpMemberDecorate %type_cbuff 0 Offset 0 +OpDecorate %type_cbuff Block +OpDecorate %18 RelaxedPrecision +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%type_cbuff = OpTypeStruct %float +%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%9 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform +%main = OpFunction %v4float None %9 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +%16 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0 +%17 = OpLoad %float %16 +%18 = OpVectorTimesScalar %v4float %14 %17 +OpStore %v %18 +%19 = OpLoad %v4float %v +OpReturnValue %19 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpCapability Float16 +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_cbuff "type.cbuff" +OpMemberName %type_cbuff 0 "c" +OpName %cbuff "cbuff" +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %cbuff DescriptorSet 0 +OpDecorate %cbuff Binding 0 +OpMemberDecorate %type_cbuff 0 Offset 0 +OpDecorate %type_cbuff Block +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%type_cbuff = OpTypeStruct %float +%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +%main = OpFunction %v4float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +%17 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0 +%18 = OpLoad %float %17 +%22 = OpFConvert %v4half %16 +%23 = OpFConvert %half %18 +%7 = OpVectorTimesScalar %v4half %22 %23 +%24 = OpFConvert %v4float %7 +OpStore %v %24 +%19 = OpLoad %v4float %v +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} TEST_F(ConvertToHalfTest, ConvertToHalfWithDrefSample) { // The resulting SPIR-V was processed with --relax-float-ops. // @@ -1397,6 +1489,87 @@ TEST_F(ConvertToHalfTest, RemoveRelaxDec) { EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); } +TEST_F(ConvertToHalfTest, HandleNonRelaxedPhi) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4452 + + // This test is a case with a non-relaxed phi with a relaxed operand. + // A convert must be inserted at the end of the block associated with + // the operand. + const std::string test = + R"( +; CHECK: [[fcvt:%\w+]] = OpFConvert %v3float {{%\w+}} +; CHECK-NEXT: OpSelectionMerge {{%\w+}} None +; CHECK: {{%\w+}} = OpPhi %v3float [[fcvt]] {{%\w+}} {{%\w+}} {{%\w+}} + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %output_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %MaterialParams "MaterialParams" + OpMemberName %MaterialParams 0 "foo" + OpName %materialParams "materialParams" + OpName %output_color "output_color" + OpMemberDecorate %MaterialParams 0 Offset 0 + OpDecorate %MaterialParams Block + OpDecorate %materialParams DescriptorSet 0 + OpDecorate %materialParams Binding 5 + OpDecorate %output_color Location 0 + OpDecorate %57 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%MaterialParams = OpTypeStruct %float +%_ptr_Uniform_MaterialParams = OpTypePointer Uniform %MaterialParams +%materialParams = OpVariable %_ptr_Uniform_MaterialParams Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%output_color = OpVariable %_ptr_Output_v4float Output + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float_0_5 = OpConstant %float 0.5 + %61 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %main = OpFunction %void None %3 + %5 = OpLabel + %55 = OpAccessChain %_ptr_Uniform_float %materialParams %int_0 + %56 = OpLoad %float %55 + %57 = OpCompositeConstruct %v3float %56 %56 %56 + %31 = OpFOrdGreaterThan %bool %56 %float_0 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + %37 = OpFMul %v3float %57 %61 + OpBranch %33 + %33 = OpLabel + %58 = OpPhi %v3float %57 %5 %37 %32 + %45 = OpAccessChain %_ptr_Output_float %output_color %uint_0 + %46 = OpCompositeExtract %float %58 0 + OpStore %45 %46 + %48 = OpAccessChain %_ptr_Output_float %output_color %uint_1 + %49 = OpCompositeExtract %float %58 1 + OpStore %48 %49 + %51 = OpAccessChain %_ptr_Output_float %output_color %uint_2 + %52 = OpCompositeExtract %float %58 2 + OpStore %51 %52 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndMatch(test, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/convert_to_sampled_image_test.cpp b/third_party/spirv-tools/test/opt/convert_to_sampled_image_test.cpp new file mode 100644 index 0000000000..37f6560109 --- /dev/null +++ b/third_party/spirv-tools/test/opt/convert_to_sampled_image_test.cpp @@ -0,0 +1,353 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/convert_to_sampled_image_pass.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using testing::Eq; +using VectorOfDescriptorSetAndBindingPairs = + std::vector; + +struct DescriptorSetAndBindingStringParsingTestCase { + const char* descriptor_set_binding_str; + bool expect_success; + VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs; +}; + +using DescriptorSetAndBindingStringParsingTest = + ::testing::TestWithParam; + +TEST_P(DescriptorSetAndBindingStringParsingTest, TestCase) { + const auto& tc = GetParam(); + auto actual_descriptor_set_binding_pairs = + ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + tc.descriptor_set_binding_str); + if (tc.expect_success) { + EXPECT_NE(nullptr, actual_descriptor_set_binding_pairs); + if (actual_descriptor_set_binding_pairs) { + EXPECT_THAT(*actual_descriptor_set_binding_pairs, + Eq(tc.expected_descriptor_set_binding_pairs)); + } + } else { + EXPECT_EQ(nullptr, actual_descriptor_set_binding_pairs); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidString, DescriptorSetAndBindingStringParsingTest, + ::testing::ValuesIn(std::vector< + DescriptorSetAndBindingStringParsingTestCase>{ + // 0. empty vector + {"", true, VectorOfDescriptorSetAndBindingPairs({})}, + // 1. one pair + {"100:1024", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 2. two pairs + {"100:1024 200:2048", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{100, 1024}, + DescriptorSetAndBinding{200, 2048}})}, + // 3. spaces between entries + {"100:1024 \n \r \t \v \f 200:2048", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{100, 1024}, + DescriptorSetAndBinding{200, 2048}})}, + // 4. \t, \n, \r and spaces before spec id + {" \n \r\t \t \v \f 100:1024", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 5. \t, \n, \r and spaces after value string + {"100:1024 \n \r\t \t \v \f ", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100, + 1024}})}, + // 6. maximum spec id + {"4294967295:0", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{ + 4294967295, 0}})}, + // 7. minimum spec id + {"0:100", true, + VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0, + 100}})}, + // 8. multiple entries + {"101:1 102:2 103:3 104:4 200:201 9999:1000", true, + VectorOfDescriptorSetAndBindingPairs( + {DescriptorSetAndBinding{101, 1}, DescriptorSetAndBinding{102, 2}, + DescriptorSetAndBinding{103, 3}, DescriptorSetAndBinding{104, 4}, + DescriptorSetAndBinding{200, 201}, + DescriptorSetAndBinding{9999, 1000}})}, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidString, DescriptorSetAndBindingStringParsingTest, + ::testing::ValuesIn( + std::vector{ + // 0. missing default value + {"100:", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 1. descriptor set is not an integer + {"100.0:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 2. descriptor set is not a number + {"something_not_a_number:1", false, + VectorOfDescriptorSetAndBindingPairs{}}, + // 3. only descriptor set number + {"100", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 4. empty descriptor set + {":3", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 5. only colon + {":", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 6. descriptor set overflow + {"4294967296:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 7. descriptor set less than 0 + {"-1:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 8. nullptr + {nullptr, false, VectorOfDescriptorSetAndBindingPairs{}}, + // 9. only a number is invalid + {"1234", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 10. invalid entry separator + {"12:34;23:14", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 11. invalid descriptor set and default value separator + {"12@34", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 12. spaces before colon + {"100 :1024", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 13. spaces after colon + {"100: 1024", false, VectorOfDescriptorSetAndBindingPairs{}}, + // 14. descriptor set represented in hex float format is invalid + {"0x3p10:200", false, VectorOfDescriptorSetAndBindingPairs{}}, + })); + +std::string BuildShader(const char* shader_decorate_instructions, + const char* shader_image_and_sampler_variables, + const char* shader_body) { + // Base HLSL code: + // + // SamplerState sam : register(s2); + // Texture2D texture : register(t5); + // + // float4 main() : SV_TARGET { + // return texture.SampleLevel(sam, float2(1, 2), 10, 2); + // } + std::stringstream ss; + ss << R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %type_2d_image "type.2d.image" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %out_var_SV_TARGET Location 0 + )"; + ss << shader_decorate_instructions; + ss << R"( + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %v2float = OpTypeVector %float 2 + %12 = OpConstantComposite %v2float %float_1 %float_2 + %float_10 = OpConstant %float 10 + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %v2int = OpTypeVector %int 2 + %17 = OpConstantComposite %v2int %int_2 %int_2 +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + )"; + ss << shader_image_and_sampler_variables; + ss << R"( +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %23 + %24 = OpLabel + )"; + ss << shader_body; + ss << R"( + OpReturn + OpFunctionEnd + )"; + return ss.str(); +} + +using ConvertToSampledImageTest = PassTest<::testing::Test>; + +TEST_F(ConvertToSampledImageTest, Texture2DAndSamplerToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 5 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + ; CHECK-NOT: OpVariable %_ptr_UniformConstant_type_2d_image + + ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]] + ; CHECK: OpImageSampleExplicitLod %v4float [[load]] + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunAndMatch( + shader, /* do_validate = */ true, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(ConvertToSampledImageTest, Texture2DToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]] + ; CHECK: [[image_extraction:%\w+]] = OpImage %type_2d_image [[load]] + ; CHECK: OpSampledImage %type_sampled_image [[image_extraction]] + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunAndMatch( + shader, /* do_validate = */ true, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(ConvertToSampledImageTest, SamplerToSampledImage) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +TEST_F(ConvertToSampledImageTest, TwoImagesWithDuplicatedDescriptorSetBinding) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam DescriptorSet 0 + OpDecorate %sam Binding 2 + OpDecorate %texture0 DescriptorSet 0 + OpDecorate %texture0 Binding 5 + OpDecorate %texture1 DescriptorSet 0 + OpDecorate %texture1 Binding 5 + )", + R"( + %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture0 + %26 = OpLoad %type_sampler %sam + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +TEST_F(ConvertToSampledImageTest, + TwoSamplersWithDuplicatedDescriptorSetBinding) { + const std::string shader = BuildShader( + R"( + OpDecorate %sam0 DescriptorSet 0 + OpDecorate %sam0 Binding 2 + OpDecorate %sam1 DescriptorSet 0 + OpDecorate %sam1 Binding 2 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 5 + )", + R"( + %sam0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %sam1 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + )", + R"( + %25 = OpLoad %type_2d_image %texture + %26 = OpLoad %type_sampler %sam0 + %27 = OpSampledImage %type_sampled_image %25 %26 + %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17 + OpStore %out_var_SV_TARGET %28 + )"); + + auto result = SinglePassRunToBinary( + shader, /* skip_nop = */ false, + VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}}); + + EXPECT_EQ(std::get<1>(result), Pass::Status::Failure); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp b/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp index 72bc7f6f69..a4599f0f6d 100644 --- a/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp +++ b/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp @@ -1814,6 +1814,31 @@ OpFunctionEnd SinglePassRunAndMatch(before, false); } +TEST_F(CopyPropArrayPassTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dataflow.cpp b/third_party/spirv-tools/test/opt/dataflow.cpp new file mode 100644 index 0000000000..4742015a73 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dataflow.cpp @@ -0,0 +1,225 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dataflow.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "opt/function_utils.h" +#include "source/opt/build_module.h" + +namespace spvtools { +namespace opt { +namespace { + +using DataFlowTest = ::testing::Test; + +// Simple analyses for testing: + +// Stores the result IDs of visited instructions in visit order. +struct VisitOrder : public ForwardDataFlowAnalysis { + std::vector visited_result_ids; + + VisitOrder(IRContext& context, LabelPosition label_position) + : ForwardDataFlowAnalysis(context, label_position) {} + + VisitResult Visit(Instruction* inst) override { + if (inst->HasResultId()) { + visited_result_ids.push_back(inst->result_id()); + } + return DataFlowAnalysis::VisitResult::kResultFixed; + } +}; + +// For each block, stores the set of blocks it can be preceded by. +// For example, with the following CFG: +// V-----------. +// -> 11 -> 12 -> 13 -> 15 +// \-> 14 ---^ +// +// The answer is: +// 11: 11, 12, 13 +// 12: 11, 12, 13 +// 13: 11, 12, 13 +// 14: 11, 12, 13 +// 15: 11, 12, 13, 14 +struct BackwardReachability : public ForwardDataFlowAnalysis { + std::map> reachable_from; + + BackwardReachability(IRContext& context) + : ForwardDataFlowAnalysis( + context, ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly) {} + + VisitResult Visit(Instruction* inst) override { + // Conditional branches can be enqueued from labels, so skip them. + if (inst->opcode() != SpvOpLabel) + return DataFlowAnalysis::VisitResult::kResultFixed; + uint32_t id = inst->result_id(); + VisitResult ret = DataFlowAnalysis::VisitResult::kResultFixed; + std::set& precedents = reachable_from[id]; + for (uint32_t pred : context().cfg()->preds(id)) { + bool pred_inserted = precedents.insert(pred).second; + if (pred_inserted) { + ret = DataFlowAnalysis::VisitResult::kResultChanged; + } + for (uint32_t block : reachable_from[pred]) { + bool inserted = precedents.insert(block).second; + if (inserted) { + ret = DataFlowAnalysis::VisitResult::kResultChanged; + } + } + } + return ret; + } + + void InitializeWorklist(Function* function, + bool is_first_iteration) override { + // Since successor function is exact, only need one pass. + if (is_first_iteration) { + ForwardDataFlowAnalysis::InitializeWorklist(function, true); + } + } +}; + +TEST_F(DataFlowTest, ReversePostOrder) { + // Note: labels and IDs are intentionally out of order. + // + // CFG: (order of branches is from bottom to top) + // V-----------. + // -> 50 -> 40 -> 20 -> 60 -> 70 + // \-> 30 ---^ + + // DFS tree with RPO numbering: + // -> 50[0] -> 40[1] -> 20[2] 60[4] -> 70[5] + // \-> 30[3] ---^ + + 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 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %6 = OpTypeBool + %5 = OpConstantTrue %6 + %2 = OpFunction %3 None %4 + %50 = OpLabel + %51 = OpUndef %6 + %52 = OpUndef %6 + OpBranch %40 + %70 = OpLabel + %69 = OpUndef %6 + OpReturn + %60 = OpLabel + %61 = OpUndef %6 + OpBranchConditional %5 %70 %40 + %30 = OpLabel + %29 = OpUndef %6 + OpBranch %60 + %20 = OpLabel + %21 = OpUndef %6 + OpBranch %60 + %40 = OpLabel + %39 = OpUndef %6 + OpBranchConditional %5 %30 %20 + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Function* function = spvtest::GetFunction(context->module(), 2); + + std::map> + expected_order; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly] = { + 50, 40, 20, 30, 60, 70, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtBeginning] = { + 50, 51, 52, 40, 39, 20, 21, 30, 29, 60, 61, 70, 69, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtEnd] = { + 51, 52, 50, 39, 40, 21, 20, 29, 30, 61, 60, 69, 70, + }; + expected_order[ForwardDataFlowAnalysis::LabelPosition::kNoLabels] = { + 51, 52, 39, 21, 29, 61, 69, + }; + + for (const auto& test_case : expected_order) { + VisitOrder analysis(*context, test_case.first); + analysis.Run(function); + EXPECT_EQ(test_case.second, analysis.visited_result_ids); + } +} + +TEST_F(DataFlowTest, BackwardReachability) { + // CFG: + // V-----------. + // -> 11 -> 12 -> 13 -> 15 + // \-> 14 ---^ + + 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 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %6 = OpTypeBool + %5 = OpConstantTrue %6 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranchConditional %5 %14 %13 + %13 = OpLabel + OpBranchConditional %5 %15 %11 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Function* function = spvtest::GetFunction(context->module(), 2); + + BackwardReachability analysis(*context); + analysis.Run(function); + + std::map> expected_result; + expected_result[11] = {11, 12, 13}; + expected_result[12] = {11, 12, 13}; + expected_result[13] = {11, 12, 13}; + expected_result[14] = {11, 12, 13}; + expected_result[15] = {11, 12, 13, 14}; + EXPECT_EQ(expected_result, analysis.reachable_from); +} + +} // 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 f89befb496..9c1ef2a317 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 @@ -3403,6 +3403,71 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(DeadBranchElimTest, DontTransferDecorations) { + // When replacing %4 with %14, we don't want %14 to inherit %4's decorations. + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision +; CHECK: [[div:%\w+]] = OpFDiv +; CHECK: {{%\w+}} = OpCopyObject %float [[div]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "STEVEN" + OpDecorate %4 RelaxedPrecision + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %float_1 = OpConstant %float 1 + %uint_0 = OpConstant %uint 0 + %10 = OpTypeFunction %void + %2 = OpFunction %void None %10 + %11 = OpLabel + OpSelectionMerge %12 None + OpSwitch %uint_0 %13 + %13 = OpLabel + %14 = OpFDiv %float %float_1 %float_1 + OpLine %3 0 0 + OpBranch %12 + %15 = OpLabel + OpBranch %12 + %12 = OpLabel + %4 = OpPhi %float %float_1 %15 %14 %13 + %16 = OpCopyObject %float %4 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow diff --git a/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp b/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp index 9ea948a49c..268e659063 100644 --- a/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp +++ b/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp @@ -170,6 +170,72 @@ OpFunctionEnd after_predefs + after, true, true); } +TEST_F(DeadInsertElimTest, DeadInsertForLinkage) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%14 = OpTypeFunction %v2float %_ptr_Function_v2float +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %v2float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v2float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v2float Function +%16 = OpLoad %v2float %v +%17 = OpAccessChain %_ptr_Function_float %BaseColor %int_1 +%18 = OpLoad %float %17 +%19 = OpCompositeInsert %v2float %18 %16 0 +%20 = OpCompositeInsert %v2float %float_0 %19 0 +OpReturnValue %20 +OpFunctionEnd +)"; + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %bb_entry "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%14 = OpTypeFunction %v2float %_ptr_Function_v2float +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %v2float None %14 +%BaseColor = OpFunctionParameter %_ptr_Function_v2float +%bb_entry = OpLabel +%v = OpVariable %_ptr_Function_v2float Function +%16 = OpLoad %v2float %v +%20 = OpCompositeInsert %v2float %float_0 %16 0 +OpReturnValue %20 +OpFunctionEnd +)"; + SinglePassRunAndCheck(before, after, true, true); +} + TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) { // Dead insert eliminated with phi in insertion chain. // diff --git a/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp b/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp index 49407fd580..e87d0beaf7 100644 --- a/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp +++ b/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp @@ -185,6 +185,122 @@ void main(float in_var_color : COLOR) { 100U); } +TEST(DebugInfoManager, CreateDebugInlinedAtWithConstantManager) { + // Show that CreateDebugInlinedAt will use the Constant manager to generate + // its line operand if the Constant and DefUse managers are valid. This is + // proven by checking that the id for the line operand 7 is the same as the + // existing constant 7. + // + // int function1() { + // return 1; + // } + // + // void main() { + // function1(); + // } + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "parent3.hlsl" +%8 = OpString "int" +%19 = OpString "function1" +%20 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_2 = OpConstant %uint 2 +%uint_17 = OpConstant %uint 17 +%uint_6 = OpConstant %uint 6 +%uint_13 = OpConstant %uint 13 +%100 = OpConstant %uint 7 +%31 = OpTypeFunction %void +%42 = OpTypeFunction %int +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%15 = OpExtInst %void %1 DebugSource %3 +%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 +%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2 +%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6 +%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27 +%main = OpFunction %void None %31 +%32 = OpLabel +%33 = OpFunctionCall %void %src_main +OpLine %3 8 1 +OpReturn +OpFunctionEnd +OpLine %3 6 1 +%src_main = OpFunction %void None %31 +OpNoLine +%bb_entry = OpLabel +%47 = OpExtInst %void %1 DebugScope %27 +%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%48 = OpExtInst %void %1 DebugScope %29 +OpLine %3 7 3 +%39 = OpFunctionCall %int %function1 +%49 = OpExtInst %void %1 DebugScope %27 +OpLine %3 8 1 +OpReturn +%50 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +OpLine %3 2 1 +%function1 = OpFunction %int None %42 +OpNoLine +%bb_entry_0 = OpLabel +%51 = OpExtInst %void %1 DebugScope %21 +%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +OpLine %3 3 3 +OpReturnValue %int_1 +%53 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + const uint32_t line_number = 7U; + Instruction line(context.get(), SpvOpLine); + line.SetInOperands({ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}}, + }); + + DebugScope scope(29U, 0U); + + auto db_manager = context.get()->get_debug_info_mgr(); + auto du_manager = context.get()->get_def_use_mgr(); + auto c_manager = context.get()->get_constant_mgr(); + + (void)du_manager; + (void)c_manager; + + uint32_t inlined_at_id = db_manager->CreateDebugInlinedAt(&line, scope); + auto* inlined_at = db_manager->GetDebugInlinedAt(inlined_at_id); + EXPECT_NE(inlined_at, nullptr); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), + 100); +} + TEST(DebugInfoManager, GetDebugInfoNone) { const std::string text = R"( OpCapability Shader diff --git a/third_party/spirv-tools/test/opt/desc_sroa_test.cpp b/third_party/spirv-tools/test/opt/desc_sroa_test.cpp index b35ad474da..dcb625d6d4 100644 --- a/third_party/spirv-tools/test/opt/desc_sroa_test.cpp +++ b/third_party/spirv-tools/test/opt/desc_sroa_test.cpp @@ -770,6 +770,69 @@ TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) { SinglePassRunAndMatch(shader, true); } +TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) { + // Check that an OpMemberDecorate instruction is correctly converted to a + // OpDecorate instruction. + + const std::string shader = R"( +; CHECK: OpDecorate [[t:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[t]] Binding 0 +; CHECK: OpDecorate [[t]] RelaxedPrecision +; CHECK: OpDecorate [[s:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[s]] Binding 1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD %out_var_SV_Target + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + OpName %sampler2D_h "sampler2D_h" + OpMemberName %sampler2D_h 0 "t" + OpMemberName %sampler2D_h 1 "s" + OpName %type_2d_image "type.2d.image" + OpName %type_sampler "type.sampler" + OpName %_MainTex "_MainTex" + OpName %in_var_TEXCOORD "in.var.TEXCOORD" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %PSMain "PSMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %_MainTex DescriptorSet 0 + OpDecorate %_MainTex Binding 0 + OpMemberDecorate %sampler2D_h 0 RelaxedPrecision + OpDecorate %out_var_SV_Target RelaxedPrecision + OpDecorate %69 RelaxedPrecision + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%type_sampler = OpTypeSampler +%sampler2D_h = OpTypeStruct %type_2d_image %type_sampler +%_ptr_UniformConstant_sampler2D_h = OpTypePointer UniformConstant %sampler2D_h + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %35 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + %_MainTex = OpVariable %_ptr_UniformConstant_sampler2D_h UniformConstant +%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %35 + %43 = OpLabel + %44 = OpLoad %v2float %in_var_TEXCOORD + %57 = OpLoad %sampler2D_h %_MainTex + %72 = OpCompositeExtract %type_2d_image %57 0 + %73 = OpCompositeExtract %type_sampler %57 1 + %68 = OpSampledImage %type_sampled_image %72 %73 + %69 = OpImageSampleImplicitLod %v4float %68 %44 None + OpStore %out_var_SV_Target %69 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(shader, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp b/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp index 7728782e70..e277999e44 100644 --- a/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp +++ b/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp @@ -576,7 +576,6 @@ TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateArrayLength) { %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform %main = OpFunction %void None %9 %10 = OpLabel - %11 = OpLoad %type__Globals %_Globals %12 = OpArrayLength %uint %_Globals 2 OpReturn OpFunctionEnd @@ -627,6 +626,67 @@ TEST_F(EliminateDeadMemberTest, KeepMembersOpStore) { EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); } +TEST_F(EliminateDeadMemberTest, KeepStorageBufferMembers) { + // Test that all members of the storage buffer struct %S are kept. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 600 + OpName %type_StructuredBuffer_S "type.StructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "A" + OpMemberName %S 1 "B" + OpName %Buf "Buf" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpDecorateString %out_var_SV_TARGET UserSemantic "SV_TARGET" + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Buf DescriptorSet 0 + OpDecorate %Buf Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpDecorate %_runtimearr_S ArrayStride 32 + OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable + OpDecorate %type_StructuredBuffer_S BufferBlock + OpDecorateString %Buf UserTypeGOOGLE "structuredbuffer" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %S = OpTypeStruct %v4float %v4float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %Buf = OpVariable %_ptr_Uniform_type_StructuredBuffer_S Uniform +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %18 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v4float %Buf %int_0 %uint_0 %int_1 + %22 = OpLoad %v4float %21 + OpStore %out_var_SV_TARGET %22 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemory) { // Test that all members are kept because of an OpCopyMemory. // No change expected. diff --git a/third_party/spirv-tools/test/opt/fold_test.cpp b/third_party/spirv-tools/test/opt/fold_test.cpp index 8457bbfe63..0487e78cc8 100644 --- a/third_party/spirv-tools/test/opt/fold_test.cpp +++ b/third_party/spirv-tools/test/opt/fold_test.cpp @@ -137,6 +137,7 @@ OpName %main "main" %int = OpTypeInt 32 1 %long = OpTypeInt 64 1 %uint = OpTypeInt 32 0 +%ulong = OpTypeInt 64 0 %v2int = OpTypeVector %int 2 %v4int = OpTypeVector %int 4 %v4float = OpTypeVector %float 4 @@ -154,6 +155,7 @@ OpName %main "main" %_ptr_double = OpTypePointer Function %double %_ptr_half = OpTypePointer Function %half %_ptr_long = OpTypePointer Function %long +%_ptr_ulong = OpTypePointer Function %ulong %_ptr_v2int = OpTypePointer Function %v2int %_ptr_v4int = OpTypePointer Function %v4int %_ptr_v4float = OpTypePointer Function %v4float @@ -171,12 +173,23 @@ OpName %main "main" %int_2 = OpConstant %int 2 %int_3 = OpConstant %int 3 %int_4 = OpConstant %int 4 +%int_10 = OpConstant %int 10 +%int_1073741824 = OpConstant %int 1073741824 +%int_n1 = OpConstant %int -1 %int_n24 = OpConstant %int -24 +%int_n858993459 = OpConstant %int -858993459 %int_min = OpConstant %int -2147483648 %int_max = OpConstant %int 2147483647 %long_0 = OpConstant %long 0 +%long_1 = OpConstant %long 1 %long_2 = OpConstant %long 2 %long_3 = OpConstant %long 3 +%long_10 = OpConstant %long 10 +%long_4611686018427387904 = OpConstant %long 4611686018427387904 +%long_n1 = OpConstant %long -1 +%long_n3689348814741910323 = OpConstant %long -3689348814741910323 +%long_min = OpConstant %long -9223372036854775808 +%long_max = OpConstant %long 9223372036854775807 %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 %uint_2 = OpConstant %uint 2 @@ -184,7 +197,13 @@ OpName %main "main" %uint_4 = OpConstant %uint 4 %uint_32 = OpConstant %uint 32 %uint_42 = OpConstant %uint 42 +%uint_2147483649 = OpConstant %uint 2147483649 %uint_max = OpConstant %uint 4294967295 +%ulong_0 = OpConstant %ulong 0 +%ulong_1 = OpConstant %ulong 1 +%ulong_2 = OpConstant %ulong 2 +%ulong_9223372036854775809 = OpConstant %ulong 9223372036854775809 +%ulong_max = OpConstant %ulong 18446744073709551615 %v2int_undef = OpUndef %v2int %v2int_0_0 = OpConstantComposite %v2int %int_0 %int_0 %v2int_1_0 = OpConstantComposite %v2int %int_1 %int_0 @@ -3589,7 +3608,19 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingT "%4 = OpCompositeExtract %int %3 2\n" + "OpReturn\n" + "OpFunctionEnd", - 4, INT_0_ID) + 4, INT_0_ID), + // Test case 15: + // Don't fold extract fed by construct with vector result if the index is + // past the last element. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeConstruct %v2int %int_0 %int_0\n" + + "%3 = OpCompositeConstruct %v4int %2 %100 %int_0\n" + + "%4 = OpCompositeExtract %int %3 4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, 0) )); INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest, @@ -5572,7 +5603,109 @@ INSTANTIATE_TEST_SUITE_P(MergeMulTest, MatchingInstructionFoldingTest, "%5 = OpFMul %float %4 %2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 5, true) + 5, true), + // Test case 25: fold overflowing signed 32 bit imuls + // (x * 1073741824) * 2 = x * int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_1073741824\n" + + "%4 = OpIMul %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 26: fold overflowing signed 64 bit imuls + // (x * 4611686018427387904) * 2 = x * long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_4611686018427387904\n" + + "%4 = OpIMul %long %3 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 27: fold overflowing 32 bit unsigned imuls + // (x * 2147483649) * 2 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[ld]] [[uint_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpIMul %uint %2 %uint_2147483649\n" + + "%4 = OpIMul %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 28: fold overflowing 64 bit unsigned imuls + // (x * 9223372036854775809) * 2 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" + + "; CHECK: [[ulong_2:%\\w+]] = OpConstant [[ulong]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" + + "; CHECK: %4 = OpIMul [[ulong]] [[ld]] [[ulong_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_ulong Function\n" + + "%2 = OpLoad %ulong %var\n" + + "%3 = OpIMul %ulong %2 %ulong_9223372036854775809\n" + + "%4 = OpIMul %ulong %3 %ulong_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 29: fold underflowing signed 32 bit imuls + // (x * (-858993459)) * 10 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_n858993459\n" + + "%4 = OpIMul %int %3 %int_10\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 30: fold underflowing signed 64 bit imuls + // (x * (-3689348814741910323)) * 10 = x * 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_n3689348814741910323\n" + + "%4 = OpIMul %long %3 %long_10\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) )); INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, @@ -5732,15 +5865,11 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "OpReturn\n" + "OpFunctionEnd\n", 4, false), - // Test case 11: merge sdiv of snegate - // (-x) / 2 = x / -2 + // Test case 11: Do not merge sdiv of snegate. If %2 is INT_MIN, then the + // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv. + // Specifically, (-INT_MIN) / 2 != INT_MIN / -2. InstructionFoldingCase( Header() + - "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + - "; CHECK: OpConstant [[int]] -2147483648\n" + - "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + - "; CHECK: %4 = OpSDiv [[int]] [[ld]] [[int_n2]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_int Function\n" + @@ -5749,16 +5878,12 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%4 = OpSDiv %int %3 %int_2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 4, true), - // Test case 12: merge sdiv of snegate - // 2 / (-x) = -2 / x + 4, false), + // Test case 12: Do not merge sdiv of snegate. If %2 is INT_MIN, then the + // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv. + // Specifically, 2 / (-INT_MIN) != -2 / INT_MIN. InstructionFoldingCase( Header() + - "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + - "; CHECK: OpConstant [[int]] -2147483648\n" + - "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + - "; CHECK: %4 = OpSDiv [[int]] [[int_n2]] [[ld]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_int Function\n" + @@ -5767,7 +5892,7 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%4 = OpSDiv %int %int_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", - 4, true), + 4, false), // Test case 13: Don't merge // (x / {null}) / {null} InstructionFoldingCase( @@ -5818,7 +5943,33 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, "%5 = OpFDiv %float %4 %2\n" + "OpReturn\n" + "OpFunctionEnd\n", - 5, true) + 5, true), + // Test case 16: Do not merge udiv of snegate + // (-x) / 2u + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpSNegate %uint %2\n" + + "%4 = OpUDiv %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 17: Do not merge udiv of snegate + // 2u / (-x) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpSNegate %uint %2\n" + + "%4 = OpUDiv %uint %uint_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false) )); INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, @@ -6026,6 +6177,108 @@ INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, "%4 = OpFAdd %float %float_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 12: fold overflowing signed 32 bit iadds + // (x + int_max) + 1 = x + int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_max\n" + + "%4 = OpIAdd %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: fold overflowing signed 64 bit iadds + // (x + long_max) + 1 = x + long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_max\n" + + "%4 = OpIAdd %long %3 %long_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 14: fold overflowing 32 bit unsigned iadds + // (x + uint_max) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: %4 = OpIAdd [[uint]] [[ld]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpIAdd %uint %2 %uint_max\n" + + "%4 = OpIAdd %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: fold overflowing 64 bit unsigned iadds + // (x + ulong_max) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" + + "; CHECK: [[ulong_1:%\\w+]] = OpConstant [[ulong]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" + + "; CHECK: %4 = OpIAdd [[ulong]] [[ld]] [[ulong_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_ulong Function\n" + + "%2 = OpLoad %ulong %var\n" + + "%3 = OpIAdd %ulong %2 %ulong_max\n" + + "%4 = OpIAdd %ulong %3 %ulong_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 16: fold underflowing signed 32 bit iadds + // (x + int_min) + (-1) = x + int_max + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_max:%\\w+]] = OpConstant [[int]] 2147483647\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_max]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_min\n" + + "%4 = OpIAdd %int %3 %int_n1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 17: fold underflowing signed 64 bit iadds + // (x + long_min) + (-1) = x + long_max + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_max:%\\w+]] = OpConstant [[long]] 9223372036854775807\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_max]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_min\n" + + "%4 = OpIAdd %long %3 %long_n1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); @@ -6394,6 +6647,40 @@ INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest, "%4 = OpISub %int %int_2 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 14: fold overflowing signed 32 bit isubs + // (x - int_max) - 1 = x - int_min + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" + + "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[ld]] [[int_min]]\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 %2 %int_max\n" + + "%4 = OpISub %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: fold overflowing signed 64 bit isubs + // (x - long_max) - 1 = x - long_min + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[ld]] [[long_min]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpISub %long %2 %long_max\n" + + "%4 = OpISub %long %3 %long_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); diff --git a/third_party/spirv-tools/test/opt/inline_opaque_test.cpp b/third_party/spirv-tools/test/opt/inline_opaque_test.cpp index 8cb8925cdb..e4db432553 100644 --- a/third_party/spirv-tools/test/opt/inline_opaque_test.cpp +++ b/third_party/spirv-tools/test/opt/inline_opaque_test.cpp @@ -226,6 +226,115 @@ OpFunctionEnd predefs + before + post_defs, predefs + after + post_defs, true, true); } +TEST_F(InlineOpaqueTest, InlineOpaqueForLinkage) { + const std::string predefs_1 = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;" +OpName %s "s" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %main LinkageAttributes "main" Export +)"; + + const std::string name = R"(OpName %return_value "return_value" +)"; + + const std::string predefs_2 = R"(OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%19 = OpTypeSampledImage %18 +%S_t = OpTypeStruct %v2float %v2float %19 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%21 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%_ptr_Function_19 = OpTypePointer Function %19 +%sampler15 = OpVariable %_ptr_UniformConstant_19 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%return_value = OpFunctionCall %void %foo_struct_S_t_vf2_vf21_ %param +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%42 = OpAccessChain %_ptr_Function_19 %param %int_2 +%43 = OpLoad %19 %42 +%44 = OpAccessChain %_ptr_Function_v2float %param %int_0 +%45 = OpLoad %v2float %44 +%46 = OpImageSampleImplicitLod %v4float %43 %45 +OpStore %outColor %46 +OpReturn +OpFunctionEnd +)"; + + const std::string post_defs = + R"(%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %21 +%s = OpFunctionParameter %_ptr_Function_S_t +%35 = OpLabel +%36 = OpAccessChain %_ptr_Function_19 %s %int_2 +%37 = OpLoad %19 %36 +%38 = OpAccessChain %_ptr_Function_v2float %s %int_0 +%39 = OpLoad %v2float %38 +%40 = OpImageSampleImplicitLod %v4float %37 %39 +OpStore %outColor %40 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_1 + name + predefs_2 + before + post_defs, + predefs_1 + predefs_2 + after + post_defs, true, true); +} + TEST_F(InlineOpaqueTest, InlineInNonEntryPointFunction) { // This demonstrates opaque inlining in a function that is not // an entry point function (main2) but is in the call tree of an diff --git a/third_party/spirv-tools/test/opt/inline_test.cpp b/third_party/spirv-tools/test/opt/inline_test.cpp index 2939901338..d22f027c56 100644 --- a/third_party/spirv-tools/test/opt/inline_test.cpp +++ b/third_party/spirv-tools/test/opt/inline_test.cpp @@ -2300,7 +2300,6 @@ TEST_F(InlineTest, DontInlineDirectlyRecursiveFunc) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft -OpDecorate %2 DescriptorSet 439418829 %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2330,7 +2329,6 @@ TEST_F(InlineTest, DontInlineInDirectlyRecursiveFunc) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft -OpDecorate %2 DescriptorSet 439418829 %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2581,6 +2579,132 @@ OpFunctionEnd SinglePassRunAndCheck(before, after, false, true); } +TEST_F(InlineTest, InlineForLinkage) { + const std::string before = + R"(OpCapability SampledBuffer +OpCapability ImageBuffer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_buffer_image "type.buffer.image" +OpName %output "output" +OpName %main "main" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %param_var_color "param.var.color" +OpName %fn "fn" +OpName %color_0 "color" +OpName %bb_entry_0 "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %output DescriptorSet 0 +OpDecorate %output Binding 1 +%float = OpTypeFloat 32 +%float_0_200000003 = OpConstant %float 0.200000003 +%v4float = OpTypeVector %float 4 +%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Function_v4float = OpTypePointer Function %v4float +%11 = OpTypeFunction %float %_ptr_Function_v4float +%_ptr_Function_float = OpTypePointer Function %float +%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %float None %11 +%color = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %color +OpStore %param_var_color %16 +%17 = OpFunctionCall %float %fn %param_var_color +OpReturnValue %17 +OpFunctionEnd +%fn = OpFunction %float None %11 +%color_0 = OpFunctionParameter %_ptr_Function_v4float +%bb_entry_0 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %color_0 +OpStore %v %22 +%23 = OpLoad %v4float %v +%24 = OpFMul %v4float %23 %6 +OpStore %v %24 +%26 = OpAccessChain %_ptr_Function_float %v %int_0 +%27 = OpLoad %float %26 +OpReturnValue %27 +OpFunctionEnd + )"; + + const std::string after = + R"(OpCapability SampledBuffer +OpCapability ImageBuffer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %type_buffer_image "type.buffer.image" +OpName %output "output" +OpName %main "main" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %param_var_color "param.var.color" +OpName %fn "fn" +OpName %color_0 "color" +OpName %bb_entry_0 "bb.entry" +OpName %v "v" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %output DescriptorSet 0 +OpDecorate %output Binding 1 +%float = OpTypeFloat 32 +%float_0_200000003 = OpConstant %float 0.200000003 +%v4float = OpTypeVector %float 4 +%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Function_v4float = OpTypePointer Function %v4float +%11 = OpTypeFunction %float %_ptr_Function_v4float +%_ptr_Function_float = OpTypePointer Function %float +%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %float None %11 +%color = OpFunctionParameter %_ptr_Function_v4float +%bb_entry = OpLabel +%28 = OpVariable %_ptr_Function_v4float Function +%29 = OpVariable %_ptr_Function_float Function +%param_var_color = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %color +OpStore %param_var_color %16 +%31 = OpLoad %v4float %param_var_color +OpStore %28 %31 +%32 = OpLoad %v4float %28 +%33 = OpFMul %v4float %32 %6 +OpStore %28 %33 +%34 = OpAccessChain %_ptr_Function_float %28 %int_0 +%35 = OpLoad %float %34 +OpStore %29 %35 +%17 = OpLoad %float %29 +OpReturnValue %17 +OpFunctionEnd +%fn = OpFunction %float None %11 +%color_0 = OpFunctionParameter %_ptr_Function_v4float +%bb_entry_0 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %color_0 +OpStore %v %22 +%23 = OpLoad %v4float %v +%24 = OpFMul %v4float %23 %6 +OpStore %v %24 +%26 = OpAccessChain %_ptr_Function_float %v %int_0 +%27 = OpLoad %float %26 +OpReturnValue %27 +OpFunctionEnd +)"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, false, true); +} + TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) { const std::string text = R"( @@ -3259,6 +3383,82 @@ TEST_F(InlineTest, DebugSimple) { SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, ShaderDebugSimple) { + // Same as DebugSimple but for NonSemantic.Shader.DebugInfo.100. + const std::string text = R"( +; CHECK: [[main_name:%\d+]] = OpString "main" +; CHECK: [[foo_name:%\d+]] = OpString "foo" +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} %uint_4 %uint_1 {{%\d+}} [[main_name]] %uint_3 %uint_4 +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} [[foo_name]] %uint_3 %uint_1 +; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} %uint_1 %uint_14 [[dbg_foo]] +; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt %uint_4 [[dbg_main]] +; CHECK: [[main:%\d+]] = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]] +; CHECK: [[foo:%\d+]] = OpFunction %v4float None + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %3 %4 + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %6 = OpString "float" + %main_name = OpString "main" + %foo_name = OpString "foo" + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_14 = OpConstant %uint 14 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %v4float = OpTypeVector %float 4 + %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void + %19 = OpTypeFunction %v4float + %3 = OpVariable %_ptr_Input_v4float Input + %4 = OpVariable %_ptr_Output_v4float Output + %20 = OpExtInst %void %1 DebugSource %5 + %21 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %20 %uint_5 + %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_3 %uint_0 + %23 = OpExtInst %void %1 DebugTypeVector %22 %uint_4 + %24 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 %23 + %25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 + %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 %uint_4 %uint_1 %21 %main_name %uint_3 %uint_4 + %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 %uint_1 %uint_1 %21 %foo_name %uint_3 %uint_1 + %29 = OpExtInst %void %1 DebugLexicalBlock %20 %uint_1 %uint_14 %dbg_foo + %main = OpFunction %void None %18 + %30 = OpLabel +%dbg_main_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_main %main + %31 = OpExtInst %void %1 DebugScope %dbg_main + %32 = OpFunctionCall %v4float %foo + %33 = OpLoad %v4float %3 + %34 = OpFAdd %v4float %32 %33 + OpStore %4 %34 + OpReturn + OpFunctionEnd + %foo = OpFunction %v4float None %19 + %36 = OpLabel +%dbg_foo_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_foo %foo + %35 = OpExtInst %void %1 DebugScope %dbg_foo + %37 = OpExtInst %void %1 DebugScope %29 + OpReturnValue %14 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(InlineTest, DebugNested) { // When function main() calls function zoo() and function zoo() calls // function bar() and function bar() calls function foo(), check that @@ -3462,6 +3662,103 @@ float4 main(float4 color : COLOR) : SV_TARGET { SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, ShaderDebugSimpleHLSLPixelShader) { + // Same as DebugSimpleHLSLPixelShader but for + // NonSemantic.Shader.DebugInfo.100. + const std::string text = R"( +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1 +; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_1 %uint_47 [[dbg_main]] +; CHECK: %main = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]] +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_10 %uint_10 +; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_3 %uint_3 +; CHECK: OpFunctionEnd +; CHECK: %src_main = OpFunction %v4float None + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %14 = OpString "#line 1 \"ps.hlsl\" +float4 main(float4 color : COLOR) : SV_TARGET { + return color; +} +" + %17 = OpString "float" + %21 = OpString "src.main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %param_var_color "param.var.color" + OpName %src_main "src.main" + OpName %color "color" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_10 = OpConstant %uint 10 + %uint_20 = OpConstant %uint 20 + %uint_32 = OpConstant %uint 32 + %uint_47 = OpConstant %uint 47 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %33 = OpTypeFunction %v4float %_ptr_Function_v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_3 %uint_0 + %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_4 + %20 = OpExtInst %void %1 DebugTypeFunction %uint_3 %19 %19 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_1 %uint_1 %16 %21 %uint_3 %uint_1 + %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_1 %uint_20 %22 %uint_4 %uint_0 + %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_1 %uint_47 %22 + %main = OpFunction %void None %27 + %28 = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function + %31 = OpLoad %v4float %in_var_COLOR + OpStore %param_var_color %31 + %32 = OpFunctionCall %v4float %src_main %param_var_color + OpStore %out_var_SV_TARGET %32 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %33 + %color = OpFunctionParameter %_ptr_Function_v4float + %bb_entry = OpLabel + %140 = OpExtInst %void %1 DebugFunctionDefinition %22 %src_main + %141 = OpExtInst %void %1 DebugLine %5 %uint_1 %uint_1 %uint_1 %uint_1 + %34 = OpExtInst %void %1 DebugScope %22 + %36 = OpExtInst %void %1 DebugDeclare %25 %color %13 + %38 = OpExtInst %void %1 DebugScope %26 + %142 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_10 %uint_10 + %39 = OpLoad %v4float %color + %143 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_3 %uint_3 + OpReturnValue %39 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) { // Check that InlinePass correctly generates DebugDeclare instructions // for callee function's parameters and maps them to corresponding @@ -3937,6 +4234,105 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(InlineTest, CreateConstantForInlinedAt) { + // This shader causes CreateDebugInlinedAt to generate a constant. + // Using the Constant manager would attempt to build the invalidated + // DefUse manager during inlining which could cause an assert because + // the function is in an inconsistant state. This test verifies that + // CreateDebugInlinedAt detects that the DefUse manager is disabled + // and creates a duplicate constant safely without the Constant manager. + // + // int function1() { + // return 1; + // } + // + // void main() { + // function1(); + // } + + const std::string text = R"(OpCapability Shader +; CHECK: %uint_7 = OpConstant %uint 7 +; CHECK: %uint_7_0 = OpConstant %uint 7 +; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_7_0 +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "parent3.hlsl" +%8 = OpString "int" +%19 = OpString "function1" +%20 = OpString "" +%26 = OpString "main" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %function1 "function1" +OpName %bb_entry_0 "bb.entry" +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%uint_5 = OpConstant %uint 5 +%uint_2 = OpConstant %uint 2 +%uint_17 = OpConstant %uint 17 +%uint_6 = OpConstant %uint 6 +%uint_13 = OpConstant %uint 13 +%uint_7 = OpConstant %uint 7 +%31 = OpTypeFunction %void +%42 = OpTypeFunction %int +%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0 +%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10 +%15 = OpExtInst %void %1 DebugSource %3 +%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5 +%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2 +%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21 +%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6 +%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27 +%main = OpFunction %void None %31 +%32 = OpLabel +%33 = OpFunctionCall %void %src_main +OpLine %3 8 1 +OpReturn +OpFunctionEnd +OpLine %3 6 1 +%src_main = OpFunction %void None %31 +OpNoLine +%bb_entry = OpLabel +%47 = OpExtInst %void %1 DebugScope %27 +%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main +%48 = OpExtInst %void %1 DebugScope %29 +OpLine %3 7 3 +%39 = OpFunctionCall %int %function1 +%49 = OpExtInst %void %1 DebugScope %27 +OpLine %3 8 1 +OpReturn +%50 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +OpLine %3 2 1 +%function1 = OpFunction %int None %42 +OpNoLine +%bb_entry_0 = OpLabel +%51 = OpExtInst %void %1 DebugScope %21 +%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1 +%52 = OpExtInst %void %1 DebugScope %23 +OpLine %3 3 3 +OpReturnValue %int_1 +%53 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Empty modules 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 41ead67a5a..95114b2397 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 @@ -615,6 +615,212 @@ OpFunctionEnd true, 7u, 23u); } +TEST_F(InstBuffAddrTest, StructLoad) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // #extension GL_ARB_gpu_shader_int64 : enable + // struct Test { + // float a; + // }; + // + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer + // TestBuffer { Test test; }; + // + // Test GetTest(uint64_t ptr) { + // return TestBuffer(ptr).test; + // } + // + // void main() { + // GetTest(0xe0000000); + // } + + const std::string defs = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +; CHECK: OpEntryPoint Fragment %main "main" %60 %99 %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_ARB_gpu_shader_int64" +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %Test "Test" +OpMemberName %Test 0 "a" +OpName %Test_0 "Test" +OpMemberName %Test_0 0 "a" +OpName %TestBuffer "TestBuffer" +OpMemberName %TestBuffer 0 "test" +)"; + + const std::string decorates = + R"( +OpMemberDecorate %Test_0 0 Offset 0 +OpMemberDecorate %TestBuffer 0 Offset 0 +OpDecorate %TestBuffer Block +; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 +; CHECK: OpDecorate %_struct_58 Block +; CHECK: OpMemberDecorate %_struct_58 0 Offset 0 +; CHECK: OpDecorate %60 DescriptorSet 7 +; CHECK: OpDecorate %60 Binding 2 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +; CHECK: OpDecorate %_struct_97 Block +; CHECK: OpMemberDecorate %_struct_97 0 Offset 0 +; CHECK: OpMemberDecorate %_struct_97 1 Offset 4 +; CHECK: OpDecorate %99 DescriptorSet 7 +; CHECK: OpDecorate %99 Binding 0 +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string globals = + R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%float = OpTypeFloat 32 +%Test = OpTypeStruct %float +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer +%Test_0 = OpTypeStruct %float +%TestBuffer = OpTypeStruct %Test_0 +%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0 +%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704 +; CHECK: %47 = OpTypeFunction %bool %ulong %uint +; CHECK: %_struct_58 = OpTypeStruct %_runtimearr_ulong +; CHECK: %60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer +; CHECK: %90 = OpTypeFunction %void %uint %uint %uint %uint +; CHECK: %_struct_97 = OpTypeStruct %uint %_runtimearr_uint +; CHECK: %99 = OpVariable %_ptr_StorageBuffer__struct_97 StorageBuffer +; CHECK: %143 = OpConstantNull %Test_0 +)"; + + const std::string main = + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704 +%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0 +%39 = OpLoad %Test_0 %38 Aligned 16 +; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16 +; CHECK: %43 = OpConvertPtrToU %ulong %38 +; CHECK: %80 = OpFunctionCall %bool %45 %43 %uint_4 +; CHECK: OpSelectionMerge %81 None +; CHECK: OpBranchConditional %80 %82 %83 +; CHECK: %82 = OpLabel +; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16 +; CHECK: OpBranch %81 +; CHECK: %83 = OpLabel +; CHECK: %85 = OpUConvert %uint %43 +; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32 +; CHECK: %88 = OpUConvert %uint %87 +; CHECK: %142 = OpFunctionCall %void %89 %uint_37 %uint_2 %85 %88 +; CHECK: OpBranch %81 +; CHECK: %81 = OpLabel +; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83 +%40 = OpCopyLogical %Test %39 +; CHECK-NOT: %40 = OpCopyLogical %Test %39 +; CHECK: %40 = OpCopyLogical %Test %144 +OpReturn +OpFunctionEnd +)"; + + const std::string output_funcs = + R"( +; CHECK: %45 = OpFunction %bool None %47 +; CHECK: %48 = OpFunctionParameter %ulong +; CHECK: %49 = OpFunctionParameter %uint +; CHECK: %50 = OpLabel +; CHECK: OpBranch %51 +; CHECK: %51 = OpLabel +; CHECK: %53 = OpPhi %uint %uint_1 %50 %54 %52 +; CHECK: OpLoopMerge %56 %52 None +; CHECK: OpBranch %52 +; CHECK: %52 = OpLabel +; CHECK: %54 = OpIAdd %uint %53 %uint_1 +; CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %54 +; CHECK: %64 = OpLoad %ulong %63 +; CHECK: %65 = OpUGreaterThan %bool %64 %48 +; CHECK: OpBranchConditional %65 %56 %51 +; CHECK: %56 = OpLabel +; CHECK: %66 = OpISub %uint %54 %uint_1 +; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %66 +; CHECK: %68 = OpLoad %ulong %67 +; CHECK: %69 = OpISub %ulong %48 %68 +; CHECK: %70 = OpUConvert %ulong %49 +; CHECK: %71 = OpIAdd %ulong %69 %70 +; CHECK: %72 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %uint_0 +; CHECK: %73 = OpLoad %ulong %72 +; CHECK: %74 = OpUConvert %uint %73 +; CHECK: %75 = OpISub %uint %66 %uint_1 +; CHECK: %76 = OpIAdd %uint %75 %74 +; CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %76 +; CHECK: %78 = OpLoad %ulong %77 +; CHECK: %79 = OpULessThanEqual %bool %71 %78 +; CHECK: OpReturnValue %79 +; CHECK: OpFunctionEnd +; CHECK: %89 = OpFunction %void None %90 +; CHECK: %91 = OpFunctionParameter %uint +; CHECK: %92 = OpFunctionParameter %uint +; CHECK: %93 = OpFunctionParameter %uint +; CHECK: %94 = OpFunctionParameter %uint +; CHECK: %95 = OpLabel +; CHECK: %101 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_0 +; CHECK: %103 = OpAtomicIAdd %uint %101 %uint_4 %uint_0 %uint_10 +; CHECK: %104 = OpIAdd %uint %103 %uint_10 +; CHECK: %105 = OpArrayLength %uint %99 1 +; CHECK: %106 = OpULessThanEqual %bool %104 %105 +; CHECK: OpSelectionMerge %107 None +; CHECK: OpBranchConditional %106 %108 %107 +; CHECK: %108 = OpLabel +; CHECK: %109 = OpIAdd %uint %103 %uint_0 +; CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %109 +; CHECK: OpStore %110 %uint_10 +; CHECK: %112 = OpIAdd %uint %103 %uint_1 +; CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %112 +; CHECK: OpStore %113 %uint_23 +; CHECK: %114 = OpIAdd %uint %103 %uint_2 +; CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %114 +; CHECK: OpStore %115 %91 +; CHECK: %117 = OpIAdd %uint %103 %uint_3 +; CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %117 +; CHECK: OpStore %118 %uint_4 +; CHECK: %122 = OpLoad %v4float %gl_FragCoord +; CHECK: %124 = OpBitcast %v4uint %122 +; CHECK: %125 = OpCompositeExtract %uint %124 0 +; CHECK: %126 = OpIAdd %uint %103 %uint_4 +; CHECK: %127 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %126 +; CHECK: OpStore %127 %125 +; CHECK: %128 = OpCompositeExtract %uint %124 1 +; CHECK: %130 = OpIAdd %uint %103 %uint_5 +; CHECK: %131 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %130 +; CHECK: OpStore %131 %128 +; CHECK: %133 = OpIAdd %uint %103 %uint_7 +; CHECK: %134 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %133 +; CHECK: OpStore %134 %92 +; CHECK: %136 = OpIAdd %uint %103 %uint_8 +; CHECK: %137 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %136 +; CHECK: OpStore %137 %93 +; CHECK: %139 = OpIAdd %uint %103 %uint_9 +; CHECK: %140 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %139 +; CHECK: OpStore %140 %94 +; CHECK: OpBranch %107 +; CHECK: %107 = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch( + defs + decorates + globals + main + output_funcs, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/ir_loader_test.cpp b/third_party/spirv-tools/test/opt/ir_loader_test.cpp index 475dd2351e..ccdd032ee1 100644 --- a/third_party/spirv-tools/test/opt/ir_loader_test.cpp +++ b/third_party/spirv-tools/test/opt/ir_loader_test.cpp @@ -105,6 +105,22 @@ TEST(IrBuilder, RoundTripIncompleteFunction) { DoRoundTripCheck("%2 = OpFunction %1 None %3\n"); } +TEST(IrBuilder, RoundTripFunctionPointer) { + DoRoundTripCheck( + "OpCapability Linkage\n" + "OpCapability FunctionPointersINTEL\n" + "OpName %some_function \"some_function\"\n" + "OpName %ptr_to_function \"ptr_to_function\"\n" + "OpDecorate %some_function LinkageAttributes \"some_function\" Import\n" + "%float = OpTypeFloat 32\n" + "%4 = OpTypeFunction %float %float\n" + "%_ptr_Function_4 = OpTypePointer Function %4\n" + "%ptr_to_function = OpConstantFunctionPointerINTEL %_ptr_Function_4 " + "%some_function\n" + "%some_function = OpFunction %float Const %4\n" + "%6 = OpFunctionParameter %float\n" + "OpFunctionEnd\n"); +} TEST(IrBuilder, KeepLineDebugInfo) { // #version 310 es // void main() {} diff --git a/third_party/spirv-tools/test/opt/local_single_block_elim.cpp b/third_party/spirv-tools/test/opt/local_single_block_elim.cpp index 8e1cee6157..28b8a07d1f 100644 --- a/third_party/spirv-tools/test/opt/local_single_block_elim.cpp +++ b/third_party/spirv-tools/test/opt/local_single_block_elim.cpp @@ -84,6 +84,56 @@ OpFunctionEnd predefs_before + before, predefs_before + after, true, true); } +TEST_F(LocalSingleBlockLoadStoreElimTest, LSBElimForLinkage) { + const std::string predefs_before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %main LinkageAttributes "main" Export +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +%15 = OpLoad %v4float %v +OpStore %gl_FragColor %15 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) { // #version 140 // diff --git a/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp b/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp index c94ff372b3..5d910c4e79 100644 --- a/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp +++ b/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp @@ -126,6 +126,91 @@ OpFunctionEnd predefs + after, true, true); } +TEST_F(LocalSingleStoreElimTest, LSSElimForLinkage) { + const std::string predefs = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %fi "fi" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %main LinkageAttributes "main" Export +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%26 = OpLoad %v4float %v +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %26 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %20 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + TEST_F(LocalSingleStoreElimTest, ThreeStores) { // Three stores to multiple loads of v is not optimized. 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 ca9aba334c..4b7542fede 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 @@ -2165,6 +2165,140 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(LocalSSAElimTest, ShaderDebugForLoop) { + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] + +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0 +; CHECK-NEXT: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0 + +; CHECK-NOT: DebugDeclare + +; CHECK: [[loop_head:%\w+]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0 +; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]] +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[loop_body:%\w+]] + +; CHECK-NEXT: [[loop_body]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]] + +; CHECK: [[bb]] = OpLabel +; CHECK: OpStore %f [[f_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]] +; CHECK-NEXT: OpBranch [[loop_cont]] + +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpStore %i [[i_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]] +; CHECK-NEXT: OpBranch [[loop_head]] + +; CHECK: [[loop_merge]] = OpLabel + +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_10 = OpConstant %uint 10 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5 +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0 +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf %uint_4 +%main_ty = OpExtInst %void %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10 +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) { // Test the distribution of DebugValue for a parameter of an inlined function // and the visibility of Phi instruction. The ssa-rewrite pass must add @@ -4091,6 +4225,32 @@ TEST_F(LocalSSAElimTest, PointerVariables) { SinglePassRunAndMatch(text, true); } +TEST_F(LocalSSAElimTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // No optimization in the presence of diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp index 0f93302187..159e4a1430 100644 --- a/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp +++ b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp @@ -42,6 +42,10 @@ class PartialUnrollerTestPass : public Pass { Status Process() override { bool changed = false; for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); for (auto& loop : loop_descriptor) { LoopUtils loop_utils{context(), &loop}; @@ -1510,6 +1514,33 @@ OpFunctionEnd SinglePassRunAndCheck>(text, text, false); } +TEST_F(PassClassTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp index 6a3cb6ee52..ac0dfde787 100644 --- a/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp +++ b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp @@ -378,6 +378,185 @@ OpFunctionEnd)"; SinglePassRunAndMatch(text, true); } +TEST_F(PassClassTest, SimpleFullyUnrollWithShaderDebugInstructions) { + // We must preserve the debug information including + // NonSemantic.Shader.DebugInfo.100 instructions and DebugLine instructions. + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %2 "main" +OpName %5 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 4 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%uint_0 = OpConstant %14 0 +%uint_1 = OpConstant %14 1 +%uint_2 = OpConstant %14 2 +%uint_3 = OpConstant %14 3 +%uint_4 = OpConstant %14 4 +%uint_5 = OpConstant %14 5 +%uint_6 = OpConstant %14 6 +%uint_7 = OpConstant %14 7 +%uint_8 = OpConstant %14 8 +%uint_10 = OpConstant %14 10 +%uint_32 = OpConstant %14 32 +%15 = OpConstant %14 4 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpConstant %13 1 +%19 = OpTypePointer Function %13 +%20 = OpConstant %8 1 +%21 = OpTypeVector %13 4 +%22 = OpTypePointer Output %21 +%3 = OpVariable %22 Output +%null_expr = OpExtInst %6 %ext DebugExpression +%deref = OpExtInst %6 %ext DebugOperation %uint_0 +%deref_expr = OpExtInst %6 %ext DebugExpression %deref +%src = OpExtInst %6 %ext DebugSource %file_name +%cu = OpExtInst %6 %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5 +%dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0 +%dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf %uint_4 +%main_ty = OpExtInst %6 %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10 +%bb = OpExtInst %6 %ext DebugLexicalBlock %src %uint_0 %uint_0 %dbg_main +%dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4 +%dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_1 %uint_0 %bb %uint_4 + +; CHECK: [[f:%\w+]] = OpString "f" +; CHECK: [[i:%\w+]] = OpString "i" +; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0 + +; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression +; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation %uint_0 +; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]] +; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction +; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock +; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} %uint_0 %uint_0 [[dbg_fn]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} %uint_1 %uint_0 [[dbg_bb]] + +%2 = OpFunction %6 None %7 +%23 = OpLabel + +; The first block has DebugDeclare and DebugValue with Deref +; +; CHECK: OpLabel +; CHECK: %x = OpVariable %_ptr_Function__arr_float_uint_4_0 Function +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]] +; CHECK: OpBranch +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1 +; CHECK: OpSLessThan +; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0 +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_bb]] +; CHECK: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0 +; +; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0 + +; Other blocks do not have DebugDeclare and DebugValue with Deref +; +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1 +; CHECK: OpSLessThan +; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0 +; CHECK: OpBranch +; CHECK: OpLabel +; +; CHECK: DebugScope [[dbg_bb]] +; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0 +; +; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0 +; +; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]] +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugLine {{%\w+}} %uint_8 %uint_8 %uint_0 %uint_0 +; CHECK: OpReturn + +%5 = OpVariable %17 Function +OpBranch %24 +%24 = OpLabel +%35 = OpPhi %8 %10 %23 %34 %26 +%s1 = OpExtInst %6 %ext DebugScope %dbg_main +%d10 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_0 %uint_0 +%value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr +OpLoopMerge %25 %26 Unroll +OpBranch %27 +%27 = OpLabel +%s2 = OpExtInst %6 %ext DebugScope %dbg_main +%d1 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_1 %uint_1 +%29 = OpSLessThan %12 %35 %11 +%d2 = OpExtInst %6 %ext DebugLine %file_name %uint_2 %uint_2 %uint_0 %uint_0 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%s3 = OpExtInst %6 %ext DebugScope %bb +%decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr +%decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr +%d3 = OpExtInst %6 %ext DebugLine %file_name %uint_3 %uint_3 %uint_0 %uint_0 +%32 = OpAccessChain %19 %5 %35 +%d4 = OpExtInst %6 %ext DebugLine %file_name %uint_4 %uint_4 %uint_0 %uint_0 +OpStore %32 %18 +%d5 = OpExtInst %6 %ext DebugLine %file_name %uint_5 %uint_5 %uint_0 %uint_0 +OpBranch %26 +%26 = OpLabel +%s4 = OpExtInst %6 %ext DebugScope %dbg_main +%d6 = OpExtInst %6 %ext DebugLine %file_name %uint_6 %uint_6 %uint_0 %uint_0 +%34 = OpIAdd %8 %35 %20 +%value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr +%d7 = OpExtInst %6 %ext DebugLine %file_name %uint_7 %uint_7 %uint_0 %uint_0 +OpBranch %24 +%25 = OpLabel +%s5 = OpExtInst %6 %ext DebugScope %dbg_main +%d8 = OpExtInst %6 %ext DebugLine %file_name %uint_8 %uint_8 %uint_0 %uint_0 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + template class PartialUnrollerTestPass : public Pass { public: 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 fd97efab53..21960d1799 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 @@ -2567,6 +2567,39 @@ TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) { SinglePassRunAndMatch(before, true); } +TEST_F(MergeReturnPassTest, OverflowTest1) { + const std::string text = + R"( +; CHECK: OpReturn +; CHECK-NOT: OpReturn +; CHECK: OpFunctionEnd + OpCapability ClipDistance + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %6 = OpTypeFunction %void + %2 = OpFunction %void None %6 + %4194303 = OpLabel + OpBranch %18 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranch %21 + %21 = OpLabel + OpReturn + %20 = OpLabel + OpBranch %18 + %19 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = + SinglePassRunToBinary(text, /* skip_nop = */ true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp b/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp index 7672e8f309..abb5cde670 100644 --- a/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp +++ b/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp @@ -17,6 +17,12 @@ #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" +namespace { + +const double kDefaultLoadReductionThreshold = 0.9; + +} // namespace + namespace spvtools { namespace opt { namespace { @@ -104,7 +110,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract) { SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndMatch(test, false); + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { @@ -202,7 +209,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndMatch(test, false); + SinglePassRunAndMatch(test, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) { @@ -280,7 +288,8 @@ OpFunctionEnd SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_5_extract) { @@ -351,7 +360,8 @@ OpFunctionEnd SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); } TEST_F(ReduceLoadSizeTest, cbuffer_load_fully_used) { @@ -416,7 +426,76 @@ OpFunctionEnd SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - SinglePassRunAndCheck(test, test, true, false); + SinglePassRunAndCheck(test, test, true, false, + kDefaultLoadReductionThreshold); +} + +TEST_F(ReduceLoadSizeTest, replace_cbuffer_load_fully_used) { + const std::string test = + R"( + OpCapability Shader + OpCapability SampledBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_MaterialInstancing_cbuffer "type.MaterialInstancing_cbuffer" + OpMemberName %type_MaterialInstancing_cbuffer 0 "MaterialInstancing_constants" + OpName %MaterialInstancing_Constants "MaterialInstancing_Constants" + OpMemberName %MaterialInstancing_Constants 0 "offset0" + OpMemberName %MaterialInstancing_Constants 1 "params" + OpName %InstancingParams_Constants "InstancingParams_Constants" + OpMemberName %InstancingParams_Constants 0 "offset1" + OpName %MaterialInstancing_cbuffer "MaterialInstancing_cbuffer" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %MaterialInstancing_cbuffer DescriptorSet 6 + OpDecorate %MaterialInstancing_cbuffer Binding 0 + OpMemberDecorate %InstancingParams_Constants 0 Offset 0 + OpMemberDecorate %MaterialInstancing_Constants 0 Offset 0 + OpMemberDecorate %MaterialInstancing_Constants 1 Offset 16 + OpMemberDecorate %type_MaterialInstancing_cbuffer 0 Offset 0 + OpDecorate %type_MaterialInstancing_cbuffer Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v4int = OpTypeVector %int 4 +%InstancingParams_Constants = OpTypeStruct %v4int +%MaterialInstancing_Constants = OpTypeStruct %v4int %InstancingParams_Constants +%type_MaterialInstancing_cbuffer = OpTypeStruct %MaterialInstancing_Constants +%_ptr_Uniform_type_MaterialInstancing_cbuffer = OpTypePointer Uniform %type_MaterialInstancing_cbuffer +%_ptr_Output_int = OpTypePointer Output %int + %void = OpTypeVoid + %60 = OpTypeFunction %void +%_ptr_Uniform_MaterialInstancing_Constants = OpTypePointer Uniform %MaterialInstancing_Constants +%MaterialInstancing_cbuffer = OpVariable %_ptr_Uniform_type_MaterialInstancing_cbuffer Uniform +%out_var_SV_Target0 = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %60 + %80 = OpLabel + %131 = OpAccessChain %_ptr_Uniform_MaterialInstancing_Constants %MaterialInstancing_cbuffer %int_0 + %132 = OpLoad %MaterialInstancing_Constants %131 +; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %MaterialInstancing_cbuffer %int_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_0 +; CHECK: OpLoad %v4int [[ac2]] + +; CHECK: [[ac3:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1 +; CHECK: [[ac4:%\w+]] = OpAccessChain {{%\w+}} [[ac3]] %uint_0 +; CHECK: OpLoad %v4int [[ac4]] + %134 = OpCompositeExtract %v4int %132 0 + %135 = OpCompositeExtract %InstancingParams_Constants %132 1 + %136 = OpCompositeExtract %v4int %135 0 + %149 = OpCompositeExtract %int %134 0 + %185 = OpCompositeExtract %int %136 0 + %156 = OpIAdd %int %149 %185 + OpStore %out_var_SV_Target0 %156 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(test, false, 1.1); } } // namespace diff --git a/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp b/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp index 474f466164..28eda73eff 100644 --- a/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp +++ b/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp @@ -335,6 +335,32 @@ TEST_F(RedundancyEliminationTest, OpenCLDebugInfo100) { SinglePassRunAndMatch(text, false); } +TEST_F(RedundancyEliminationTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt -} // namespace spvtools +} // namespace spvtools \ No newline at end of file diff --git a/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp b/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp index 14cde0b94d..b9cb0de097 100644 --- a/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp +++ b/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp @@ -137,6 +137,86 @@ OpFunctionEnd true); } +TEST_F(RelaxFloatOpsTest, RelaxFloatOpsForLinkage) { + const std::string defs0 = + R"(OpCapability Shader +OpCapability Linkage +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpSource HLSL 630 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %i_Tex0 "i.Tex0" +OpName %i_Tex1 "i.Tex1" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %main LinkageAttributes "main" Export +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpDecorate %i_Tex0 Location 0 +OpDecorate %i_Tex1 Location 1 +OpDecorate %_entryPointOutput_Color Location 0 +)"; + + const std::string defs1 = + R"(%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%17 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant +%25 = OpTypeSampledImage %17 +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%i_Tex1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%float_0_5 = OpConstant %float 0.5 +%116 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +)"; + + const std::string relax_decos = + R"(OpDecorate %60 RelaxedPrecision +OpDecorate %63 RelaxedPrecision +OpDecorate %82 RelaxedPrecision +OpDecorate %88 RelaxedPrecision +OpDecorate %91 RelaxedPrecision +OpDecorate %94 RelaxedPrecision +)"; + + const std::string func_orig = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%60 = OpLoad %float %i_Tex0 +%63 = OpLoad %float %i_Tex1 +%77 = OpLoad %17 %g_tTex1df4 +%78 = OpLoad %21 %g_sSamp +%79 = OpSampledImage %25 %77 %78 +%82 = OpImageSampleImplicitLod %v4float %79 %60 +%83 = OpLoad %17 %g_tTex1df4 +%84 = OpLoad %21 %g_sSamp +%85 = OpSampledImage %25 %83 %84 +%88 = OpImageSampleImplicitLod %v4float %85 %63 +%91 = OpFAdd %v4float %82 %88 +%94 = OpFMul %v4float %91 %116 +OpStore %_entryPointOutput_Color %94 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true, + true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/replace_desc_array_access_using_var_index_test.cpp b/third_party/spirv-tools/test/opt/replace_desc_array_access_using_var_index_test.cpp new file mode 100644 index 0000000000..ca62581265 --- /dev/null +++ b/third_party/spirv-tools/test/opt/replace_desc_array_access_using_var_index_test.cpp @@ -0,0 +1,411 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ReplaceDescArrayAccessUsingVarIndexTest = PassTest<::testing::Test>; + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArray) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 + %bool = OpTypeBool +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_3 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_2 = OpConstant %uint 2 + %66 = OpConstantNull %v4float + +; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float + + %psmain = OpFunction %void None %21 + %39 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %37 = OpIEqual %bool %30 %uint_2 + OpSelectionMerge %38 None + OpBranchConditional %37 %28 %40 + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID +; CHECK: OpSelectionMerge [[cond_branch_merge:%\w+]] None +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[bb_cond_br:%\w+]] + + %28 = OpLabel + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case2]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel + + %33 = OpLoad %type_sampler %Sampler0 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %33 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[case2]] = OpLabel +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_2 +; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0 +; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]] +; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]] +; CHECK: [[value2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]] +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case0]] [[value1]] [[case1]] [[value2]] [[case2]] [[null_value]] [[default]] + + OpBranch %38 + %40 = OpLabel + OpBranch %38 + %38 = OpLabel + %41 = OpPhi %v4float %36 %28 %29 %40 + +; CHECK: OpBranch [[cond_branch_merge]] +; CHECK: [[bb_cond_br]] = OpLabel +; CHECK: OpBranch [[cond_branch_merge]] +; CHECK: [[cond_branch_merge]] = OpLabel +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[phi0]] [[merge]] {{%\w+}} [[bb_cond_br]] +; CHECK: OpStore {{%\w+}} [[phi1]] + + OpStore %out_var_SV_TARGET %41 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayAndSamplerArray) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 +%type_sampler = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2 +%_ptr_UniformConstant__arr_type_sampler_uint_2 = OpTypePointer UniformConstant %_arr_type_sampler_uint_2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_2 UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %66 = OpConstantNull %v4float + %psmain = OpFunction %void None %21 + %28 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float + +; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] +; CHECK: [[case0]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[case1]] = OpLabel +; CHECK: OpAccessChain +; CHECK: OpLoad +; CHECK: OpImageWrite +; CHECK: OpBranch [[merge]] +; CHECK: [[default]] = OpLabel +; CHECK: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel + + %33 = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %30 + %37 = OpLoad %type_sampler %33 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %37 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + +; SPIR-V instructions to be replaced (will be killed by ADCE) +; CHECK: OpSelectionMerge +; CHECK: OpSwitch + +; CHECK: OpSelectionMerge [[merge_sampler:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_sampler:%\w+]] 0 [[case_sampler0:%\w+]] 1 [[case_sampler1:%\w+]] + +; CHECK: [[case_sampler0]] = OpLabel +; CHECK: OpSelectionMerge [[merge_texture0:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]] +; CHECK: [[case_texture0]] = OpLabel +; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0 +; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]] +; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]] +; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s0]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]] +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[case_texture1]] = OpLabel +; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0 +; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]] +; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]] +; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s0]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]] +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[default_texture]] = OpLabel +; CHECK: OpBranch [[merge_texture0]] +; CHECK: [[merge_texture0]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]] +; CHECK: OpBranch [[merge_sampler]] + +; CHECK: [[case_sampler1]] = OpLabel +; CHECK: OpSelectionMerge [[merge_texture1:%\w+]] None +; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]] +; CHECK: [[case_texture0]] = OpLabel +; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1 +; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]] +; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]] +; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s1]] +; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]] +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[case_texture1]] = OpLabel +; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1 +; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1 +; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]] +; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]] +; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s1]] +; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]] +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[default_texture]] = OpLabel +; CHECK: OpBranch [[merge_texture1]] +; CHECK: [[merge_texture1]] = OpLabel +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]] + +; CHECK: [[default_sampler]] = OpLabel +; CHECK: OpBranch [[merge_sampler]] +; CHECK: [[merge_sampler]] = OpLabel +; CHECK: OpPhi %v4float [[phi0]] [[merge_texture0]] [[phi1]] [[merge_texture1]] [[null_value]] [[default_sampler]] +; CHECK: OpStore + + OpStore %out_var_SV_TARGET %36 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayWithSingleElement) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET + OpExecutionMode %psmain OriginUpperLeft + OpSource HLSL 600 + OpName %type_sampler "type.sampler" + OpName %Sampler0 "Sampler0" + OpName %type_2d_image "type.2d.image" + OpName %Tex0 "Tex0" + OpName %in_var_INSTANCEID "in.var.INSTANCEID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %psmain "psmain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_INSTANCEID Flat + OpDecorate %in_var_INSTANCEID Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %Sampler0 DescriptorSet 0 + OpDecorate %Sampler0 Binding 1 + OpDecorate %Tex0 DescriptorSet 0 + OpDecorate %Tex0 Binding 2 +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown +%_arr_type_2d_image_uint_1 = OpTypeArray %type_2d_image %uint_1 +%_ptr_UniformConstant__arr_type_2d_image_uint_1 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_1 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v2float = OpTypeVector %float 2 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %27 = OpConstantComposite %v2uint %uint_0 %uint_1 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_1 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_2 = OpConstant %uint 2 + %66 = OpConstantNull %v4float + %psmain = OpFunction %void None %21 + %28 = OpLabel + %29 = OpLoad %v4float %gl_FragCoord + %30 = OpLoad %uint %in_var_INSTANCEID + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30 + %32 = OpLoad %type_2d_image %31 + OpImageWrite %32 %27 %29 + +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0 +; CHECK-NOT: OpAccessChain +; CHECK-NOT: OpSwitch +; CHECK-NOT: OpPhi + + %33 = OpLoad %type_sampler %Sampler0 + %34 = OpVectorShuffle %v2float %29 %29 0 1 + %35 = OpSampledImage %type_sampled_image %32 %33 + %36 = OpImageSampleImplicitLod %v4float %35 %34 None + + OpStore %out_var_SV_TARGET %36 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp b/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp index 8115f5fb57..8cb888c93e 100644 --- a/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp +++ b/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp @@ -1935,12 +1935,12 @@ OpName %6 "simple_struct" ; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable ; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 ; CHECK-NOT: DebugDeclare %decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr @@ -2058,10 +2058,10 @@ OpName %6 "simple_struct" ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 +; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_2 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 %int_0 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_1 %int_1 -; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 ; CHECK-NOT: DebugDeclare %decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr @@ -2174,12 +2174,12 @@ OpName %6 "simple_struct" ; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLocalVariable ; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3 ; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 ; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function -; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 ; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 OpBranch %20 @@ -2237,6 +2237,32 @@ OpFunctionEnd SinglePassRunAndMatch(text, false); } +TEST_F(ScalarReplacementTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp b/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp index 5e63862e9a..f1dd50eee3 100644 --- a/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp +++ b/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp @@ -935,6 +935,98 @@ INSTANTIATE_TEST_SUITE_P( "%2 = OpSpecConstantTrue %bool\n" "%3 = OpSpecConstantTrue %bool\n", }, + // 19. 16-bit signed int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%short = OpTypeInt 16 1\n" + "%1 = OpSpecConstant %short 10\n" + "%2 = OpSpecConstant %short 11\n" + "%3 = OpSpecConstant %short 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected. These are sign-extended + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%short = OpTypeInt 16 1\n" + "%1 = OpSpecConstant %short 32767\n" + "%2 = OpSpecConstant %short -1\n" + "%3 = OpSpecConstant %short -42\n", + }, + // 20. 16-bit unsigned int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%ushort = OpTypeInt 16 0\n" + "%1 = OpSpecConstant %ushort 10\n" + "%2 = OpSpecConstant %ushort 11\n" + "%3 = OpSpecConstant %ushort 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}}, + // expected. Upper bits are always zero. + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%ushort = OpTypeInt 16 0\n" + "%1 = OpSpecConstant %ushort 32767\n" + "%2 = OpSpecConstant %ushort 65535\n" + "%3 = OpSpecConstant %ushort 65494\n", + }, + // 21. 8-bit signed int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%char = OpTypeInt 8 1\n" + "%1 = OpSpecConstant %char 10\n" + "%2 = OpSpecConstant %char 11\n" + "%3 = OpSpecConstant %char 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {127}}, {101, {128}}, {102, {0xd6}}}, + // expected. These are sign extended + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%char = OpTypeInt 8 1\n" + "%1 = OpSpecConstant %char 127\n" + "%2 = OpSpecConstant %char -128\n" + "%3 = OpSpecConstant %char -42\n", + }, + // 22. 8-bit unsigned int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "%uchar = OpTypeInt 8 0\n" + "%1 = OpSpecConstant %uchar 10\n" + "%2 = OpSpecConstant %uchar 11\n" + "%3 = OpSpecConstant %uchar 11\n" + "%4 = OpSpecConstant %uchar 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {127}}, {101, {128}}, {102, {256}}, {103, {0xffffffd6}}}, + // expected. Upper bits are always zero. + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "%uchar = OpTypeInt 8 0\n" + "%1 = OpSpecConstant %uchar 127\n" + "%2 = OpSpecConstant %uchar 128\n" + "%3 = OpSpecConstant %uchar 0\n" + "%4 = OpSpecConstant %uchar 214\n", + }, })); INSTANTIATE_TEST_SUITE_P( diff --git a/third_party/spirv-tools/test/opt/simplification_test.cpp b/third_party/spirv-tools/test/opt/simplification_test.cpp index 7a9696eab4..7727f5673d 100644 --- a/third_party/spirv-tools/test/opt/simplification_test.cpp +++ b/third_party/spirv-tools/test/opt/simplification_test.cpp @@ -360,6 +360,31 @@ OpFunctionEnd SinglePassRunAndMatch(spirv, true); } +TEST_F(SimplificationTest, FunctionDeclaration) { + // Make sure the pass works with a function declaration that is called. + const std::string text = R"(OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int8 +%1 = OpExtInstImport "OpenCL.std" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" +OpExecutionMode %2 ContractionOff +OpSource Unknown 0 +OpDecorate %3 LinkageAttributes "julia_error_7712" Import +%void = OpTypeVoid +%5 = OpTypeFunction %void +%3 = OpFunction %void None %5 +OpFunctionEnd +%2 = OpFunction %void None %5 +%6 = OpLabel +%7 = OpFunctionCall %void %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp b/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp index 7f64ffd7cf..2cd3c7dfb5 100644 --- a/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp +++ b/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp @@ -404,7 +404,7 @@ OpCapability VariablePointers OpExtension "SPV_KHR_variable_pointers" OpMemoryModel Logical GLSL450 OpDecorate %param Coherent -OpDecorate %param ArrayStride 4 +OpDecorate %ptr_int_StorageBuffer ArrayStride 4 %void = OpTypeVoid %bool = OpTypeBool %int = OpTypeInt 32 0 diff --git a/third_party/spirv-tools/test/opt/vector_dce_test.cpp b/third_party/spirv-tools/test/opt/vector_dce_test.cpp index 9bdad375bc..b14e2256ee 100644 --- a/third_party/spirv-tools/test/opt/vector_dce_test.cpp +++ b/third_party/spirv-tools/test/opt/vector_dce_test.cpp @@ -1351,6 +1351,72 @@ OpFunctionEnd SinglePassRunAndMatch(text, true); } +TEST_F(VectorDCETest, OutOfBoundsExtract) { + // It tests that the vector DCE pass is able to handle an extract with an + // index that is out of bounds. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpCompositeExtract %float [[undef]] 8 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_float = OpTypePointer Output %float + %OutColor = OpVariable %_ptr_Output_float Output + %null = OpConstantNull %v4float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %28 = OpLabel + %33 = OpCompositeInsert %v4float %float_1 %null 1 + %extract = OpCompositeExtract %float %33 8 + OpStore %OutColor %extract + OpReturn + OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +TEST_F(VectorDCETest, OutOfBoundsShuffle) { + // It tests that the vector DCE pass is able to handle a shuffle with an + // index that is out of bounds. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpVectorShuffle %v4float [[undef]] [[undef]] 9 10 11 12 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %null = OpConstantNull %v4float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %28 = OpLabel + %33 = OpCompositeInsert %v4float %float_1 %null 1 + %shuffle = OpVectorShuffle %v4float %33 %33 9 10 11 12 + OpStore %OutColor %shuffle + OpReturn + OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary_test.cpp b/third_party/spirv-tools/test/text_to_binary_test.cpp index 57f0a6ce0b..99d9ed6869 100644 --- a/third_party/spirv-tools/test/text_to_binary_test.cpp +++ b/third_party/spirv-tools/test/text_to_binary_test.cpp @@ -247,12 +247,6 @@ INSTANTIATE_TEST_SUITE_P( {"0x1.804p4", 0x00004e01}, })); -TEST(CreateContext, InvalidEnvironment) { - spv_target_env env; - std::memset(&env, 99, sizeof(env)); - EXPECT_THAT(spvContextCreate(env), IsNull()); -} - TEST(CreateContext, UniversalEnvironment) { auto c = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); EXPECT_THAT(c, NotNull()); diff --git a/third_party/spirv-tools/test/val/CMakeLists.txt b/third_party/spirv-tools/test/val/CMakeLists.txt index b1e12ac03c..64eba44616 100644 --- a/third_party/spirv-tools/test/val/CMakeLists.txt +++ b/third_party/spirv-tools/test/val/CMakeLists.txt @@ -23,6 +23,7 @@ set(VAL_TEST_COMMON_SRCS add_spvtools_unittest(TARGET val_abcde SRCS val_adjacency_test.cpp + val_annotation_test.cpp val_arithmetics_test.cpp val_atomics_test.cpp val_barriers_test.cpp @@ -45,6 +46,7 @@ add_spvtools_unittest(TARGET val_abcde val_extension_spv_khr_bit_instructions.cpp val_extension_spv_khr_terminate_invocation.cpp val_ext_inst_test.cpp + val_ext_inst_debug_test.cpp ${VAL_TEST_COMMON_SRCS} LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} PCH_FILE pch_test_val diff --git a/third_party/spirv-tools/test/val/val_annotation_test.cpp b/third_party/spirv-tools/test/val/val_annotation_test.cpp new file mode 100644 index 0000000000..889c76caea --- /dev/null +++ b/third_party/spirv-tools/test/val/val_annotation_test.cpp @@ -0,0 +1,951 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for decorations + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; + +using DecorationTest = spvtest::ValidateBase; + +TEST_F(DecorationTest, WorkgroupSizeShader) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ones BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%ones = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(DecorationTest, WorkgroupSizeKernel) { + const std::string text = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %var BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%ptr = OpTypePointer Input %int3 +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +using MemberOnlyDecorations = spvtest::ValidateBase; + +TEST_P(MemberOnlyDecorations, MemberDecoration) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 )" + + deco + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemberOnlyDecorations, Decoration) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can only be applied to structure members")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateMemberOnlyDecorations, MemberOnlyDecorations, + Values("RowMajor", "ColMajor", "MatrixStride 16" + // SPIR-V spec bug? + /*,"Offset 0"*/)); + +using NonMemberOnlyDecorations = spvtest::ValidateBase; + +TEST_P(NonMemberOnlyDecorations, MemberDecoration) { + const auto deco = GetParam(); + const auto text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpCapability Addresses +OpCapability PhysicalStorageBufferAddresses +OpCapability ShaderNonUniform +OpExtension "SPV_KHR_no_integer_wrap_decoration" +OpExtension "SPV_KHR_physical_storage_buffer" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 )" + + deco + R"( +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%float2x2 = OpTypeMatrix %float2 2 +%struct = OpTypeStruct %float2x2 +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("cannot be applied to structure members")); +} + +INSTANTIATE_TEST_SUITE_P( + ValidateNonMemberOnlyDecorations, NonMemberOnlyDecorations, + Values("SpecId 1", "Block", "BufferBlock", "ArrayStride 4", "GLSLShared", + "GLSLPacked", "CPacked", + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + //"Restrict", + "Aliased", "Constant", "Uniform", "SaturatedConversion", "Index 0", + "Binding 0", "DescriptorSet 0", "FuncParamAttr Zext", + "FPRoundingMode RTE", "FPFastMathMode None", + "LinkageAttributes \"ext\" Import", "NoContraction", + "InputAttachmentIndex 0", "Alignment 4", "MaxByteOffset 4", + "AlignmentId %float", "MaxByteOffsetId %float", "NoSignedWrap", + "NoUnsignedWrap", "NonUniform", "RestrictPointer", "AliasedPointer", + "CounterBuffer %float")); + +using StructDecorations = spvtest::ValidateBase; + +TEST_P(StructDecorations, Struct) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%struct = OpTypeStruct +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(StructDecorations, OtherType) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int )" + deco + R"( +%int = OpTypeInt 32 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, Variable) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +TEST_P(StructDecorations, Constant) { + const std::string deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int_0 )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateStructDecorations, StructDecorations, + Values("Block", "BufferBlock", "GLSLShared", + "GLSLPacked", "CPacked")); + +using ArrayDecorations = spvtest::ValidateBase; + +TEST_P(ArrayDecorations, Array) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %array )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, RuntimeArray) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %array )" + deco + + R"( +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, Pointer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ptr )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArrayDecorations, Struct) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct )" + deco + + R"( +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +TEST_P(ArrayDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %null )" + deco + + R"( +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%null = OpConstantNull %array +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be an array or pointer type")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateArrayDecorations, ArrayDecorations, + Values("ArrayStride 4")); + +using BuiltInDecorations = spvtest::ValidateBase; + +TEST_P(BuiltInDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Input %int +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + if (deco != "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a constant for WorkgroupSize")); + } +} + +TEST_P(BuiltInDecorations, IntegerType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); +} + +TEST_P(BuiltInDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%void = OpTypeVoid +%fn = OpTypeFunction %void %int +%func = OpFunction %void None %fn +%param = OpFunctionParameter %int +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); +} + +TEST_P(BuiltInDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %const BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%const = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + if (deco == "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); + } +} + +TEST_P(BuiltInDecorations, SpecConstant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %const BuiltIn )" + + deco + R"( +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%const = OpSpecConstantComposite %int3 %int_1 %int_1 %int_1 +)"; + + CompileSuccessfully(text); + if (deco == "WorkgroupSize") { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); + } +} + +INSTANTIATE_TEST_SUITE_P(ValidateBuiltInDecorations, BuiltInDecorations, + Values("Position", "PointSize", "VertexId", + "InstanceId", "FragCoord", "FrontFacing", + "NumWorkgroups", "WorkgroupSize", + "LocalInvocationId", "GlobalInvocationId")); + +using MemoryObjectDecorations = spvtest::ValidateBase; + +TEST_P(MemoryObjectDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemoryObjectDecorations, FunctionParameterGood) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%void = OpTypeVoid +%fn = OpTypeFunction %void %ptr +%func = OpFunction %void None %fn +%param = OpFunctionParameter %ptr +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(MemoryObjectDecorations, FunctionParameterNotAPointer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%void = OpTypeVoid +%fn = OpTypeFunction %void %float +%func = OpFunction %void None %fn +%param = OpFunctionParameter %float +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); +} + +TEST_P(MemoryObjectDecorations, FloatType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %float )" + deco + + R"( +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a memory object declaration")); +} + +TEST_P(MemoryObjectDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampleRateShading +OpCapability TransformFeedback +OpCapability GeometryStreams +OpCapability Tessellation +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel Logical GLSL450 +OpDecorate %const )" + deco + + R"( +%float = OpTypeFloat 32 +%const = OpConstant %float 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be a memory object declaration")); +} + +// NonWritable and NonReadable are covered by other tests. +INSTANTIATE_TEST_SUITE_P( + ValidateMemoryObjectDecorations, MemoryObjectDecorations, + Values("NoPerspective", "Flat", "Patch", "Centroid", "Component 0", + "Sample", "Restrict", "Aliased", "Volatile", "Coherent", "Stream 0", + "XfbBuffer 1", "XfbStride 1", "AliasedPointer", "RestrictPointer")); + +using VariableDecorations = spvtest::ValidateBase; + +TEST_P(VariableDecorations, Variable) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %var )" + deco + R"( +%float = OpTypeFloat 32 +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(VariableDecorations, FunctionParameter) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "import" Import +OpDecorate %param )" + deco + + R"( +%float = OpTypeFloat 32 +%void = OpTypeVoid +%fn = OpTypeFunction %void %float +%func = OpFunction %void None %fn +%param = OpFunctionParameter %float +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_P(VariableDecorations, FloatType) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %float )" + deco + + R"( +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_P(VariableDecorations, Constant) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpCapability InputAttachment +OpMemoryModel Logical GLSL450 +OpDecorate %const )" + deco + + R"( +%float = OpTypeFloat 32 +%const = OpConstant %float 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVariableDecorations, VariableDecorations, + Values("Invariant", "Constant", "Location 0", + "Index 0", "Binding 0", "DescriptorSet 0")); + +using VulkanIOStorageClass = + spvtest::ValidateBase>; + +TEST_P(VulkanIOStorageClass, Invalid) { + const auto deco = std::get<0>(GetParam()); + const auto sc = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%ptr = OpTypePointer )" + + sc + + R"( %float +%var = OpVariable %ptr )" + sc + + R"( +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decoration must not be applied to this storage class")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanIOStorageClass, VulkanIOStorageClass, + Combine(Values("Location", "Component"), + Values("StorageBuffer", "Uniform", + "UniformConstant", "Workgroup", + "Private"))); + +using VulkanResourceStorageClass = + spvtest::ValidateBase>; + +TEST_P(VulkanResourceStorageClass, Invalid) { + const auto deco = std::get<0>(GetParam()); + const auto sc = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%ptr = OpTypePointer )" + + sc + + R"( %float +%var = OpVariable %ptr )" + sc + + R"( +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanResourceStorageClass, + VulkanResourceStorageClass, + Combine(Values("DescriptorSet", "Binding"), + Values("Private", "Input", "Output", + "Workgroup"))); + +using VulkanInterpolationStorageClass = spvtest::ValidateBase; + +TEST_P(VulkanInterpolationStorageClass, Input) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Input %float +%var = OpVariable %ptr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_P(VulkanInterpolationStorageClass, Output) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Output %float +%var = OpVariable %ptr Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_P(VulkanInterpolationStorageClass, Private) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Private %float +%var = OpVariable %ptr Private +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +TEST_P(VulkanInterpolationStorageClass, Uniform) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +OpDecorate %var Binding 0 +OpDecorate %var DescriptorSet 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer Uniform %float +%var = OpVariable %ptr Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +TEST_P(VulkanInterpolationStorageClass, StorageBuffer) { + const auto deco = GetParam(); + const std::string text = R"( +OpCapability Shader +OpCapability SampleRateShading +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var )" + deco + R"( +OpDecorate %var Binding 0 +OpDecorate %var DescriptorSet 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%ptr = OpTypePointer StorageBuffer %float +%var = OpVariable %ptr StorageBuffer +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("storage class must be Input or Output")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[VUID-StandaloneSpirv-Flat-04670")); +} + +INSTANTIATE_TEST_SUITE_P(ValidateVulkanInterpolationStorageClass, + VulkanInterpolationStorageClass, + Values("Flat", "NoPerspective", "Centroid", "Sample")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_arithmetics_test.cpp b/third_party/spirv-tools/test/val/val_arithmetics_test.cpp index b82fc97e12..856ad02c18 100644 --- a/third_party/spirv-tools/test/val/val_arithmetics_test.cpp +++ b/third_party/spirv-tools/test/val/val_arithmetics_test.cpp @@ -1309,6 +1309,57 @@ TEST_F(ValidateArithmetics, CoopMatDimFail) { HasSubstr("Cooperative matrix 'M' mismatch: CooperativeMatrixMulAddNV")); } +TEST_F(ValidateArithmetics, CoopMatComponentTypeNotScalarNumeric) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %bool %subgroup %u32_8 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Component Type " + "'4[%bool]' is not a scalar numerical type.")); +} + +TEST_F(ValidateArithmetics, CoopMatScopeNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %f32_1 %u32_8 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Scope '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + +TEST_F(ValidateArithmetics, CoopMatRowsNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %f32_1 %u32_8 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Rows '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + +TEST_F(ValidateArithmetics, CoopMatColumnsNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixNV Cols '17[%float_1]' is not a " + "constant instruction with scalar integer type.")); +} + TEST_F(ValidateArithmetics, IAddCarrySuccess) { const std::string body = R"( %val1 = OpIAddCarry %struct_u32_u32 %u32_0 %u32_1 diff --git a/third_party/spirv-tools/test/val/val_atomics_test.cpp b/third_party/spirv-tools/test/val/val_atomics_test.cpp index b7f6948efb..d1a030a87a 100644 --- a/third_party/spirv-tools/test/val/val_atomics_test.cpp +++ b/third_party/spirv-tools/test/val/val_atomics_test.cpp @@ -2679,6 +2679,39 @@ OpFunctionEnd "CooperativeMatrixNV capability is present")); } +TEST_F(ValidateAtomics, IIncrementBadPointerDataType) { + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + %uint = OpTypeInt 32 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %16 = OpTypeFunction %void +%uint_538976288 = OpConstant %uint 538976288 + %int = OpTypeInt 32 1 +%_runtimearr_int = OpTypeRuntimeArray %int + %_struct_5 = OpTypeStruct %_runtimearr_int +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %3 = OpVariable %_ptr_Input_v3uint Input + %7 = OpVariable %_ptr_Uniform__struct_5 Uniform + %8224 = OpFunction %void None %16 + %65312 = OpLabel + %25 = OpAccessChain %_ptr_Input_uint %3 %uint_538976288 + %26 = OpLoad %uint %25 + %2097184 = OpAtomicIIncrement %int %7 %uint_538976288 %26 + OpUnreachable + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIIncrement: expected Pointer to point to a " + "value of type Result Type")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_builtins_test.cpp b/third_party/spirv-tools/test/val/val_builtins_test.cpp index 2116fff26a..dff9adfef9 100644 --- a/third_party/spirv-tools/test/val/val_builtins_test.cpp +++ b/third_party/spirv-tools/test/val/val_builtins_test.cpp @@ -767,18 +767,18 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotGLCompute, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", - "WorkgroupId"), - Values("Vertex", "Fragment", "Geometry", "TessellationControl", - "TessellationEvaluation"), - Values("Input"), Values("%u32vec3"), - Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 " - "VUID-LocalInvocationId-LocalInvocationId-04281 " - "VUID-NumWorkgroups-NumWorkgroups-04296 " - "VUID-WorkgroupId-WorkgroupId-04422"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("Vertex", "Fragment", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32vec3"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 " + "VUID-LocalInvocationId-LocalInvocationId-04281 " + "VUID-NumWorkgroups-NumWorkgroups-04296 " + "VUID-WorkgroupId-WorkgroupId-04422"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute, MeshNV, or " + "TaskNV execution model")))); INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotInput, @@ -2828,9 +2828,10 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) { CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used " - "only with GLCompute execution model")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used " + "only with GLCompute, MeshNV, or TaskNV execution model")); EXPECT_THAT(getDiagnosticString(), HasSubstr("is referencing ID <2> (OpConstantComposite) which is " "decorated with BuiltIn WorkgroupSize in function <1> " @@ -2860,9 +2861,9 @@ OpDecorate %copy BuiltIn WorkgroupSize CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("BuiltIns can only target variables, structs or constants")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure " + "members or constants")); } CodeGenerator GetWorkgroupSizeNotVectorGenerator() { @@ -3481,35 +3482,6 @@ OpDecorate %gl_ViewportIndex PerPrimitiveNV EXPECT_THAT(getDiagnosticString(), HasSubstr("is not an int scalar")); } -TEST_F(ValidateBuiltIns, GetUnderlyingTypeNoAssert) { - std::string spirv = R"( - OpCapability Shader - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "PSMa" %12 %17 - OpExecutionMode %4 OriginUpperLeft - OpDecorate %gl_PointCoord BuiltIn PointCoord - OpDecorate %12 Location 0 - OpDecorate %17 Location 0 - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 - %gl_PointCoord = OpTypeStruct %v4float - %_ptr_Input_v4float = OpTypePointer Input %v4float - %_ptr_Output_v4float = OpTypePointer Output %v4float - %12 = OpVariable %_ptr_Input_v4float Input - %17 = OpVariable %_ptr_Output_v4float Output - %4 = OpFunction %void None %3 - %15 = OpLabel - OpReturn - OpFunctionEnd)"; - CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("did not find an member index to get underlying data " - "type")); -} - TEST_P(ValidateVulkanSubgroupBuiltIns, InMain) { const char* const built_in = std::get<0>(GetParam()); const char* const execution_model = std::get<1>(GetParam()); @@ -3711,13 +3683,13 @@ OpFunctionEnd INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotCompute, ValidateVulkanSubgroupBuiltIns, - Combine( - Values("SubgroupId", "NumSubgroups"), Values("Vertex"), Values("Input"), - Values("%u32"), - Values("VUID-SubgroupId-SubgroupId-04367 " - "VUID-NumSubgroups-NumSubgroups-04293"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); + Combine(Values("SubgroupId", "NumSubgroups"), Values("Vertex"), + Values("Input"), Values("%u32"), + Values("VUID-SubgroupId-SubgroupId-04367 " + "VUID-NumSubgroups-NumSubgroups-04293"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute, MeshNV, or " + "TaskNV execution model")))); INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns, @@ -3780,9 +3752,9 @@ OpDecorate %void BuiltIn Position CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("BuiltIns can only target variables, structs or constants")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structure members " + "or constants")); } TEST_F(ValidateBuiltIns, TargetIsVariable) { @@ -3800,47 +3772,6 @@ OpDecorate %wg_var BuiltIn Position EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateBuiltIns, TargetIsStruct) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %struct BuiltIn Position -%struct = OpTypeStruct -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateBuiltIns, TargetIsConstant) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %int0 BuiltIn Position -%int = OpTypeInt 32 0 -%int0 = OpConstant %int 0 -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateBuiltIns, TargetIsSpecConstant) { - const std::string text = R"( -OpCapability Shader -OpCapability Linkage -OpMemoryModel Logical GLSL450 -OpDecorate %int0 BuiltIn Position -%int = OpTypeInt 32 0 -%int0 = OpSpecConstant %int 0 -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - INSTANTIATE_TEST_SUITE_P( PrimitiveShadingRateOutputSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, diff --git a/third_party/spirv-tools/test/val/val_capability_test.cpp b/third_party/spirv-tools/test/val/val_capability_test.cpp index 82f8d38196..c432c3cfaf 100644 --- a/third_party/spirv-tools/test/val/val_capability_test.cpp +++ b/third_party/spirv-tools/test/val/val_capability_test.cpp @@ -1205,8 +1205,10 @@ INSTANTIATE_TEST_SUITE_P(Decoration, ValidateCapability, Values( std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt RelaxedPrecision\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var RelaxedPrecision\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Private %intt\n" + "%var = OpVariable %ptr Private\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + // Block applies to struct type. @@ -1224,93 +1226,125 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt RowMajor\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 RowMajor\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt ColMajor\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 ColMajor\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt ArrayStride 1\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %array ArrayStride 4\n" + "%intt = OpTypeInt 32 0\n" + "%array = OpTypeRuntimeArray %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt MatrixStride 1\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 MatrixStride 8\n" + "%floatt = OpTypeFloat 32\n" + "%float2 = OpTypeVector %floatt 2\n" + "%mat2x2 = OpTypeMatrix %float2 2\n" + "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid), MatrixDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt GLSLShared\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct GLSLShared\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt GLSLPacked\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct GLSLPacked\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" - "OpDecorate %intt CPacked\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %struct CPacked\n" + "%struct = OpTypeStruct\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt NoPerspective\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var NoPerspective\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Flat\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Flat\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Patch\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Patch\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Centroid\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Centroid\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Sample\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Sample\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Invariant\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Invariant\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Restrict\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Restrict\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Aliased\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Aliased\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Volatile\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Volatile\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" - "OpDecorate %intt Constant\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Constant\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Coherent\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Coherent\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + // NonWritable must target something valid, such as a storage image. @@ -1324,8 +1358,12 @@ std::make_pair(std::string(kOpenCLMemoryModel) + AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt NonReadable\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var NonReadable " + "%float = OpTypeFloat 32 " + "%imstor = OpTypeImage %float 2D 0 0 0 2 Unknown " + "%ptr = OpTypePointer UniformConstant %imstor " + "%var = OpVariable %ptr UniformConstant " + + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + // Uniform must target a non-void value. @@ -1342,8 +1380,10 @@ std::make_pair(std::string(kGLSL450MemoryModel) + KernelDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Stream 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Stream 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Output %intt\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), std::vector{"GeometryStreams"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" @@ -1360,33 +1400,44 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Index 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Index 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Binding 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Binding 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt DescriptorSet 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var DescriptorSet 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Offset 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpMemberDecorate %structt 0 Offset 0\n" + "%intt = OpTypeInt 32 0\n" + "%structt = OpTypeStruct %intt\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt XfbBuffer 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var XfbBuffer 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt XfbStride 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var XfbStride 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Uniform %intt\n" + "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" @@ -1410,8 +1461,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt InputAttachmentIndex 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var InputAttachmentIndex 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer UniformConstant %intt\n" + "%var = OpVariable %ptr UniformConstant\n" + std::string(kVoidFVoid), std::vector{"InputAttachment"}), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" @@ -1471,264 +1524,300 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapability, Values( std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn Position\n" + "OpDecorate %var BuiltIn Position\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), // Just mentioning PointSize, ClipDistance, or CullDistance as a BuiltIn does // not trigger the requirement for the associated capability. // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" + "OpDecorate %var BuiltIn PointSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn ClipDistance\n" + "OpDecorate %var BuiltIn ClipDistance\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" + "OpDecorate %var BuiltIn CullDistance\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexId\n" + "OpDecorate %var BuiltIn VertexId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceId\n" + "OpDecorate %var BuiltIn InstanceId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PrimitiveId\n" + "OpDecorate %var BuiltIn PrimitiveId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryTessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InvocationId\n" + "OpDecorate %var BuiltIn InvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryTessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn Layer\n" + "OpDecorate %var BuiltIn Layer\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), GeometryDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn ViewportIndex\n" + "OpDecorate %var BuiltIn ViewportIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"MultiViewport"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessLevelOuter\n" + "OpDecorate %var BuiltIn TessLevelOuter\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessLevelInner\n" + "OpDecorate %var BuiltIn TessLevelInner\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn TessCoord\n" + "OpDecorate %var BuiltIn TessCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PatchVertices\n" + "OpDecorate %var BuiltIn PatchVertices\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), TessellationDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FragCoord\n" + "OpDecorate %var BuiltIn FragCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn PointCoord\n" + "OpDecorate %var BuiltIn PointCoord\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FrontFacing\n" + "OpDecorate %var BuiltIn FrontFacing\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SampleId\n" + "OpDecorate %var BuiltIn SampleId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SamplePosition\n" + "OpDecorate %var BuiltIn SamplePosition\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), std::vector{"SampleRateShading"}), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn SampleMask\n" + "OpDecorate %var BuiltIn SampleMask\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn FragDepth\n" + "OpDecorate %var BuiltIn FragDepth\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn HelperInvocation\n" + "OpDecorate %var BuiltIn HelperInvocation\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexIndex\n" + "OpDecorate %var BuiltIn VertexIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceIndex\n" + "OpDecorate %var BuiltIn InstanceIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn NumWorkgroups\n" + "OpDecorate %var BuiltIn NumWorkgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn WorkgroupSize\n" + "OpDecorate %var BuiltIn WorkgroupId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn WorkgroupId\n" + "OpDecorate %var BuiltIn LocalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn LocalInvocationId\n" + "OpDecorate %var BuiltIn GlobalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn GlobalInvocationId\n" + "OpDecorate %var BuiltIn LocalInvocationIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), - AllCapabilities()), -std::make_pair(std::string(kOpenCLMemoryModel) + - "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn LocalInvocationIndex\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), AllCapabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn WorkDim\n" + "OpDecorate %var BuiltIn WorkDim\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalSize\n" + "OpDecorate %var BuiltIn GlobalSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn EnqueuedWorkgroupSize\n" + "OpDecorate %var BuiltIn EnqueuedWorkgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalOffset\n" + "OpDecorate %var BuiltIn GlobalOffset\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn GlobalLinearId\n" + "OpDecorate %var BuiltIn GlobalLinearId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupSize\n" + "OpDecorate %var BuiltIn SubgroupSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupMaxSize\n" + "OpDecorate %var BuiltIn SubgroupMaxSize\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn NumSubgroups\n" + "OpDecorate %var BuiltIn NumSubgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn NumEnqueuedSubgroups\n" + "OpDecorate %var BuiltIn NumEnqueuedSubgroups\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupId\n" + "OpDecorate %var BuiltIn SubgroupId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kGLSL450MemoryModel) + "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn SubgroupLocalInvocationId\n" + "OpDecorate %var BuiltIn SubgroupLocalInvocationId\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), KernelAndGroupNonUniformDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn VertexIndex\n" + "OpDecorate %var BuiltIn VertexIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" + - "OpDecorate %int0 BuiltIn InstanceIndex\n" + "OpDecorate %var BuiltIn InstanceIndex\n" "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()) ))); @@ -1742,11 +1831,11 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityVulkan10, ValuesIn(AllSpirV10Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" - "OpMemberDecorate %block 0 BuiltIn PointSize\n" - "%f32 = OpTypeFloat 32\n" - "%block = OpTypeStruct %f32\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), // Capabilities which should succeed. AllVulkan10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + @@ -1775,22 +1864,31 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityOpenGL40, ValuesIn(AllSpirV10Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn ClipDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn ClipDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllSpirV10Capabilities()) ))); @@ -1800,16 +1898,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11, ValuesIn(AllCapabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllVulkan11Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllVulkan11Capabilities()) ))); @@ -1819,16 +1922,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan12, ValuesIn(AllSpirV15Capabilities()), Values( std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn PointSize\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn PointSize\n" + "%float = OpTypeFloat 32\n" + "%ptr_output_float = OpTypePointer Output %float\n" + "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid), AllVulkan12Capabilities()), std::make_pair(std::string(kGLSL450MemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + - "OpDecorate %int0 BuiltIn CullDistance\n" - "%intt = OpTypeInt 32 0\n" - "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + "OpEntryPoint Vertex %func \"shader\" %var\n" + + "OpDecorate %var BuiltIn CullDistance\n" + "%float = OpTypeFloat 32\n" + "%int = OpTypeInt 32 0\n" + "%int_1 = OpConstant %int 1\n" + "%array = OpTypeArray %float %int_1\n" + "%ptr = OpTypePointer Output %array\n" + "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid), AllVulkan12Capabilities()) ))); 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 6ae2ee6db9..311cfa7713 100644 --- a/third_party/spirv-tools/test/val/val_cfg_test.cpp +++ b/third_party/spirv-tools/test/val/val_cfg_test.cpp @@ -4229,6 +4229,67 @@ OpFunctionEnd ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) { + // In this test, we try to make a case where the false branches + // to %20 and %60 from blocks %10 and %50 must be registered + // during the validity check for sturctured selections. + // However, an error is caught earlier in the flow, that the + // branches from %100 to %20 and %60 violate dominance. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %bool = OpTypeBool + %cond = OpUndef %bool + + %main = OpFunction %void None %void_fn + + %1 = OpLabel + OpSelectionMerge %999 None + OpBranchConditional %cond %10 %100 + + %10 = OpLabel + OpSelectionMerge %30 None ; force registration of %30 + OpBranchConditional %cond %30 %20 ; %20 should be registered too + + %20 = OpLabel + OpBranch %30 + + %30 = OpLabel ; merge for first if + OpBranch %50 + + + %50 = OpLabel + OpSelectionMerge %70 None ; force registration of %70 + OpBranchConditional %cond %70 %60 ; %60 should be registered + + %60 = OpLabel + OpBranch %70 + + %70 = OpLabel ; merge for second if + OpBranch %999 + + %100 = OpLabel + OpBranchConditional %cond %20 %60 ; should require a merge + + %999 = OpLabel + OpReturn + + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The selection construct with the selection header " + "8[%8] does not dominate the merge block 10[%10]\n")); +} + TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) { const std::string text = R"( OpCapability Shader @@ -4399,6 +4460,125 @@ OpFunctionEnd "1[%BAD], but not via a structured exit")); } +TEST_F(ValidateCFG, SwitchSelectorNotAnInt) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %default None +OpSwitch %float_1 %default +%default = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Selector type must be OpTypeInt")); +} + +TEST_F(ValidateCFG, SwitchDefaultNotALabel) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %default None +OpSwitch %int_1 %int_1 +%default = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Default must be an OpLabel instruction")); +} + +TEST_F(ValidateCFG, BlockDepthRecursion) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef %3 %4 +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpBranch %5 +%5 = OpLabel +OpSelectionMerge %2 None +OpBranchConditional %undef %6 %7 +%6 = OpLabel +OpReturn +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + +TEST_F(ValidateCFG, BadStructuredExitBackwardsMerge) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef %4 %6 +%6 = OpLabel +OpSelectionMerge %7 None +OpBranchConditional %undef %8 %9 +%7 = OpLabel +OpReturn +%8 = OpLabel +OpBranch %5 +%9 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %undef %5 %5 +%5 = OpLabel +OpBranch %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_composites_test.cpp b/third_party/spirv-tools/test/val/val_composites_test.cpp index bf7caa9fae..507ee88945 100644 --- a/third_party/spirv-tools/test/val/val_composites_test.cpp +++ b/third_party/spirv-tools/test/val/val_composites_test.cpp @@ -86,10 +86,10 @@ OpCapability Float64 %f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 %f32vec2arr3 = OpTypeArray %f32vec2 %u32_3 -%f32vec2rarr = OpTypeRuntimeArray %f32vec2 +%f32vec2arr2 = OpTypeArray %f32vec2 %u32_2 %f32u32struct = OpTypeStruct %f32 %u32 -%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2rarr %f32u32struct +%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2arr2 %f32u32struct %ptr_big_struct = OpTypePointer Uniform %big_struct %var_big_struct = OpVariable %ptr_big_struct Uniform @@ -150,7 +150,6 @@ OpMemoryModel Logical GLSL450 ; uniform blockName { ; S s; ; bool cond; -; RunTimeArray arr; ; } %f32arr = OpTypeRuntimeArray %float @@ -161,7 +160,7 @@ OpMemoryModel Logical GLSL450 %_ptr_Function_vec4 = OpTypePointer Function %v4float %_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float %struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3 -%struct_blockName = OpTypeStruct %struct_s %int %f32arr +%struct_blockName = OpTypeStruct %struct_s %int %_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName %_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s %_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3 @@ -644,8 +643,8 @@ TEST_F(ValidateComposites, CompositeExtractSuccess) { %val12 = OpCompositeExtract %f32 %struct 2 2 1 %val13 = OpCompositeExtract %f32vec2 %struct 3 2 %val14 = OpCompositeExtract %f32 %struct 3 2 1 -%val15 = OpCompositeExtract %f32vec2 %struct 4 100 -%val16 = OpCompositeExtract %f32 %struct 4 1000 1 +%val15 = OpCompositeExtract %f32vec2 %struct 4 1 +%val16 = OpCompositeExtract %f32 %struct 4 0 1 %val17 = OpCompositeExtract %f32 %struct 5 0 %val18 = OpCompositeExtract %u32 %struct 5 1 )"; @@ -868,8 +867,8 @@ TEST_F(ValidateComposites, CompositeInsertSuccess) { %val12 = OpCompositeInsert %big_struct %f32_3 %struct 2 2 1 %val13 = OpCompositeInsert %big_struct %f32vec2_01 %struct 3 2 %val14 = OpCompositeInsert %big_struct %f32_3 %struct 3 2 1 -%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 100 -%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1 +%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 1 +%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 0 1 %val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0 %val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1 )"; @@ -1382,8 +1381,8 @@ TEST_F(ValidateComposites, CompositeExtractStructIndexOutOfBoundBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds, can not find index 3 in the " - "structure '25'. This structure has 3 members. " - "Largest valid index is 2.")); + "structure '25'. This structure has 2 members. " + "Largest valid index is 1.")); } // Invalid. Index into a struct is larger than the number of struct members. @@ -1403,8 +1402,8 @@ TEST_F(ValidateComposites, CompositeInsertStructIndexOutOfBoundBad) { EXPECT_THAT( getDiagnosticString(), HasSubstr("Index is out of bounds, can not find index 3 in the structure " - " '25'. This structure has 3 members. Largest valid index " - "is 2.")); + " '25'. This structure has 2 members. Largest valid index " + "is 1.")); } // #1403: Ensure that the default spec constant value is not used to check the diff --git a/third_party/spirv-tools/test/val/val_decoration_test.cpp b/third_party/spirv-tools/test/val/val_decoration_test.cpp index 096fd172dd..f2953edcc4 100644 --- a/third_party/spirv-tools/test/val/val_decoration_test.cpp +++ b/third_party/spirv-tools/test/val/val_decoration_test.cpp @@ -687,8 +687,7 @@ TEST_F(ValidateDecorations, BlockDecoratingArrayBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, BlockDecoratingIntBad) { @@ -713,8 +712,7 @@ TEST_F(ValidateDecorations, BlockDecoratingIntBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, BlockMissingOffsetBad) { @@ -6129,9 +6127,7 @@ TEST_F(ValidateDecorations, NonWritableLabelTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %label = OpLabel")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { @@ -6140,9 +6136,7 @@ TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %void = OpTypeVoid")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableValueTargetBad) { @@ -6151,9 +6145,7 @@ TEST_F(ValidateDecorations, NonWritableValueTargetBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration must be a " - "memory object declaration (a variable or a function " - "parameter)\n %float_0 = OpConstant %float 0")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, NonWritableValueParamBad) { @@ -6161,10 +6153,7 @@ TEST_F(ValidateDecorations, NonWritableValueParamBad) { CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %param_f = OpFunctionParameter %float")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); } TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) { @@ -6467,8 +6456,7 @@ OpFunctionEnd CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of Component decoration must be " - "a memory object declaration")); + HasSubstr("must be a memory object declaration")); } TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { @@ -6767,8 +6755,8 @@ TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { )"; CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); - EXPECT_THAT(getDiagnosticString(), Eq("")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type")); } TEST_F(ValidateDecorations, VulkanStorageBufferBlock) { @@ -7164,9 +7152,7 @@ OpDecorate %struct Location 0 CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Location decoration can only be applied to a variable " - "or member of a structure type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); } TEST_F(ValidateDecorations, LocationFloatBad) { @@ -7180,9 +7166,7 @@ OpDecorate %float Location 0 CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Location decoration can only be applied to a variable " - "or member of a structure type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); } TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) { @@ -7571,9 +7555,7 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) { CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Block decoration on a non-struct type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type")); } TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) { @@ -7648,6 +7630,213 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) { "member 0 at offset 1 is not aligned to 4")); } +TEST_F(ValidateDecorations, BadMatrixStrideUniform) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer Uniform %block +%var = OpVariable %block_ptr Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 0 is " + "a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %block_ptr StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow standard storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStridePushConstant) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer PushConstant %block +%var = OpVariable %block_ptr PushConstant +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in PushConstant " + "storage class must follow standard storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 MatrixStride 3 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%matrix4x4 = OpTypeMatrix %float4 4 +%block = OpTypeStruct %matrix4x4 +%block_ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %block_ptr StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->scalar_block_layout = true; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow scalar storage buffer layout rules: " + "member 0 is a matrix with stride 3 not satisfying alignment to 4")); +} + +TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %array ArrayStride 4 +OpDecorate %outer Block +OpMemberDecorate %outer 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%inner = OpTypeStruct %int +%array = OpTypeArray %inner %int_4 +%outer = OpTypeStruct %array +%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer +%var = OpVariable %ptr_ssbo_outer StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 3 decorated as Block must be explicitly " + "laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, AllOnesOffset) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %outer Block +OpMemberDecorate %outer 0 Offset 0 +OpMemberDecorate %struct 0 Offset 4294967295 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%outer = OpTypeStruct %struct +%ptr = OpTypePointer Uniform %outer +%var = OpVariable %ptr Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("decorated as Block must be explicitly laid out with " + "Offset decorations")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_derivatives_test.cpp b/third_party/spirv-tools/test/val/val_derivatives_test.cpp index 606abb93dd..0a8466107c 100644 --- a/third_party/spirv-tools/test/val/val_derivatives_test.cpp +++ b/third_party/spirv-tools/test/val/val_derivatives_test.cpp @@ -160,6 +160,31 @@ TEST_F(ValidateDerivatives, OpDPdxWrongExecutionModel) { "execution model: DPdx")); } +TEST_F(ValidateDerivatives, NoExecutionModeGLCompute) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float4 = OpTypeVector %float 4 +%undef = OpUndef %float4 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%derivative = OpDPdy %float4 %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Derivative instructions require " + "DerivativeGroupQuadsNV or DerivativeGroupLinearNV " + "execution mode for GLCompute execution model")); +} + using ValidateHalfDerivatives = spvtest::ValidateBase; TEST_P(ValidateHalfDerivatives, ScalarFailure) { diff --git a/third_party/spirv-tools/test/val/val_ext_inst_debug_test.cpp b/third_party/spirv-tools/test/val/val_ext_inst_debug_test.cpp new file mode 100644 index 0000000000..307a800959 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_ext_inst_debug_test.cpp @@ -0,0 +1,5313 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions. +// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported +// by standard SPIR-V). + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateOldDebugInfo = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; +using ValidateXDebugInfo = spvtest::ValidateBase; +using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeInheritance = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateVulkan100DebugInfo = spvtest::ValidateBase; + +std::string GenerateShaderCodeForDebugInfo( + const std::string& op_string_instructions, + const std::string& op_const_instructions, + const std::string& debug_instructions_before_main, const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"" + << " %f32_output" + << " %f32vec2_output" + << " %u32_output" + << " %u32vec2_output" + << " %u64_output" + << " %f32_input" + << " %f32vec2_input" + << " %u32_input" + << " %u32vec2_input" + << " %u64_input" + << "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << op_string_instructions; + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%u16 = OpTypeInt 16 0 +%s16 = OpTypeInt 16 1 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec2 = OpTypeVector %s32 2 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%u64vec2 = OpTypeVector %u64 2 +%s64vec2 = OpTypeVector %s64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 +%f16_h = OpConstant %f16 0.5 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +)"; + + ss << op_const_instructions; + + ss << R"( +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 + +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%f32_ptr_output = OpTypePointer Output %f32 +%f32vec2_ptr_output = OpTypePointer Output %f32vec2 + +%u32_ptr_output = OpTypePointer Output %u32 +%u32vec2_ptr_output = OpTypePointer Output %u32vec2 + +%u64_ptr_output = OpTypePointer Output %u64 + +%f32_output = OpVariable %f32_ptr_output Output +%f32vec2_output = OpVariable %f32vec2_ptr_output Output + +%u32_output = OpVariable %u32_ptr_output Output +%u32vec2_output = OpVariable %u32vec2_ptr_output Output + +%u64_output = OpVariable %u64_ptr_output Output + +%f32_ptr_input = OpTypePointer Input %f32 +%f32vec2_ptr_input = OpTypePointer Input %f32vec2 + +%u32_ptr_input = OpTypePointer Input %u32 +%u32vec2_ptr_input = OpTypePointer Input %u32vec2 + +%u64_ptr_input = OpTypePointer Input %u64 + +%f32_ptr_function = OpTypePointer Function %f32 + +%f32_input = OpVariable %f32_ptr_input Input +%f32vec2_input = OpVariable %f32vec2_ptr_input Input + +%u32_input = OpVariable %u32_ptr_input Input +%u32vec2_input = OpVariable %u32vec2_ptr_input Input + +%u64_input = OpVariable %u64_ptr_input Input + +%u32_ptr_function = OpTypePointer Function %u32 + +%struct_f16_u16 = OpTypeStruct %f16 %u16 +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 +)"; + + ss << debug_instructions_before_main; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "DebugInfo" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, VulkanDebugInfo100DebugScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 %u32_1 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%main_def = OpExtInst %void %DbgExt DebugFunctionDefinition %main_info %main +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +INSTANTIATE_TEST_SUITE_P( + AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, + ::testing::ValuesIn(std::vector{ + "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", + "%no_scope = OpExtInst %void %DbgExt DebugNoScope", + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +%foo = OpVariable %f32_ptr_function Function +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%ty_name = OpString "struct VS_OUTPUT" +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceWrongResultType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected result type must be a result id of " + "OpTypeVoid")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceFailFile) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand File must be a result id of " + "OpString")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceFailSource) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Text must be a result id of " + "OpString")); +} + +TEST_P(ValidateXDebugInfo, DebugSourceNoText) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + GetParam(), "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +INSTANTIATE_TEST_SUITE_P(OpenCLAndVkDebugInfo100, ValidateXDebugInfo, + ::testing::ValuesIn(std::vector{ + R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)", + R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)", + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Source must be a result id of " + "DebugSource")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugCompilationUnitFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %src %u32_5 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, dbg_inst, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Source must be a result id of " + "DebugSource")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Name must be a result id of " + "OpString")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailName) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %u32_32 %u32_32 %u32_3 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Name must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Size must be a result id of " + "OpConstant")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name %u32_3 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Size must be a result id of " + "OpConstant")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} +TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifier) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifierFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%int_name = OpString "int" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug " + "type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_32 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%uint_name = OpString "uint" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_6 = OpConstant %u32 6 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_6 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %u32_32 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug " + "type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_6 = OpConstant %u32 6 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_1 %main_info %u32_4 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_5 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)", + "Name"), + std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)", + "Base Type"), + std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)", + "Source"), + std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)", + "Parent"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit)", + "Base Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Return Type is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Parameter Types is not a valid debug type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionAndParams) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void %float_info %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailReturn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %dbg_src %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Return Type is not a valid debug type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %void +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Parameter Types is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)", + "Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src %u32_32 %u32_3 %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %void %u32_3 %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_0 %u32_3 %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %u32_1)", + "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + ss << R"( +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + " must be ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)", + "Members"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float pos : SV_POSITION; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (!param.second.empty()) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); + } +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT {}; +struct foo : VS_OUTPUT {}; +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%foo_name = OpString "foo" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be a result id of"), + std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)", + "Parent must be a result id of"), + std::make_pair( + R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be class or struct debug type"), + std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)", + "Parent must be class or struct debug type"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)", + "Offset"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeComposite) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3 +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %foo_name %u32_0 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugTypeComposite, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3 +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + " must be ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %comp_unit %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %dbg_src %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %u32_128 %u32_128 %u32_3 %VS_OUTPUT_pos_info)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src %u32_3 %VS_OUTPUT_pos_info)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %dbg_src %VS_OUTPUT_pos_info)", + "Flags"), + std::make_pair( + R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %dbg_src)", + "Members"), + })); + +TEST_P(ValidateVulkan100DebugInfoDebugTypeMember, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float pos : SV_POSITION; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%u32_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (!param.second.empty()) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); + } +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %void %u32_32 %u32_3)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %void %u32_3)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %void)", + "Flags"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)", + "Function"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, + ValidateOpenCL100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", + "Linkage Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugFunctionDeclaration) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugFunction, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3 %u32_13)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3 %u32_13)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateVulkan100DebugInfoDebugFunctionDeclaration, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_12 = OpConstant %u32 12 +%u32_13 = OpConstant %u32 13 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, + ValidateVulkan100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3)", + "Linkage Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"), + std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"), + std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src %u32_1 %u32_1 %comp_unit %main_name +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit %u32_1 %u32_1 %comp_unit %main_name)", + "Source"), + std::make_pair(R"(%dbg_src %u32_1 %u32_1 %dbg_src %main_name)", + "Parent"), + std::make_pair(R"(%dbg_src %u32_1 %u32_1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)", + "Parent"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugLocalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugLocalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_1 %u32_10 %dbg_src %u32_3 %u32_0)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_COLOR + %4 = OpString "test.hlsl" + OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %11 = OpString "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %14 = OpString "float" + %17 = OpString "src.main" + %20 = OpString "foo" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpName %param_var_foo "param.var.foo" + OpName %src_main "src.main" + OpName %foo "foo" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %29 = OpTypeFunction %void %_ptr_Function_float + OpLine %4 1 21 +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %10 = OpExtInst %void %1 DebugExpression + %12 = OpExtInst %void %1 DebugSource %4 %11 + %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float + %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 + OpLine %4 1 1 + %main = OpFunction %void None %23 + %24 = OpLabel + OpLine %4 1 17 +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + OpLine %4 1 1 + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + OpLine %4 1 17 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpLine %4 1 29 + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), + std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugDeclare) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_COLOR + %4 = OpString "test.hlsl" + OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %11 = OpString "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %14 = OpString "float" + %17 = OpString "src.main" + %20 = OpString "foo" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpName %param_var_foo "param.var.foo" + OpName %src_main "src.main" + OpName %foo "foo" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %u32_0 = OpConstant %uint 0 + %u32_1 = OpConstant %uint 1 + %u32_2 = OpConstant %uint 2 + %u32_3 = OpConstant %uint 3 + %u32_4 = OpConstant %uint 4 + %u32_5 = OpConstant %uint 5 + %u32_10 = OpConstant %uint 10 + %u32_17 = OpConstant %uint 17 + %u32_28 = OpConstant %uint 28 + %u32_32 = OpConstant %uint 32 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %29 = OpTypeFunction %void %_ptr_Function_float + OpLine %4 1 21 +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %10 = OpExtInst %void %1 DebugExpression + %12 = OpExtInst %void %1 DebugSource %4 %11 + %13 = OpExtInst %void %1 DebugCompilationUnit %u32_1 %u32_4 %12 %u32_5 + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 %u32_3 %u32_0 + %16 = OpExtInst %void %1 DebugTypeFunction %u32_3 %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 %u32_1 %u32_1 %13 %17 %u32_3 %u32_1 + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 %u32_1 %u32_17 %18 %u32_4 %u32_0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_28 %18 + %main = OpFunction %void None %23 + %24 = OpLabel +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugDeclare, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugDeclare, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), + std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation Deref +%op1 = OpExtInst %void %DbgExt DebugOperation Plus +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation Deref +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Operation must be a result id of DebugOperation")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation %u32_0 +%op1 = OpExtInst %void %DbgExt DebugOperation %u32_1 +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation %u32_0 +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Operation must be a result id of DebugOperation")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %float_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", + "Variable"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %t %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %dbgNone %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" + << param.first; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %dbg_src %foo_name %f32_input %u32_3)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %void %f32_input %u32_3)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %void %u32_3)", + "Variable"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %inlined_at +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %inlined_at %inlined_at +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail2) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %main_info +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), + std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_F(ValidateVulkan100DebugInfo, DebugValue) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_32 %u32_4 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0 +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src %u32_0 %u32_0 %comp_unit %u32_4 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %len_info +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateVulkan100DebugInfoDebugValue, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_10 = OpConstant %u32 10 +%u32_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0 +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << param.first; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %u32_32 %null_expr %u32_3)", + "Local Variable"), + std::make_pair(R"(%foo_info %u32_32 %dbg_src %u32_3)", "Expression"), + std::make_pair(R"(%foo_info %u32_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_F(ValidateVulkan100DebugInfo, VulkanDebugInfoSample) { + std::ostringstream ss; + ss << R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %id_1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %id_MainPs "MainPs" %id_in_var_TEXCOORD2 %id_out_var_SV_Target0 + OpExecutionMode %id_MainPs OriginUpperLeft + %id_7 = OpString "foo.frag" + %id_27 = OpString "float" + %id_32 = OpString "vColor" + %id_36 = OpString "PS_OUTPUT" + %id_42 = OpString "vTextureCoords" + %id_46 = OpString "PS_INPUT" + %id_49 = OpString "MainPs" + %id_50 = OpString "" + %id_55 = OpString "ps_output" + %id_59 = OpString "i" + %id_63 = OpString "@type.sampler" + %id_64 = OpString "type.sampler" + %id_66 = OpString "g_sAniso" + %id_69 = OpString "@type.2d.image" + %id_70 = OpString "type.2d.image" + %id_72 = OpString "TemplateParam" + %id_75 = OpString "g_tColor" + OpName %id_type_2d_image "type.2d.image" + OpName %id_g_tColor "g_tColor" + OpName %id_type_sampler "type.sampler" + OpName %id_g_sAniso "g_sAniso" + OpName %id_in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %id_out_var_SV_Target0 "out.var.SV_Target0" + OpName %id_MainPs "MainPs" + OpName %id_PS_INPUT "PS_INPUT" + OpMemberName %id_PS_INPUT 0 "vTextureCoords" + OpName %id_param_var_i "param.var.i" + OpName %id_PS_OUTPUT "PS_OUTPUT" + OpMemberName %id_PS_OUTPUT 0 "vColor" + OpName %id_src_MainPs "src.MainPs" + OpName %id_i "i" + OpName %id_bb_entry "bb.entry" + OpName %id_ps_output "ps_output" + OpName %id_type_sampled_image "type.sampled.image" + OpDecorate %id_in_var_TEXCOORD2 Location 0 + OpDecorate %id_out_var_SV_Target0 Location 0 + OpDecorate %id_g_tColor DescriptorSet 0 + OpDecorate %id_g_tColor Binding 0 + OpDecorate %id_g_sAniso DescriptorSet 0 + OpDecorate %id_g_sAniso Binding 1 + %id_int = OpTypeInt 32 1 + %id_int_0 = OpConstant %id_int 0 + %id_uint = OpTypeInt 32 0 + %id_uint_32 = OpConstant %id_uint 32 + %id_float = OpTypeFloat 32 +%id_type_2d_image = OpTypeImage %id_float 2D 2 0 0 1 Unknown +%id__ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %id_type_2d_image +%id_type_sampler = OpTypeSampler +%id__ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %id_type_sampler + %id_v2float = OpTypeVector %id_float 2 +%id__ptr_Input_v2float = OpTypePointer Input %id_v2float + %id_v4float = OpTypeVector %id_float 4 +%id__ptr_Output_v4float = OpTypePointer Output %id_v4float + %id_void = OpTypeVoid + %id_uint_1 = OpConstant %id_uint 1 + %id_uint_4 = OpConstant %id_uint 4 + %id_uint_5 = OpConstant %id_uint 5 + %id_uint_3 = OpConstant %id_uint 3 + %id_uint_0 = OpConstant %id_uint 0 + %id_uint_128 = OpConstant %id_uint 128 + %id_uint_12 = OpConstant %id_uint 12 + %id_uint_10 = OpConstant %id_uint 10 + %id_uint_8 = OpConstant %id_uint 8 + %id_uint_2 = OpConstant %id_uint 2 + %id_uint_64 = OpConstant %id_uint 64 + %id_uint_7 = OpConstant %id_uint 7 + %id_uint_15 = OpConstant %id_uint 15 + %id_uint_16 = OpConstant %id_uint 16 + %id_uint_17 = OpConstant %id_uint 17 + %id_uint_29 = OpConstant %id_uint 29 + %id_uint_14 = OpConstant %id_uint 14 + %id_uint_11 = OpConstant %id_uint 11 + %id_78 = OpTypeFunction %id_void + %id_PS_INPUT = OpTypeStruct %id_v2float +%id__ptr_Function_PS_INPUT = OpTypePointer Function %id_PS_INPUT + %id_PS_OUTPUT = OpTypeStruct %id_v4float + %id_89 = OpTypeFunction %id_PS_OUTPUT %id__ptr_Function_PS_INPUT +%id__ptr_Function_PS_OUTPUT = OpTypePointer Function %id_PS_OUTPUT + %id_uint_20 = OpConstant %id_uint 20 + %id_uint_19 = OpConstant %id_uint 19 + %id_uint_26 = OpConstant %id_uint 26 + %id_uint_46 = OpConstant %id_uint 46 +%id__ptr_Function_v2float = OpTypePointer Function %id_v2float + %id_uint_57 = OpConstant %id_uint 57 + %id_uint_78 = OpConstant %id_uint 78 +%id_type_sampled_image = OpTypeSampledImage %id_type_2d_image + %id_uint_81 = OpConstant %id_uint 81 +%id__ptr_Function_v4float = OpTypePointer Function %id_v4float + %id_g_tColor = OpVariable %id__ptr_UniformConstant_type_2d_image UniformConstant + %id_g_sAniso = OpVariable %id__ptr_UniformConstant_type_sampler UniformConstant +%id_in_var_TEXCOORD2 = OpVariable %id__ptr_Input_v2float Input +%id_out_var_SV_Target0 = OpVariable %id__ptr_Output_v4float Output + %id_22 = OpExtInst %id_void %id_1 DebugSource %id_7 + %id_23 = OpExtInst %id_void %id_1 DebugCompilationUnit %id_uint_1 %id_uint_4 %id_22 %id_uint_5 + %id_28 = OpExtInst %id_void %id_1 DebugTypeBasic %id_27 %id_uint_32 %id_uint_3 %id_uint_0 + %id_31 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_4 + %id_34 = OpExtInst %id_void %id_1 DebugTypeMember %id_32 %id_31 %id_22 %id_uint_12 %id_uint_12 %id_uint_0 %id_uint_128 %id_uint_3 + %id_37 = OpExtInst %id_void %id_1 DebugTypeComposite %id_36 %id_uint_1 %id_22 %id_uint_10 %id_uint_8 %id_23 %id_36 %id_uint_128 %id_uint_3 %id_34 + %id_40 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_2 + %id_44 = OpExtInst %id_void %id_1 DebugTypeMember %id_42 %id_40 %id_22 %id_uint_7 %id_uint_12 %id_uint_0 %id_uint_64 %id_uint_3 + %id_47 = OpExtInst %id_void %id_1 DebugTypeComposite %id_46 %id_uint_1 %id_22 %id_uint_5 %id_uint_8 %id_23 %id_46 %id_uint_64 %id_uint_3 %id_44 + %id_48 = OpExtInst %id_void %id_1 DebugTypeFunction %id_uint_3 %id_37 %id_47 + %id_51 = OpExtInst %id_void %id_1 DebugFunction %id_49 %id_48 %id_22 %id_uint_15 %id_uint_1 %id_23 %id_50 %id_uint_3 %id_uint_16 + %id_54 = OpExtInst %id_void %id_1 DebugLexicalBlock %id_22 %id_uint_16 %id_uint_1 %id_51 + %id_56 = OpExtInst %id_void %id_1 DebugLocalVariable %id_55 %id_37 %id_22 %id_uint_17 %id_uint_15 %id_54 %id_uint_4 + %id_58 = OpExtInst %id_void %id_1 DebugExpression + %id_60 = OpExtInst %id_void %id_1 DebugLocalVariable %id_59 %id_47 %id_22 %id_uint_15 %id_uint_29 %id_51 %id_uint_4 %id_uint_1 + %id_62 = OpExtInst %id_void %id_1 DebugInfoNone + %id_65 = OpExtInst %id_void %id_1 DebugTypeComposite %id_63 %id_uint_1 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_64 %id_62 %id_uint_3 + %id_67 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_66 %id_65 %id_22 %id_uint_3 %id_uint_14 %id_23 %id_66 %id_g_sAniso %id_uint_8 + %id_71 = OpExtInst %id_void %id_1 DebugTypeComposite %id_69 %id_uint_0 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_70 %id_62 %id_uint_3 + %id_73 = OpExtInst %id_void %id_1 DebugTypeTemplateParameter %id_72 %id_31 %id_62 %id_22 %id_uint_0 %id_uint_0 + %id_74 = OpExtInst %id_void %id_1 DebugTypeTemplate %id_71 %id_73 + %id_76 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_75 %id_74 %id_22 %id_uint_1 %id_uint_11 %id_23 %id_75 %id_g_tColor %id_uint_8 + %id_MainPs = OpFunction %id_void None %id_78 + %id_79 = OpLabel +%id_param_var_i = OpVariable %id__ptr_Function_PS_INPUT Function + %id_83 = OpLoad %id_v2float %id_in_var_TEXCOORD2 + %id_84 = OpCompositeConstruct %id_PS_INPUT %id_83 + OpStore %id_param_var_i %id_84 + %id_86 = OpFunctionCall %id_PS_OUTPUT %id_src_MainPs %id_param_var_i + %id_88 = OpCompositeExtract %id_v4float %id_86 0 + OpStore %id_out_var_SV_Target0 %id_88 + OpReturn + OpFunctionEnd + %id_src_MainPs = OpFunction %id_PS_OUTPUT None %id_89 + %id_i = OpFunctionParameter %id__ptr_Function_PS_INPUT + %id_bb_entry = OpLabel + %id_ps_output = OpVariable %id__ptr_Function_PS_OUTPUT Function + %id_94 = OpExtInst %id_void %id_1 DebugScope %id_51 + %id_97 = OpExtInst %id_void %id_1 DebugDeclare %id_60 %id_i %id_58 + %id_99 = OpExtInst %id_void %id_1 DebugFunctionDefinition %id_51 %id_src_MainPs + %id_100 = OpExtInst %id_void %id_1 DebugScope %id_54 + %id_102 = OpExtInst %id_void %id_1 DebugDeclare %id_56 %id_ps_output %id_58 + %id_106 = OpLoad %id_type_2d_image %id_g_tColor + %id_109 = OpLoad %id_type_sampler %id_g_sAniso + %id_114 = OpAccessChain %id__ptr_Function_v2float %id_i %id_int_0 + %id_115 = OpLoad %id_v2float %id_114 + %id_119 = OpSampledImage %id_type_sampled_image %id_106 %id_109 + %id_120 = OpImageSampleImplicitLod %id_v4float %id_119 %id_115 None + %id_123 = OpAccessChain %id__ptr_Function_v4float %id_ps_output %id_int_0 + OpStore %id_123 %id_120 + %id_125 = OpLoad %id_PS_OUTPUT %id_ps_output + OpReturnValue %id_125 + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_ext_inst_test.cpp b/third_party/spirv-tools/test/val/val_ext_inst_test.cpp index b73ec3415d..2b6df04ddd 100644 --- a/third_party/spirv-tools/test/val/val_ext_inst_test.cpp +++ b/third_party/spirv-tools/test/val/val_ext_inst_test.cpp @@ -33,33 +33,6 @@ using ::testing::HasSubstr; using ::testing::Not; using ValidateExtInst = spvtest::ValidateBase; -using ValidateOldDebugInfo = spvtest::ValidateBase; -using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; -using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; -using ValidateOpenCL100DebugInfoDebugTypedef = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeEnum = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeComposite = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeMember = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugTypeInheritance = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugFunction = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugLexicalBlock = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugLocalVariable = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugGlobalVariable = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugDeclare = - spvtest::ValidateBase>; -using ValidateOpenCL100DebugInfoDebugValue = - spvtest::ValidateBase>; using ValidateGlslStd450SqrtLike = spvtest::ValidateBase; using ValidateGlslStd450FMinLike = spvtest::ValidateBase; using ValidateGlslStd450FClampLike = spvtest::ValidateBase; @@ -488,1685 +461,6 @@ OpFunctionEnd)"; return ss.str(); } -std::string GenerateShaderCodeForDebugInfo( - const std::string& op_string_instructions, - const std::string& op_const_instructions, - const std::string& debug_instructions_before_main, const std::string& body, - const std::string& capabilities_and_extensions = "", - const std::string& execution_model = "Fragment") { - std::ostringstream ss; - ss << R"( -OpCapability Shader -OpCapability Float16 -OpCapability Float64 -OpCapability Int16 -OpCapability Int64 -)"; - - ss << capabilities_and_extensions; - ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; - ss << "OpMemoryModel Logical GLSL450\n"; - ss << "OpEntryPoint " << execution_model << " %main \"main\"" - << " %f32_output" - << " %f32vec2_output" - << " %u32_output" - << " %u32vec2_output" - << " %u64_output" - << " %f32_input" - << " %f32vec2_input" - << " %u32_input" - << " %u32vec2_input" - << " %u64_input" - << "\n"; - if (execution_model == "Fragment") { - ss << "OpExecutionMode %main OriginUpperLeft\n"; - } - - ss << op_string_instructions; - - ss << R"( -%void = OpTypeVoid -%func = OpTypeFunction %void -%bool = OpTypeBool -%f16 = OpTypeFloat 16 -%f32 = OpTypeFloat 32 -%f64 = OpTypeFloat 64 -%u32 = OpTypeInt 32 0 -%s32 = OpTypeInt 32 1 -%u64 = OpTypeInt 64 0 -%s64 = OpTypeInt 64 1 -%u16 = OpTypeInt 16 0 -%s16 = OpTypeInt 16 1 -%f32vec2 = OpTypeVector %f32 2 -%f32vec3 = OpTypeVector %f32 3 -%f32vec4 = OpTypeVector %f32 4 -%f64vec2 = OpTypeVector %f64 2 -%f64vec3 = OpTypeVector %f64 3 -%f64vec4 = OpTypeVector %f64 4 -%u32vec2 = OpTypeVector %u32 2 -%u32vec3 = OpTypeVector %u32 3 -%s32vec2 = OpTypeVector %s32 2 -%u32vec4 = OpTypeVector %u32 4 -%s32vec4 = OpTypeVector %s32 4 -%u64vec2 = OpTypeVector %u64 2 -%s64vec2 = OpTypeVector %s64 2 -%f64mat22 = OpTypeMatrix %f64vec2 2 -%f32mat22 = OpTypeMatrix %f32vec2 2 -%f32mat23 = OpTypeMatrix %f32vec2 3 -%f32mat32 = OpTypeMatrix %f32vec3 2 -%f32mat33 = OpTypeMatrix %f32vec3 3 - -%f32_0 = OpConstant %f32 0 -%f32_1 = OpConstant %f32 1 -%f32_2 = OpConstant %f32 2 -%f32_3 = OpConstant %f32 3 -%f32_4 = OpConstant %f32 4 -%f32_h = OpConstant %f32 0.5 -%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 -%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 -%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 -%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 -%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 -%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 - -%f64_0 = OpConstant %f64 0 -%f64_1 = OpConstant %f64 1 -%f64_2 = OpConstant %f64 2 -%f64_3 = OpConstant %f64 3 -%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 -%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 -%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 - -%f16_0 = OpConstant %f16 0 -%f16_1 = OpConstant %f16 1 -%f16_h = OpConstant %f16 0.5 - -%u32_0 = OpConstant %u32 0 -%u32_1 = OpConstant %u32 1 -%u32_2 = OpConstant %u32 2 -%u32_3 = OpConstant %u32 3 - -%s32_0 = OpConstant %s32 0 -%s32_1 = OpConstant %s32 1 -%s32_2 = OpConstant %s32 2 -%s32_3 = OpConstant %s32 3 - -%u64_0 = OpConstant %u64 0 -%u64_1 = OpConstant %u64 1 -%u64_2 = OpConstant %u64 2 -%u64_3 = OpConstant %u64 3 - -%s64_0 = OpConstant %s64 0 -%s64_1 = OpConstant %s64 1 -%s64_2 = OpConstant %s64 2 -%s64_3 = OpConstant %s64 3 -)"; - - ss << op_const_instructions; - - ss << R"( -%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 -%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 - -%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 -%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 - -%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 -%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 - -%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 -%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 - -%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 -%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 - -%f32_ptr_output = OpTypePointer Output %f32 -%f32vec2_ptr_output = OpTypePointer Output %f32vec2 - -%u32_ptr_output = OpTypePointer Output %u32 -%u32vec2_ptr_output = OpTypePointer Output %u32vec2 - -%u64_ptr_output = OpTypePointer Output %u64 - -%f32_output = OpVariable %f32_ptr_output Output -%f32vec2_output = OpVariable %f32vec2_ptr_output Output - -%u32_output = OpVariable %u32_ptr_output Output -%u32vec2_output = OpVariable %u32vec2_ptr_output Output - -%u64_output = OpVariable %u64_ptr_output Output - -%f32_ptr_input = OpTypePointer Input %f32 -%f32vec2_ptr_input = OpTypePointer Input %f32vec2 - -%u32_ptr_input = OpTypePointer Input %u32 -%u32vec2_ptr_input = OpTypePointer Input %u32vec2 - -%u64_ptr_input = OpTypePointer Input %u64 - -%f32_ptr_function = OpTypePointer Function %f32 - -%f32_input = OpVariable %f32_ptr_input Input -%f32vec2_input = OpVariable %f32vec2_ptr_input Input - -%u32_input = OpVariable %u32_ptr_input Input -%u32vec2_input = OpVariable %u32vec2_ptr_input Input - -%u64_input = OpVariable %u64_ptr_input Input - -%u32_ptr_function = OpTypePointer Function %u32 - -%struct_f16_u16 = OpTypeStruct %f16 %u16 -%struct_f32_f32 = OpTypeStruct %f32 %f32 -%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 -%struct_f32_u32 = OpTypeStruct %f32 %u32 -%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 -%struct_u32_f32 = OpTypeStruct %u32 %f32 -%struct_u32_u32 = OpTypeStruct %u32 %u32 -%struct_f32_f64 = OpTypeStruct %f32 %f64 -%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 -%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 -)"; - - ss << debug_instructions_before_main; - - ss << R"( -%main = OpFunction %void None %func -%main_entry = OpLabel -)"; - - ss << body; - - ss << R"( -OpReturn -OpFunctionEnd)"; - - return ss.str(); -} - -TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { - const std::string src = R"( -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "DebugInfo" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Debug info extension instructions other than DebugScope, " - "DebugNoScope, DebugDeclare, DebugValue must appear between " - "section 9 (types, constants, global variables) and section 10 " - "(function declarations)")); -} - -TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%int_name = OpString "int" -%foo_name = OpString "foo" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal -%expr = OpExtInst %void %DbgExt DebugExpression -)"; - - const std::string body = R"( -%foo = OpVariable %u32_ptr_function Function -%foo_val = OpLoad %u32 %foo -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " - "of debug info extension must appear in a function " - "body")); -} - -INSTANTIATE_TEST_SUITE_P( - AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, - ::testing::ValuesIn(std::vector{ - "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", - "%no_scope = OpExtInst %void %DbgExt DebugNoScope", - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbgNone = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info -%foo = OpVariable %f32_ptr_function Function -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%ty_name = OpString "struct VS_OUTPUT" -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; - float4 color : COLOR; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_color_name = OpString "color : COLOR" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; - float4 color : COLOR; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_color_name = OpString "color : COLOR" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("forward referenced IDs have not been defined")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected result type must be a result id of " - "OpTypeVoid")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Source must be a result id of " - "DebugSource")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) { - const std::string src = R"( -%code = OpString "main() {}" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand File must be a result id of " - "OpString")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) { - const std::string src = R"( -%src = OpString "simple.hlsl" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Text must be a result id of " - "OpString")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) { - const std::string src = R"( -%src = OpString "simple.hlsl" -)"; - - const std::string dbg_inst = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", - extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Name must be a result id of " - "OpString")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Size must be a result id of " - "OpConstant")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float4 main(float arg) { - float foo; - return float4(0, 0, 0, 0); -} -" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%int_name = OpString "int" -%main_name = OpString "main" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type is not a valid debug " - "type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%main_name = OpString "main" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal -%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; - ss << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second + - " must be a result id of ")); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)", - "Name"), - std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)", - "Base Type"), - std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)", - "Source"), - std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)", - "Parent"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info -%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info -%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("expected operand Return Type is not a valid debug type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -%float_name = OpString "float" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("expected operand Parameter Types is not a valid debug type")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%none = OpExtInst %void %DbgExt DebugInfoNone -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name -%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name -%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; - ss << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", - "Underlying Types"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)", - "Parent"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)", - "Size"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)", - "Size"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)", - "Value"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)", - "Name"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -struct foo : VS_OUTPUT { -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%foo_name = OpString "foo" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main -%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -struct foo : VS_OUTPUT { -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%foo_name = OpString "foo" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; - ss << param.first; - ss << R"( -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main -%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second + " must be ")); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Name"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Source"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Parent"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Linkage Name"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", - "Size"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)", - "Members"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)", - "Members"), - std::make_pair( - R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)", - "Members"), - })); - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float pos : SV_POSITION; -}; -main() {} -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%float_name = OpString "float" -%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" -%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; - ss << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - if (!param.second.empty()) { - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second + - " must be a result id of ")); - } -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", - "Name"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", - ""), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", - "Source"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)", - "Parent"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)", - "Offset"), - std::make_pair( - R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", - "Size"), - })); - -TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT {}; -struct foo : VS_OUTPUT {}; -" -%VS_OUTPUT_name = OpString "struct VS_OUTPUT" -%foo_name = OpString "foo" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child -%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic -%child = OpExtInst %void %DbgExt DebugTypeInheritance )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", - "Child must be a result id of"), - std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)", - "Parent must be a result id of"), - std::make_pair( - R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", - "Child must be class or struct debug type"), - std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)", - "Parent must be class or struct debug type"), - std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)", - "Offset"), - std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", - "Size"), - })); TEST_P(ValidateGlslStd450SqrtLike, Success) { const std::string ext_inst_name = GetParam(); std::ostringstream ss; @@ -2178,1086 +472,6 @@ TEST_P(ValidateGlslStd450SqrtLike, Success) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -main() {} -" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -main() {} -" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic -%main_info = OpExtInst %void %DbgExt DebugFunction )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", - "Name"), - std::make_pair( - R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", - "Type"), - std::make_pair( - R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", - "Source"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)", - "Parent"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", - "Linkage Name"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)", - "Function"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)", - "Declaration"), - })); - -TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "struct VS_OUTPUT { - float4 pos : SV_POSITION; -}; -main() {} -" -%main_name = OpString "main" -%main_linkage_name = OpString "v4f_main_f" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, - ValidateOpenCL100DebugInfoDebugFunctionDeclaration, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", - "Name"), - std::make_pair( - R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", - "Type"), - std::make_pair( - R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)", - "Source"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)", - "Parent"), - std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", - "Linkage Name"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "main() {}" -%main_name = OpString "main" -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", - extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"), - std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"), - std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugLocalVariable )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)", - "Type"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)", - "Parent"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const std::string body = R"( -%foo = OpVariable %f32_ptr_function Function -%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) { - CompileSuccessfully(R"( - OpCapability Shader - %1 = OpExtInstImport "OpenCL.DebugInfo.100" - OpMemoryModel Logical GLSL450 - OpEntryPoint Vertex %main "main" %in_var_COLOR - %4 = OpString "test.hlsl" - OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" -void main(float foo:COLOR) {} -" - %11 = OpString "#line 1 \"test.hlsl\" -void main(float foo:COLOR) {} -" - %14 = OpString "float" - %17 = OpString "src.main" - %20 = OpString "foo" - OpName %in_var_COLOR "in.var.COLOR" - OpName %main "main" - OpName %param_var_foo "param.var.foo" - OpName %src_main "src.main" - OpName %foo "foo" - OpName %bb_entry "bb.entry" - OpDecorate %in_var_COLOR Location 0 - %uint = OpTypeInt 32 0 - %uint_32 = OpConstant %uint 32 - %float = OpTypeFloat 32 -%_ptr_Input_float = OpTypePointer Input %float - %void = OpTypeVoid - %23 = OpTypeFunction %void -%_ptr_Function_float = OpTypePointer Function %float - %29 = OpTypeFunction %void %_ptr_Function_float - OpLine %4 1 21 -%in_var_COLOR = OpVariable %_ptr_Input_float Input - %10 = OpExtInst %void %1 DebugExpression - %12 = OpExtInst %void %1 DebugSource %4 %11 - %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL - %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float - %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 - %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main - %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 - %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 - OpLine %4 1 1 - %main = OpFunction %void None %23 - %24 = OpLabel - OpLine %4 1 17 -%param_var_foo = OpVariable %_ptr_Function_float Function - %27 = OpLoad %float %in_var_COLOR - OpLine %4 1 1 - %28 = OpFunctionCall %void %src_main %param_var_foo - OpReturn - OpFunctionEnd - %src_main = OpFunction %void None %29 - OpLine %4 1 17 - %foo = OpFunctionParameter %_ptr_Function_float - %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 - %bb_entry = OpLabel - OpLine %4 1 29 - OpReturn - OpFunctionEnd -)"); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%foo = OpVariable %f32_ptr_function Function -%decl = OpExtInst %void %DbgExt DebugDeclare )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), - std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), - std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) { - const std::string dbg_inst_header = R"( -%op0 = OpExtInst %void %DbgExt DebugOperation Deref -%op1 = OpExtInst %void %DbgExt DebugOperation Plus -%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) { - const std::string dbg_inst_header = R"( -%op = OpExtInst %void %DbgExt DebugOperation Deref -%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "expected operand Operation must be a result id of DebugOperation")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Target must be DebugTypeComposite or " - "DebugFunction")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "OpaqueType foo; -main() {} -" -%float_name = OpString "float" -%ty_name = OpString "Texture" -%t_name = OpString "T" -%main_name = OpString "main" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -%int_128 = OpConstant %u32 128 -)"; - - const std::string dbg_inst_header = R"( -%dbg_none = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main -%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "expected operand Parameters must be DebugTypeTemplateParameter or " - "DebugTypeTemplateTemplateParameter")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a -%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbgNone = OpExtInst %void %DbgExt DebugInfoNone -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "float foo; void main() {}" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), - "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable, - ::testing::ValuesIn(std::vector>{ - std::make_pair( - R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Name"), - std::make_pair( - R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Type"), - std::make_pair( - R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Source"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", - "Scope"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", - "Linkage Name"), - std::make_pair( - R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", - "Variable"), - })); - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() {}" -%void_name = OpString "void" -%main_name = OpString "main" -%main_linkage_name = OpString "v_main" -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main -%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info -%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info -)"; - - const std::string body = R"( -%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_3 = OpConstant %u32 3 -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const std::string body = R"( -%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%int_name = OpString "int" -%foo_name = OpString "foo" -%len_name = OpString "length" -)"; - - const std::string size_const = R"( -%int_3 = OpConstant %u32 3 -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed -%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal -%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal -)"; - - const std::string body = R"( -%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info -)"; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, body, extension, "Vertex")); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) { - const std::string src = R"( -%src = OpString "simple.hlsl" -%code = OpString "void main() { float foo; }" -%float_name = OpString "float" -%foo_name = OpString "foo" -)"; - - const std::string size_const = R"( -%int_32 = OpConstant %u32 32 -)"; - - const std::string dbg_inst_header = R"( -%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code -%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL -%null_expr = OpExtInst %void %DbgExt DebugExpression -%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float -%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 -)"; - - const auto& param = GetParam(); - - std::ostringstream ss; - ss << R"( -%decl = OpExtInst %void %DbgExt DebugValue )" - << param.first; - - const std::string extension = R"( -%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" -)"; - - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand " + param.second)); -} - -INSTANTIATE_TEST_SUITE_P( - AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue, - ::testing::ValuesIn(std::vector>{ - std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), - std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), - std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), - })); - TEST_P(ValidateGlslStd450SqrtLike, IntResultType) { const std::string ext_inst_name = GetParam(); const std::string body = @@ -3507,6 +721,19 @@ TEST_P(ValidateGlslStd450SAbsLike, WrongBitWidthOperand) { "Result Type")); } +TEST_P(ValidateGlslStd450SAbsLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %main_entry\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllSAbsLike, ValidateGlslStd450SAbsLike, ::testing::ValuesIn(std::vector{ "SAbs", @@ -3656,6 +883,19 @@ TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand2) { "Result Type")); } +TEST_P(ValidateGlslStd450UMinLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %s64_0 %main_entry\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllUMinLike, ValidateGlslStd450UMinLike, ::testing::ValuesIn(std::vector{ "UMin", @@ -3819,6 +1059,19 @@ TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand3) { "Result Type")); } +TEST_P(ValidateGlslStd450UClampLike, TypelessOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %main_entry %s64_0 %s64_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or vectors")); +} + INSTANTIATE_TEST_SUITE_P(AllUClampLike, ValidateGlslStd450UClampLike, ::testing::ValuesIn(std::vector{ "UClamp", @@ -4327,6 +1580,19 @@ TEST_F(ValidateExtInst, GlslStd450LdexpExpWrongSize) { "number as Result Type")); } +TEST_F(ValidateExtInst, GlslStd450LdexpExpNoType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Ldexp %f32_1 %main_entry +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected operand Exp to be a 32-bit int scalar " + "or vector type")); +} + TEST_F(ValidateExtInst, GlslStd450FrexpStructSuccess) { const std::string body = R"( %val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f32_h @@ -5552,7 +2818,8 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32) { "expected Interpolant to be a pointer")); } -TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { +TEST_F(ValidateExtInst, + GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { const std::string body = R"( %ld2 = OpLoad %f32vec2 %f32vec2_input %val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01 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 dd4c952cf9..ac05749333 100644 --- a/third_party/spirv-tools/test/val/val_id_test.cpp +++ b/third_party/spirv-tools/test/val/val_id_test.cpp @@ -5524,9 +5524,9 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target " - "'1[%uint_3]' is not a scalar specialization " - "constant.")); + HasSubstr("SpecId decoration on target " + "'1[%uint_3]' must be a scalar specialization " + "constant")); } TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { @@ -5546,8 +5546,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " - "not a scalar specialization constant.")); + HasSubstr("SpecId decoration on target '1[%1]' " + "must be a scalar specialization constant")); } TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { @@ -5566,8 +5566,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " - "not a scalar specialization constant.")); + HasSubstr("SpecId decoration on target '1[%1]' " + "must be a scalar specialization constant")); } TEST_F(ValidateIdWithMessage, SpecIdTargetGood) { diff --git a/third_party/spirv-tools/test/val/val_image_test.cpp b/third_party/spirv-tools/test/val/val_image_test.cpp index 701e35e132..11b14fb0ca 100644 --- a/third_party/spirv-tools/test/val/val_image_test.cpp +++ b/third_party/spirv-tools/test/val/val_image_test.cpp @@ -1815,9 +1815,10 @@ TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " - "cannot be used together")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together")); } TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { @@ -1833,9 +1834,10 @@ TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-Offset-04662")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " - "cannot be used together")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together")); } TEST_F(ValidateImage, SampleImplicitLodMinLodWrongType) { @@ -6061,6 +6063,42 @@ TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) { "R32f, R32i, or R32ui for Vulkan environment")); } +TEST_F(ValidateImage, ImageExecutionModeLimitationNoMode) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 " " %4 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui +%13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%4 = OpVariable %_ptr_Input_v4float Input +%v2float = OpTypeVector %float 2 +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%2 = OpFunction %void None %8 +%8224 = OpLabel +%6 = OpLoad %13 %5 +%19 = OpLoad %v4float %4 +%20 = OpVectorShuffle %v2float %19 %19 0 1 +%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19 +%65312 = OpImageSampleImplicitLod %v4float %6 %21 +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImplicitLod instructions require " + "DerivativeGroupQuadsNV or DerivativeGroupLinearNV " + "execution mode for GLCompute execution model")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_interfaces_test.cpp b/third_party/spirv-tools/test/val/val_interfaces_test.cpp index 6869e79445..a01fc19b69 100644 --- a/third_party/spirv-tools/test/val/val_interfaces_test.cpp +++ b/third_party/spirv-tools/test/val/val_interfaces_test.cpp @@ -1267,10 +1267,9 @@ OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Index can only be applied to Fragment output variables")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be in the Output storage class")); } TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) { @@ -1410,6 +1409,93 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent1) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%float_arr = OpTypeArray %float %int_2 +%struct = OpTypeStruct %float_arr %float_arr +%ptr = OpTypePointer Input %struct +%in = OpVariable %ptr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent2) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%double_arr = OpTypeArray %double %int_2 +%struct = OpTypeStruct %float %double_arr +%ptr = OpTypePointer Input %struct +%in = OpVariable %ptr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, DuplicateInterfaceVariableSuccess) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%in_ptr = OpTypePointer Input %float +%out_ptr = OpTypePointer Output %float +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_layout_test.cpp b/third_party/spirv-tools/test/val/val_layout_test.cpp index d34c97f9cf..7ebd7c0084 100644 --- a/third_party/spirv-tools/test/val/val_layout_test.cpp +++ b/third_party/spirv-tools/test/val/val_layout_test.cpp @@ -538,7 +538,6 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidIn10) { OpMemoryModel Logical GLSL450 OpName %void "void" OpModuleProcessed "this is ok in 1.1 and later" - OpDecorate %void Volatile ; bogus, but makes the example short %void = OpTypeVoid )"; @@ -558,7 +557,6 @@ TEST_F(ValidateLayout, ModuleProcessedValidIn11) { OpMemoryModel Logical GLSL450 OpName %void "void" OpModuleProcessed "this is ok in 1.1 and later" - OpDecorate %void Volatile ; bogus, but makes the example short %void = OpTypeVoid )"; diff --git a/third_party/spirv-tools/test/val/val_memory_test.cpp b/third_party/spirv-tools/test/val/val_memory_test.cpp index 9799b404b0..2a884c4da5 100644 --- a/third_party/spirv-tools/test/val/val_memory_test.cpp +++ b/third_party/spirv-tools/test/val/val_memory_test.cpp @@ -746,7 +746,7 @@ TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) { %float = OpTypeFloat 32 %uint = OpTypeInt 32 0 %_runtimearr_float = OpTypeRuntimeArray %float - %_struct_7 = OpTypeStruct %float %_runtimearr_float + %_struct_7 = OpTypeStruct %float %_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 %1 = OpFunction %void None %3 %9 = OpLabel @@ -2344,11 +2344,12 @@ OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t Block +OpDecorate %struct Block %uint_t = OpTypeInt 32 0 %inner_array_t = OpTypeRuntimeArray %uint_t %array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer StorageBuffer %array_t +%struct = OpTypeStruct %array_t +%array_ptr = OpTypePointer StorageBuffer %struct %2 = OpVariable %array_ptr StorageBuffer %void = OpTypeVoid %func_t = OpTypeFunction %void @@ -2504,13 +2505,14 @@ OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t Block +OpDecorate %struct Block %uint_t = OpTypeInt 32 0 %dim = OpConstant %uint_t 1 %sampler_t = OpTypeSampler %inner_array_t = OpTypeRuntimeArray %uint_t %array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer StorageBuffer %array_t +%struct = OpTypeStruct %array_t +%array_ptr = OpTypePointer StorageBuffer %struct %2 = OpVariable %array_ptr StorageBuffer %void = OpTypeVoid %func_t = OpTypeFunction %void @@ -4284,6 +4286,117 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateMemory, LoadRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_rta %var %int_0 +%ld = OpLoad %rta %gep +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, LoadRuntimeArrayInStruct) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_block StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, LoadRuntimeArrayInArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%rta = OpTypeRuntimeArray %int +%block = OpTypeStruct %rta +%array = OpTypeArray %block %int_4 +%ptr_rta = OpTypePointer StorageBuffer %rta +%ptr_block = OpTypePointer StorageBuffer %block +%ptr_array = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %array %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot load a runtime-sized array")); +} + +TEST_F(ValidateMemory, Pre1p4WorkgroupMemoryBadLayoutOk) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%bool = OpTypeBool +%struct = OpTypeStruct %bool +%ptr = OpTypePointer Workgroup %struct +%var = OpVariable %ptr Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_modes_test.cpp b/third_party/spirv-tools/test/val/val_modes_test.cpp index 99f5c9cfae..02a61327e9 100644 --- a/third_party/spirv-tools/test/val/val_modes_test.cpp +++ b/third_party/spirv-tools/test/val/val_modes_test.cpp @@ -63,12 +63,13 @@ OpEntryPoint GLCompute %main "main" CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-LocalSize-04683")); + AnyVUID("VUID-StandaloneSpirv-LocalSize-06426")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("In the Vulkan environment, GLCompute execution model entry " - "points require either the LocalSize execution mode or an " - "object decorated with WorkgroupSize must be specified.")); + HasSubstr( + "In the Vulkan environment, GLCompute execution model entry " + "points require either the LocalSize or LocalSizeId execution mode " + "or an object decorated with WorkgroupSize must be specified.")); } TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) { @@ -101,6 +102,40 @@ OpExecutionMode %main LocalSize 1 1 1 EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId mode is not allowed by the current environment.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2 + CompileSuccessfully(spirv, env); + spvValidatorOptionsSetAllowLocalSizeId(getValidatorOptions(), true); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { const std::string spirv = R"( OpCapability Shader @@ -672,7 +707,7 @@ INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution, Values("Kernel"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", - "LocalSizeHintId %int1"), + "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( @@ -684,7 +719,7 @@ INSTANTIATE_TEST_SUITE_P( Values("Geometry", "TessellationControl", "TessellationEvaluation", "GLCompute", "Vertex", "Fragment"), Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", - "LocalSizeHintId %int1"), + "LocalSizeHintId %int1 %int1 %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); INSTANTIATE_TEST_SUITE_P( @@ -863,7 +898,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" -OpExecutionMode %main LocalSizeHintId %int_1 +OpExecutionMode %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; @@ -882,7 +917,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Kernel %main "main" -OpExecutionModeId %main LocalSizeHintId %int_1 +OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_1 = OpConstant %int 1 )" + kVoidFunction; @@ -898,7 +933,7 @@ OpCapability Kernel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" -OpExecutionModeId %main LocalSizeHintId %int_1 +OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1 %int = OpTypeInt 32 0 %int_ptr = OpTypePointer Private %int %int_1 = OpVariable %int_ptr Private diff --git a/third_party/spirv-tools/test/wasm/test.js b/third_party/spirv-tools/test/wasm/test.js new file mode 100644 index 0000000000..7f0d8f3cf8 --- /dev/null +++ b/third_party/spirv-tools/test/wasm/test.js @@ -0,0 +1,64 @@ +// Copyright (c) 2020 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. + +const spirvTools = require("../../out/web/spirv-tools"); +const fs = require("fs"); +const util = require("util"); +const readFile = util.promisify(fs.readFile); + +const SPV_PATH = "./test/fuzzers/corpora/spv/simple.spv"; + +const test = async () => { + const spv = await spirvTools(); + + // disassemble from file + const buffer = await readFile(SPV_PATH); + const disFileResult = spv.dis( + buffer, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis from file:\n", disFileResult); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); diff --git a/third_party/spirv-tools/tools/CMakeLists.txt b/third_party/spirv-tools/tools/CMakeLists.txt index 124a33281a..6039089a07 100644 --- a/third_party/spirv-tools/tools/CMakeLists.txt +++ b/third_party/spirv-tools/tools/CMakeLists.txt @@ -48,6 +48,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY}) endif() add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-lint SRCS lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-cfg SRCS cfg/cfg.cpp cfg/bin_to_dot.h @@ -56,7 +57,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR} ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt - spirv-cfg spirv-link) + spirv-cfg spirv-link spirv-lint) if(NOT DEFINED IOS_PLATFORM) set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) endif() diff --git a/third_party/spirv-tools/tools/cfg/cfg.cpp b/third_party/spirv-tools/tools/cfg/cfg.cpp index a93488d545..6a5faa18c9 100644 --- a/third_party/spirv-tools/tools/cfg/cfg.cpp +++ b/third_party/spirv-tools/tools/cfg/cfg.cpp @@ -69,16 +69,16 @@ int main(int argc, char** argv) { if (0 == strcmp(argv[argi], "--help")) { print_usage(argv[0]); return 0; - } else if (0 == strcmp(argv[argi], "--version")) { + } + if (0 == strcmp(argv[argi], "--version")) { printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString()); printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment)); return 0; - } else { - print_usage(argv[0]); - return 1; } - } break; + print_usage(argv[0]); + return 1; + } case 0: { // Setting a filename of "-" to indicate stdin. if (!inFile) { diff --git a/third_party/spirv-tools/tools/fuzz/fuzz.cpp b/third_party/spirv-tools/tools/fuzz/fuzz.cpp index 422bea53b1..306f9255b4 100644 --- a/third_party/spirv-tools/tools/fuzz/fuzz.cpp +++ b/third_party/spirv-tools/tools/fuzz/fuzz.cpp @@ -630,7 +630,7 @@ bool Fuzz(const spv_target_env& target_env, std::move(ir_context), std::move(transformation_context), std::move(fuzzer_context), message_consumer, donor_suppliers, fuzzer_options->all_passes_enabled, repeated_pass_strategy, - fuzzer_options->fuzzer_pass_validation_enabled, validator_options); + fuzzer_options->fuzzer_pass_validation_enabled, validator_options, false); auto fuzz_result = fuzzer.Run(0); if (fuzz_result.status == spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) { @@ -673,6 +673,19 @@ void DumpTransformationsBinary( transformations_file.close(); } +// The Chromium project applies the following patch to the protobuf library: +// +// source.chromium.org/chromium/chromium/src/+/main:third_party/protobuf/patches/0003-remove-static-initializers.patch +// +// This affects how Status objects must be constructed. This method provides a +// convenient way to get the OK status that works both with and without the +// patch. With the patch OK is a StatusPod, from which a Status can be +// constructed. Without the patch, OK is already a Status, and we harmlessly +// copy-construct the result from it. +google::protobuf::util::Status GetProtobufOkStatus() { + return google::protobuf::util::Status(google::protobuf::util::Status::OK); +} + // Dumps |transformations| to file |filename| in JSON format. Useful for // interactive debugging. void DumpTransformationsJson( @@ -683,7 +696,7 @@ void DumpTransformationsJson( json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations, &json_string, json_options); - if (json_generation_status == google::protobuf::util::Status::OK) { + if (json_generation_status == GetProtobufOkStatus()) { std::ofstream transformations_json_file(filename); transformations_json_file << json_string; transformations_json_file.close(); @@ -734,9 +747,8 @@ int main(int argc, const char** argv) { std::string facts_json_string((std::istreambuf_iterator(facts_input)), std::istreambuf_iterator()); facts_input.close(); - if (google::protobuf::util::Status::OK != - google::protobuf::util::JsonStringToMessage(facts_json_string, - &initial_facts)) { + if (GetProtobufOkStatus() != google::protobuf::util::JsonStringToMessage( + facts_json_string, &initial_facts)) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data"); return 1; } @@ -816,7 +828,7 @@ int main(int argc, const char** argv) { json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations_applied, &json_string, json_options); - if (json_generation_status != google::protobuf::util::Status::OK) { + if (json_generation_status != GetProtobufOkStatus()) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error writing out transformations in JSON format"); return 1; diff --git a/third_party/spirv-tools/tools/io.h b/third_party/spirv-tools/tools/io.h index aff9eabd97..83a85c1d1c 100644 --- a/third_party/spirv-tools/tools/io.h +++ b/third_party/spirv-tools/tools/io.h @@ -89,7 +89,7 @@ bool ReadBinaryFile(const char* filename, std::vector* data) { ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); - if (use_file) fclose(fp); + if (use_file && fp) fclose(fp); return succeeded; } @@ -111,7 +111,7 @@ bool ReadTextFile(const char* filename, std::vector* data) { ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); - if (use_file) fclose(fp); + if (use_file && fp) fclose(fp); return succeeded; } diff --git a/third_party/spirv-tools/tools/lint/lint.cpp b/third_party/spirv-tools/tools/lint/lint.cpp new file mode 100644 index 0000000000..5c2a82ac1d --- /dev/null +++ b/third_party/spirv-tools/tools/lint/lint.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/opt/log.h" +#include "spirv-tools/linter.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +namespace { +// Status and actions to perform after parsing command-line arguments. +enum LintActions { LINT_CONTINUE, LINT_STOP }; + +struct LintStatus { + LintActions action; + int code; +}; + +// Parses command-line flags. |argc| contains the number of command-line flags. +// |argv| points to an array of strings holding the flags. +// +// On return, this function stores the name of the input program in |in_file|. +// The return value indicates whether optimization should continue and a status +// code indicating an error or success. +LintStatus ParseFlags(int argc, const char** argv, const char** in_file) { + // TODO (dongja): actually parse flags, etc. + if (argc != 2) { + spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {}, + "expected exactly one argument: in_file"); + return {LINT_STOP, 1}; + } + + *in_file = argv[1]; + + return {LINT_CONTINUE, 0}; +} +} // namespace + +int main(int argc, const char** argv) { + const char* in_file = nullptr; + + spv_target_env target_env = kDefaultEnvironment; + + spvtools::Linter linter(target_env); + linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + + LintStatus status = ParseFlags(argc, argv, &in_file); + + if (status.action == LINT_STOP) { + return status.code; + } + + std::vector binary; + if (!ReadBinaryFile(in_file, &binary)) { + return 1; + } + + bool ok = linter.Run(binary.data(), binary.size()); + + return ok ? 0 : 1; +} diff --git a/third_party/spirv-tools/tools/opt/opt.cpp b/third_party/spirv-tools/tools/opt/opt.cpp index 53bf31d6d9..04f81b8c1a 100644 --- a/third_party/spirv-tools/tools/opt/opt.cpp +++ b/third_party/spirv-tools/tools/opt/opt.cpp @@ -143,6 +143,15 @@ Options (in lexicographical order):)", does not support RelaxedPrecision or ignores it. This pass also removes all RelaxedPrecision decorations.)"); printf(R"( + --convert-to-sampled-image ": ..." + convert images and/or samplers with the given pairs of descriptor + set and binding to sampled images. If a pair of an image and a + sampler have the same pair of descriptor set and binding that is + one of the given pairs, they will be converted to a sampled + image. In addition, if only an image or a sampler has the + descriptor set and binding that is one of the given pairs, it + will be converted to a sampled image.)"); + printf(R"( --copy-propagate-arrays Does propagation of memory references when an array is a copy of another. It will only propagate an array if the source is never @@ -154,6 +163,11 @@ Options (in lexicographical order):)", around known issues with some Vulkan drivers for initialize variables.)"); printf(R"( + --replace-desc-array-access-using-var-index + Replaces accesses to descriptor arrays based on a variable index + with a switch that has a case for every possible value of the + index.)"); + printf(R"( --descriptor-scalar-replacement Replaces every array variable |desc| that has a DescriptorSet and Binding decorations with a new variable for each element of @@ -378,9 +392,12 @@ Options (in lexicographical order):)", Change the scope of private variables that are used in a single function to that function.)"); printf(R"( - --reduce-load-size + --reduce-load-size[=] Replaces loads of composite objects where not every component is - used by loads of just the elements that are used.)"); + used by loads of just the elements that are used. If the ratio + of the used components of the load is less than the , + we replace the load. is a double type number. If + it is bigger than 1.0, we always replaces the load.)"); printf(R"( --redundancy-elimination Looks for instructions in the same function that compute the diff --git a/third_party/spirv-tools/tools/val/val.cpp b/third_party/spirv-tools/tools/val/val.cpp index 21a7d8f435..55321dab28 100644 --- a/third_party/spirv-tools/tools/val/val.cpp +++ b/third_party/spirv-tools/tools/val/val.cpp @@ -64,6 +64,8 @@ Options: --relax-struct-store Allow store from one struct type to a different type with compatible layout and members. + --allow-localsizeid Allow use of the LocalSizeId decoration where it would otherwise not + be allowed by the target environment. --before-hlsl-legalization Allows code patterns that are intended to be fixed by spirv-opt's legalization passes. --version Display validator version information. @@ -153,6 +155,8 @@ int main(int argc, char** argv) { options.SetWorkgroupScalarBlockLayout(true); } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { options.SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--allow-localsizeid")) { + options.SetAllowLocalSizeId(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { options.SetRelaxStructStore(true); } else if (0 == cur_arg[1]) { diff --git a/third_party/spirv-tools/utils/roll_deps.sh b/third_party/spirv-tools/utils/roll_deps.sh index 7ecfdd3b35..f61f2a31c8 100755 --- a/third_party/spirv-tools/utils/roll_deps.sh +++ b/third_party/spirv-tools/utils/roll_deps.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2019 Google Inc. +# Copyright (c) 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ # Attempts to roll all entries in DEPS to tip-of-tree and create a commit. # -# Depends on roll-dep from depot_path being in PATH. +# Depends on roll-dep from depot_tools +# (https://chromium.googlesource.com/chromium/tools/depot_tools) being in PATH. + +set -eo pipefail effcee_dir="external/effcee/" effcee_trunk="origin/main" @@ -44,3 +47,4 @@ roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}" roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}" git rebase --interactive "${old_head}" +