aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-11-30 21:31:12 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-11-30 21:31:12 +0000
commitd850eb9b0048a9b84e7401656ece1af7fbc452b1 (patch)
tree91e55dcba56d758debfaff55f88b4558e6655143
parent18d866c00b2f28fc562f2a085c3714d839463f82 (diff)
parentdf000f93d5c42cb983047fe3702afc2d4ae029da (diff)
downloadspirv-tools-ndk-r24-release.tar.gz
Snap for 7956253 from df000f93d5c42cb983047fe3702afc2d4ae029da to ndk-r24-releasendk-r24-rc1ndk-r24-beta2ndk-r24ndk-r24-release
Change-Id: I0ba3b524ad64da20e424243ef7845714a86c2f44
-rw-r--r--.appveyor.yml90
-rw-r--r--.github/workflows/wasm.yml14
-rw-r--r--Android.mk22
-rw-r--r--BUILD.bazel11
-rw-r--r--BUILD.gn57
-rw-r--r--CHANGES14
-rw-r--r--CMakeLists.txt9
-rw-r--r--DEPS8
-rw-r--r--README.md125
-rw-r--r--build_defs.bzl24
-rw-r--r--docker-compose.yml10
-rw-r--r--docs/downloads.md20
-rw-r--r--include/spirv-tools/libspirv.h8
-rw-r--r--include/spirv-tools/libspirv.hpp6
-rw-r--r--include/spirv-tools/linter.hpp2
-rw-r--r--include/spirv-tools/optimizer.hpp23
-rwxr-xr-xkokoro/linux-clang-ubsan/build.sh24
-rw-r--r--kokoro/linux-clang-ubsan/continuous.cfg16
-rw-r--r--kokoro/linux-clang-ubsan/presubmit.cfg16
-rwxr-xr-xkokoro/scripts/linux/build-docker.sh9
-rw-r--r--source/CMakeLists.txt15
-rw-r--r--source/binary.cpp3
-rw-r--r--source/common_debug_info.h4
-rw-r--r--source/ext_inst.cpp16
-rw-r--r--source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json638
-rw-r--r--source/fuzz/fuzzer_pass_apply_id_synonyms.cpp2
-rw-r--r--source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp63
-rw-r--r--source/fuzz/fuzzer_util.cpp87
-rw-r--r--source/fuzz/fuzzer_util.h15
-rw-r--r--source/fuzz/protobufs/spirvfuzz_protobufs.h1
-rw-r--r--source/fuzz/transformation_replace_id_with_synonym.cpp94
-rw-r--r--source/fuzz/transformation_replace_id_with_synonym.h15
-rw-r--r--source/fuzz/transformation_wrap_vector_synonym.cpp29
-rw-r--r--source/fuzz/transformation_wrap_vector_synonym.h7
-rw-r--r--source/link/linker.cpp3
-rw-r--r--source/lint/CMakeLists.txt2
-rw-r--r--source/lint/lint_divergent_derivatives.cpp169
-rw-r--r--source/lint/linter.cpp21
-rw-r--r--source/lint/lints.h34
-rw-r--r--source/operand.cpp4
-rw-r--r--source/opt/CMakeLists.txt4
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.cpp789
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.h152
-rw-r--r--source/opt/block_merge_pass.cpp2
-rw-r--r--source/opt/block_merge_util.cpp15
-rw-r--r--source/opt/ccp_pass.cpp4
-rw-r--r--source/opt/combine_access_chains.cpp4
-rw-r--r--source/opt/compact_ids_pass.cpp5
-rw-r--r--source/opt/constants.cpp3
-rw-r--r--source/opt/convert_to_half_pass.cpp36
-rw-r--r--source/opt/convert_to_half_pass.h2
-rw-r--r--source/opt/copy_prop_arrays.cpp4
-rw-r--r--source/opt/dead_branch_elim_pass.cpp5
-rw-r--r--source/opt/dead_insert_elim_pass.cpp2
-rw-r--r--source/opt/debug_info_manager.cpp87
-rw-r--r--source/opt/debug_info_manager.h2
-rw-r--r--source/opt/decoration_manager.cpp8
-rw-r--r--source/opt/decoration_manager.h4
-rw-r--r--source/opt/def_use_manager.cpp21
-rw-r--r--source/opt/desc_sroa.cpp224
-rw-r--r--source/opt/desc_sroa.h47
-rw-r--r--source/opt/desc_sroa_util.cpp117
-rw-r--r--source/opt/desc_sroa_util.h54
-rw-r--r--source/opt/eliminate_dead_members_pass.cpp31
-rw-r--r--source/opt/feature_manager.cpp8
-rw-r--r--source/opt/feature_manager.h8
-rw-r--r--source/opt/folding_rules.cpp236
-rw-r--r--source/opt/function.h3
-rw-r--r--source/opt/if_conversion.cpp1
-rw-r--r--source/opt/inline_exhaustive_pass.cpp2
-rw-r--r--source/opt/inline_opaque_pass.cpp2
-rw-r--r--source/opt/inline_pass.cpp13
-rw-r--r--source/opt/inst_buff_addr_check_pass.cpp60
-rw-r--r--source/opt/inst_buff_addr_check_pass.h12
-rw-r--r--source/opt/instruction.cpp96
-rw-r--r--source/opt/instruction.h49
-rw-r--r--source/opt/ir_builder.h22
-rw-r--r--source/opt/ir_context.cpp10
-rw-r--r--source/opt/ir_context.h4
-rw-r--r--source/opt/ir_loader.cpp48
-rw-r--r--source/opt/iterator.h2
-rw-r--r--source/opt/local_access_chain_convert_pass.cpp15
-rw-r--r--source/opt/local_single_block_elim_pass.cpp17
-rw-r--r--source/opt/local_single_store_elim_pass.cpp17
-rw-r--r--source/opt/loop_unroller.cpp10
-rw-r--r--source/opt/mem_pass.cpp2
-rw-r--r--source/opt/merge_return_pass.cpp21
-rw-r--r--source/opt/merge_return_pass.h4
-rw-r--r--source/opt/module.cpp37
-rw-r--r--source/opt/module.h2
-rw-r--r--source/opt/optimizer.cpp42
-rw-r--r--source/opt/pass.cpp4
-rw-r--r--source/opt/passes.h1
-rw-r--r--source/opt/reduce_load_size.cpp5
-rw-r--r--source/opt/reduce_load_size.h8
-rw-r--r--source/opt/redundancy_elimination.cpp4
-rw-r--r--source/opt/reflect.h5
-rw-r--r--source/opt/relax_float_ops_pass.cpp2
-rw-r--r--source/opt/replace_desc_array_access_using_var_index.cpp423
-rw-r--r--source/opt/replace_desc_array_access_using_var_index.h204
-rw-r--r--source/opt/replace_invalid_opc.cpp18
-rw-r--r--source/opt/scalar_replacement_pass.cpp22
-rw-r--r--source/opt/scalar_replacement_pass.h3
-rw-r--r--source/opt/set_spec_constant_default_value_pass.cpp33
-rw-r--r--source/opt/simplification_pass.cpp4
-rw-r--r--source/opt/ssa_rewrite_pass.cpp3
-rw-r--r--source/opt/vector_dce.cpp32
-rw-r--r--source/opt/vector_dce.h7
-rw-r--r--source/spirv_constant.h1
-rw-r--r--source/spirv_target_env.cpp63
-rw-r--r--source/spirv_target_env.h3
-rw-r--r--source/spirv_validator_options.cpp5
-rw-r--r--source/spirv_validator_options.h2
-rw-r--r--source/text_handler.cpp3
-rw-r--r--source/util/hex_float.h16
-rw-r--r--source/val/construct.cpp3
-rw-r--r--source/val/function.cpp3
-rw-r--r--source/val/validate_adjacency.cpp2
-rw-r--r--source/val/validate_annotation.cpp262
-rw-r--r--source/val/validate_atomics.cpp2
-rw-r--r--source/val/validate_builtins.cpp8
-rw-r--r--source/val/validate_cfg.cpp18
-rw-r--r--source/val/validate_decorations.cpp34
-rw-r--r--source/val/validate_derivatives.cpp12
-rw-r--r--source/val/validate_extensions.cpp16
-rw-r--r--source/val/validate_image.cpp32
-rw-r--r--source/val/validate_instruction.cpp7
-rw-r--r--source/val/validate_interfaces.cpp45
-rw-r--r--source/val/validate_layout.cpp38
-rw-r--r--source/val/validate_mode_setting.cpp19
-rw-r--r--source/val/validate_type.cpp2
-rw-r--r--source/val/validation_state.cpp69
-rw-r--r--source/val/validation_state.h26
-rw-r--r--source/wasm/README.md43
-rwxr-xr-xsource/wasm/build.sh78
-rw-r--r--source/wasm/package.json17
-rw-r--r--source/wasm/spirv-tools.cpp93
-rw-r--r--source/wasm/spirv-tools.d.ts56
-rw-r--r--test/fuzz/fuzzerutil_test.cpp253
-rw-r--r--test/fuzz/transformation_replace_id_with_synonym_test.cpp256
-rw-r--r--test/fuzz/transformation_wrap_vector_synonym_test.cpp159
-rw-r--r--test/fuzzers/BUILD.gn1
-rw-r--r--test/fuzzers/CMakeLists.txt30
-rw-r--r--test/fuzzers/random_generator.cpp135
-rw-r--r--test/fuzzers/random_generator.h68
-rw-r--r--test/fuzzers/spvtools_as_fuzzer.cpp23
-rw-r--r--test/fuzzers/spvtools_binary_parser_fuzzer.cpp16
-rw-r--r--test/fuzzers/spvtools_dis_fuzzer.cpp18
-rw-r--r--test/fuzzers/spvtools_fuzz_fuzzer.cpp80
-rw-r--r--test/fuzzers/spvtools_opt_legalization_fuzzer.cpp8
-rw-r--r--test/fuzzers/spvtools_opt_performance_fuzzer.cpp8
-rw-r--r--test/fuzzers/spvtools_opt_size_fuzzer.cpp8
-rw-r--r--test/fuzzers/spvtools_val_fuzzer.cpp8
-rw-r--r--test/hex_float_test.cpp70
-rw-r--r--test/operand_capabilities_test.cpp13
-rw-r--r--test/opt/CMakeLists.txt1
-rw-r--r--test/opt/aggressive_dead_code_elim_test.cpp458
-rw-r--r--test/opt/block_merge_test.cpp163
-rw-r--r--test/opt/ccp_test.cpp26
-rw-r--r--test/opt/combine_access_chains_test.cpp26
-rw-r--r--test/opt/convert_relaxed_to_half_test.cpp173
-rw-r--r--test/opt/copy_prop_array_test.cpp25
-rw-r--r--test/opt/dead_branch_elim_test.cpp65
-rw-r--r--test/opt/dead_insert_elim_test.cpp66
-rw-r--r--test/opt/debug_info_manager_test.cpp116
-rw-r--r--test/opt/desc_sroa_test.cpp63
-rw-r--r--test/opt/eliminate_dead_member_test.cpp61
-rw-r--r--test/opt/fold_test.cpp297
-rw-r--r--test/opt/inline_opaque_test.cpp109
-rw-r--r--test/opt/inline_test.cpp400
-rw-r--r--test/opt/inst_buff_addr_check_test.cpp206
-rw-r--r--test/opt/ir_loader_test.cpp16
-rw-r--r--test/opt/local_single_block_elim.cpp50
-rw-r--r--test/opt/local_single_store_elim_test.cpp85
-rw-r--r--test/opt/local_ssa_elim_test.cpp160
-rw-r--r--test/opt/loop_optimizations/unroll_assumptions.cpp31
-rw-r--r--test/opt/loop_optimizations/unroll_simple.cpp179
-rw-r--r--test/opt/pass_merge_return_test.cpp33
-rw-r--r--test/opt/reduce_load_size_test.cpp89
-rw-r--r--test/opt/redundancy_elimination_test.cpp28
-rw-r--r--test/opt/relax_float_ops_test.cpp80
-rw-r--r--test/opt/replace_desc_array_access_using_var_index_test.cpp411
-rw-r--r--test/opt/scalar_replacement_test.cpp40
-rw-r--r--test/opt/set_spec_const_default_value_test.cpp92
-rw-r--r--test/opt/simplification_test.cpp25
-rw-r--r--test/opt/upgrade_memory_model_test.cpp2
-rw-r--r--test/opt/vector_dce_test.cpp66
-rw-r--r--test/text_to_binary_test.cpp6
-rw-r--r--test/val/CMakeLists.txt1
-rw-r--r--test/val/val_annotation_test.cpp951
-rw-r--r--test/val/val_arithmetics_test.cpp51
-rw-r--r--test/val/val_atomics_test.cpp33
-rw-r--r--test/val/val_builtins_test.cpp82
-rw-r--r--test/val/val_capability_test.cpp466
-rw-r--r--test/val/val_cfg_test.cpp180
-rw-r--r--test/val/val_decoration_test.cpp106
-rw-r--r--test/val/val_derivatives_test.cpp25
-rw-r--r--test/val/val_ext_inst_debug_test.cpp120
-rw-r--r--test/val/val_ext_inst_test.cpp13
-rw-r--r--test/val/val_id_test.cpp14
-rw-r--r--test/val/val_image_test.cpp50
-rw-r--r--test/val/val_interfaces_test.cpp69
-rw-r--r--test/val/val_layout_test.cpp2
-rw-r--r--test/val/val_memory_test.cpp33
-rw-r--r--test/val/val_modes_test.cpp43
-rw-r--r--test/wasm/test.js64
-rw-r--r--tools/opt/opt.cpp12
-rw-r--r--tools/val/val.cpp4
-rwxr-xr-xutils/roll_deps.sh8
209 files changed, 10177 insertions, 2803 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 0a4cca05..00000000
--- a/.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/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
new file mode 100644
index 00000000..d9a9c5cb
--- /dev/null
+++ b/.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/Android.mk b/Android.mk
index 2bd72ae3..bc748e53 100644
--- a/Android.mk
+++ b/Android.mk
@@ -100,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 \
@@ -157,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 \
@@ -184,7 +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=$(LOCAL_PATH)/source/extinst.nonsemantic.vulkan.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)
@@ -216,7 +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.vulkan.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 \
@@ -246,7 +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),NonSemanticVulkanDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR)))
+$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR)))
define gen_spvtools_vendor_tables
@@ -261,22 +263,10 @@ $(1)/$(2).insts.inc : \
@echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar"
$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc
endef
-define gen_spvtools_vendor_tables_local
-$(call generate-file-dir,$(1)/$(2).insts.inc)
-$(1)/$(2).insts.inc : \
- $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- $(LOCAL_PATH)/source/extinst.$(2).grammar.json
- @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
- --extinst-vendor-grammar=$(LOCAL_PATH)/source/extinst.$(2).grammar.json \
- --vendor-insts-output=$(1)/$(2).insts.inc \
- --vendor-operand-kind-prefix=$(3)
- @echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar"
-$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc
-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_local,$(SPVTOOLS_OUT_PATH),nonsemantic.vulkan.debuginfo.100,"VKDEBUG100_"))
+$(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/BUILD.bazel b/BUILD.bazel
index 68e612ac..b2031ded 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -3,7 +3,7 @@ load(
"COMMON_COPTS",
"DEBUGINFO_GRAMMAR_JSON_FILE",
"CLDEBUGINFO100_GRAMMAR_JSON_FILE",
- "VKDEBUGINFO100_GRAMMAR_JSON_FILE",
+ "SHDEBUGINFO100_GRAMMAR_JSON_FILE",
"TEST_COPTS",
"base_test",
"generate_core_tables",
@@ -12,7 +12,6 @@ load(
"generate_glsl_tables",
"generate_opencl_tables",
"generate_vendor_tables",
- "generate_vendor_tables_local",
"link_test",
"lint_test",
"opt_test",
@@ -62,7 +61,7 @@ generate_vendor_tables("debuginfo")
generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_")
-generate_vendor_tables_local("nonsemantic.vulkan.debuginfo.100", "VKDEBUG100_")
+generate_vendor_tables("nonsemantic.shader.debuginfo.100", "SHDEBUG100_")
generate_vendor_tables("nonsemantic.clspvreflection")
@@ -70,7 +69,7 @@ generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE)
generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE)
-generate_extinst_lang_headers("NonSemanticVulkanDebugInfo100", VKDEBUGINFO100_GRAMMAR_JSON_FILE)
+generate_extinst_lang_headers("NonSemanticShaderDebugInfo100", SHDEBUGINFO100_GRAMMAR_JSON_FILE)
py_binary(
name = "generate_registry_tables",
@@ -108,14 +107,14 @@ cc_library(
":gen_enum_string_mapping",
":gen_extinst_lang_headers_DebugInfo",
":gen_extinst_lang_headers_OpenCLDebugInfo100",
- ":gen_extinst_lang_headers_NonSemanticVulkanDebugInfo100",
+ ":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_vulkan_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",
diff --git a/BUILD.gn b/BUILD.gn
index 20fdeedf..309d5137 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -225,29 +225,6 @@ template("spvtools_vendor_table") {
}
}
-template("spvtools_vendor_table_local") {
- assert(defined(invoker.name), "Need name in $target_name generation.")
-
- action("spvtools_vendor_tables_" + target_name) {
- script = "utils/generate_grammar_tables.py"
-
- name = invoker.name
- extinst_vendor_grammar = "source/extinst.${name}.grammar.json"
- extinst_file = "${target_gen_dir}/${name}.insts.inc"
-
- args = [
- "--extinst-vendor-grammar",
- rebase_path(extinst_vendor_grammar, root_build_dir),
- "--vendor-insts-output",
- rebase_path(extinst_file, root_build_dir),
- "--vendor-operand-kind-prefix",
- invoker.operand_kind_prefix,
- ]
- inputs = [ extinst_vendor_grammar ]
- outputs = [ extinst_file ]
- }
-}
-
action("spvtools_generators_inc") {
script = "utils/generate_registry_tables.py"
@@ -300,8 +277,8 @@ spvtools_language_header("cldebuginfo100") {
grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
}
spvtools_language_header("vkdebuginfo100") {
- name = "NonSemanticVulkanDebugInfo100"
- grammar_file = "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json"
+ name = "NonSemanticShaderDebugInfo100"
+ grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json"
}
spvtools_vendor_tables = [
@@ -333,13 +310,12 @@ spvtools_vendor_tables = [
"nonsemantic.clspvreflection",
"...nil...",
],
+ [
+ "nonsemantic.shader.debuginfo.100",
+ "SHDEBUG100_",
+ ],
]
-spvtools_vendor_tables_local = [ [
- "nonsemantic.vulkan.debuginfo.100",
- "VKDEBUG100_",
- ] ]
-
foreach(table_def, spvtools_vendor_tables) {
spvtools_vendor_table(table_def[0]) {
name = table_def[0]
@@ -347,13 +323,6 @@ foreach(table_def, spvtools_vendor_tables) {
}
}
-foreach(table_def, spvtools_vendor_tables_local) {
- spvtools_vendor_table_local(table_def[0]) {
- name = table_def[0]
- operand_kind_prefix = table_def[1]
- }
-}
-
config("spvtools_public_config") {
include_dirs = [ "include" ]
}
@@ -421,10 +390,6 @@ static_library("spvtools") {
target_name = table_def[0]
deps += [ ":spvtools_vendor_tables_$target_name" ]
}
- foreach(table_def, spvtools_vendor_tables_local) {
- target_name = table_def[0]
- deps += [ ":spvtools_vendor_tables_$target_name" ]
- }
sources = [
"source/assembly_grammar.cpp",
@@ -610,14 +575,14 @@ static_library("spvtools_opt") {
"source/opt/constants.h",
"source/opt/control_dependence.cpp",
"source/opt/control_dependence.h",
- "source/opt/convert_to_sampled_image_pass.cpp",
- "source/opt/convert_to_sampled_image_pass.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.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",
@@ -632,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",
@@ -751,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",
diff --git a/CHANGES b/CHANGES
index 44c49273..ee232b60 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,19 @@
Revision history for SPIRV-Tools
+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)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 12231724..70caf857 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -84,6 +84,8 @@ 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)
option(SPIRV_WERROR "Enable error on warning" ON)
@@ -162,6 +164,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)
@@ -213,7 +216,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.
@@ -294,7 +299,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($<$<CONFIG:Debug>:-DSPIRV_CHECK_CONTEXT>)
endif()
# Precompiled header macro. Parameters are source file list and filename for pch cpp file.
diff --git a/DEPS b/DEPS
index 92c67e05..2aaf11f9 100644
--- a/DEPS
+++ b/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': 'e71feddb3f17c5586ff7f4cfb5ed1258b800574b',
+ 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133',
+ 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77',
+ 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844',
+ 'spirv_headers_revision': '814e728b30ddd0f4509233099a3ad96fd4318c07',
}
deps = {
diff --git a/README.md b/README.md
index 9230fd1a..14db1e70 100644
--- a/README.md
+++ b/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)
<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[![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)
<img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>[![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)
<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[![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)
@@ -53,7 +52,7 @@ 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 file.
+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
@@ -256,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
@@ -274,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
@@ -295,46 +314,12 @@ tests:
`googletest` source into the `<spirv-dir>/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 `<spirv-dir>` is the root directory of the
-checked out code:
-
-```sh
-cd <spirv-dir>
-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]:
@@ -379,6 +364,30 @@ You can also use [Bazel](https://bazel.build/) to build the project.
cd <spirv-dir>
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 <spirv-dir>
+./source/wasm/build.sh
+```
+
+The resulting node package, with JavaScript and TypeScript bindings, is
+written to `<spirv-dir>/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
@@ -392,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
@@ -435,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:
@@ -456,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
@@ -743,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/build_defs.bzl b/build_defs.bzl
index 519fa193..b2cd41b9 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -41,7 +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"
-VKDEBUGINFO100_GRAMMAR_JSON_FILE = "source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json"
+SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1"
def generate_core_tables(version = None):
if not version:
@@ -165,28 +165,6 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""):
visibility = ["//visibility:private"],
)
-def generate_vendor_tables_local(extension, operand_kind_prefix = ""):
- if not extension:
- fail("Must specify extension", "extension")
- extension_rule = extension.replace("-", "_").replace(".", "_")
- grammars = ["source/extinst.{}.grammar.json".format(extension)]
- outs = ["{}.insts.inc".format(extension)]
- prefices = [operand_kind_prefix]
- fmtargs = grammars + outs + prefices
- native.genrule(
- name = "gen_vendor_tables_" + extension_rule,
- srcs = grammars,
- outs = outs,
- cmd = (
- "$(location :generate_grammar_tables) " +
- "--extinst-vendor-grammar=$(location {0}) " +
- "--vendor-insts-output=$(location {1}) " +
- "--vendor-operand-kind-prefix={2}"
- ).format(*fmtargs),
- tools = [":generate_grammar_tables"],
- visibility = ["//visibility:private"],
- )
-
def generate_extinst_lang_headers(name, grammar = None):
if not grammar:
fail("Must specify grammar", "grammar")
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..fb6d114f
--- /dev/null
+++ b/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/docs/downloads.md b/docs/downloads.md
index 9c7d8567..168937a7 100644
--- a/docs/downloads.md
+++ b/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/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 999589e2..8df14f5f 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -309,7 +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_VULKAN_DEBUGINFO_100,
+ 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
@@ -516,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.
@@ -659,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/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 0c31a182..8dfb46b7 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/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/include/spirv-tools/linter.hpp b/include/spirv-tools/linter.hpp
index 57d1b4e9..52ed5a46 100644
--- a/include/spirv-tools/linter.hpp
+++ b/include/spirv-tools/linter.hpp
@@ -35,7 +35,7 @@ class Linter {
void SetMessageConsumer(MessageConsumer consumer);
// Returns a reference to the registered message consumer.
- const MessageConsumer& consumer() const;
+ const MessageConsumer& Consumer() const;
bool Run(const uint32_t* binary, size_t binary_size);
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index b1442dcf..21059cbe 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -29,7 +29,7 @@ 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
@@ -514,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
@@ -701,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
@@ -825,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.
diff --git a/kokoro/linux-clang-ubsan/build.sh b/kokoro/linux-clang-ubsan/build.sh
new file mode 100755
index 00000000..b5941e34
--- /dev/null
+++ b/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/kokoro/linux-clang-ubsan/continuous.cfg b/kokoro/linux-clang-ubsan/continuous.cfg
new file mode 100644
index 00000000..cb5535e1
--- /dev/null
+++ b/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/kokoro/linux-clang-ubsan/presubmit.cfg b/kokoro/linux-clang-ubsan/presubmit.cfg
new file mode 100644
index 00000000..029c74a5
--- /dev/null
+++ b/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/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh
index c6bbe951..8f76803c 100755
--- a/kokoro/scripts/linux/build-docker.sh
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -58,7 +58,7 @@ if [ $TOOL = "cmake" ]; then
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"
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 6530f060..331ff675 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -20,10 +20,7 @@ set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_lang
# 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")
-
-# For now, assume the NonSemantic.Vulkan.DebugInfo grammar file is in the current directory.
-# It will later migrate to SPIRV-Headers.
-set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.nonsemantic.vulkan.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
@@ -154,11 +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.vulkan.debuginfo.100" "vkdi100" "VKDEBUG100_")
+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("NonSemanticVulkanDebugInfo100" ${VKDEBUGINFO100_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})
@@ -431,8 +428,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/source/binary.cpp b/source/binary.cpp
index 090cccfe..93d5da7a 100644
--- a/source/binary.cpp
+++ b/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<uint32_t>(SpvOp::SpvOpMax) ||
+ grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
return diagnostic()
<< "Invalid " << spvOperandTypeStr(type) << ": " << word;
}
diff --git a/source/common_debug_info.h b/source/common_debug_info.h
index 0ae85aa0..ffa5d340 100644
--- a/source/common_debug_info.h
+++ b/source/common_debug_info.h
@@ -18,8 +18,8 @@
#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.Vulkan.DebugInfo.100.
-// note that NonSemantic.DebugInfo.100 instructions can still have slightly
+// 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,
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 8246c204..812053ec 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -29,7 +29,7 @@
#include "debuginfo.insts.inc"
#include "glsl.std.450.insts.inc"
#include "nonsemantic.clspvreflection.insts.inc"
-#include "nonsemantic.vulkan.debuginfo.100.insts.inc"
+#include "nonsemantic.shader.debuginfo.100.insts.inc"
#include "opencl.debuginfo.100.insts.inc"
#include "opencl.std.insts.inc"
@@ -56,9 +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_VULKAN_DEBUGINFO_100,
- ARRAY_SIZE(nonsemantic_vulkan_debuginfo_100_entries),
- nonsemantic_vulkan_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},
@@ -130,8 +130,8 @@ 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.Vulkan.DebugInfo.100", name)) {
- return SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_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;
@@ -146,7 +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_VULKAN_DEBUGINFO_100 ||
+ type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 ||
type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
return true;
}
@@ -155,7 +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_VULKAN_DEBUGINFO_100 ||
+ type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 ||
type == SPV_EXT_INST_TYPE_DEBUGINFO) {
return true;
}
diff --git a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json b/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json
deleted file mode 100644
index 1d7914d7..00000000
--- a/source/extinst.nonsemantic.vulkan.debuginfo.100.grammar.json
+++ /dev/null
@@ -1,638 +0,0 @@
-{
- "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" : 2,
- "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'" }
- ]
- },
- {
- "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'" }
- ]
- }
- ],
- "operand_kinds" : [
- {
- "category" : "BitEnum",
- "kind" : "DebugInfoFlags",
- "enumerants" : [
- {
- "enumerant" : "FlagIsProtected",
- "value" : "0x01"
- },
- {
- "enumerant" : "FlagIsPrivate",
- "value" : "0x02"
- },
- {
- "enumerant" : "FlagIsPublic",
- "value" : "0x03"
- },
- {
- "enumerant" : "FlagIsLocal",
- "value" : "0x04"
- },
- {
- "enumerant" : "FlagIsDefinition",
- "value" : "0x08"
- },
- {
- "enumerant" : "FlagFwdDecl",
- "value" : "0x10"
- },
- {
- "enumerant" : "FlagArtificial",
- "value" : "0x20"
- },
- {
- "enumerant" : "FlagExplicit",
- "value" : "0x40"
- },
- {
- "enumerant" : "FlagPrototyped",
- "value" : "0x80"
- },
- {
- "enumerant" : "FlagObjectPointer",
- "value" : "0x100"
- },
- {
- "enumerant" : "FlagStaticMember",
- "value" : "0x200"
- },
- {
- "enumerant" : "FlagIndirectVariable",
- "value" : "0x400"
- },
- {
- "enumerant" : "FlagLValueReference",
- "value" : "0x800"
- },
- {
- "enumerant" : "FlagRValueReference",
- "value" : "0x1000"
- },
- {
- "enumerant" : "FlagIsOptimized",
- "value" : "0x2000"
- },
- {
- "enumerant" : "FlagIsEnumClass",
- "value" : "0x4000"
- },
- {
- "enumerant" : "FlagTypePassByValue",
- "value" : "0x8000"
- },
- {
- "enumerant" : "FlagTypePassByReference",
- "value" : "0x10000"
- }
- ]
- },
- {
- "category" : "ValueEnum",
- "kind" : "DebugBaseTypeAttributeEncoding",
- "enumerants" : [
- {
- "enumerant" : "Unspecified",
- "value" : "0"
- },
- {
- "enumerant" : "Address",
- "value" : "1"
- },
- {
- "enumerant" : "Boolean",
- "value" : "2"
- },
- {
- "enumerant" : "Float",
- "value" : "3"
- },
- {
- "enumerant" : "Signed",
- "value" : "4"
- },
- {
- "enumerant" : "SignedChar",
- "value" : "5"
- },
- {
- "enumerant" : "Unsigned",
- "value" : "6"
- },
- {
- "enumerant" : "UnsignedChar",
- "value" : "7"
- }
- ]
- },
- {
- "category" : "ValueEnum",
- "kind" : "DebugCompositeType",
- "enumerants" : [
- {
- "enumerant" : "Class",
- "value" : "0"
- },
- {
- "enumerant" : "Structure",
- "value" : "1"
- },
- {
- "enumerant" : "Union",
- "value" : "2"
- }
- ]
- },
- {
- "category" : "ValueEnum",
- "kind" : "DebugTypeQualifier",
- "enumerants" : [
- {
- "enumerant" : "ConstType",
- "value" : "0"
- },
- {
- "enumerant" : "VolatileType",
- "value" : "1"
- },
- {
- "enumerant" : "RestrictType",
- "value" : "2"
- },
- {
- "enumerant" : "AtomicType",
- "value" : "3"
- }
- ]
- },
- {
- "category" : "ValueEnum",
- "kind" : "DebugOperation",
- "enumerants" : [
- {
- "enumerant" : "Deref",
- "value" : "0"
- },
- {
- "enumerant" : "Plus",
- "value" : "1"
- },
- {
- "enumerant" : "Minus",
- "value" : "2"
- },
- {
- "enumerant" : "PlusUconst",
- "value" : "3",
- "parameters" : [
- { "kind" : "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/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 8965c15e..5c3b86b9 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -198,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/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
index 37c93b30..35adcfec 100644
--- a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
+++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
@@ -55,21 +55,12 @@ void FuzzerPassWrapVectorSynonym::Apply() {
SpvOpCompositeConstruct, instruction_iterator)) {
return;
}
- // Get the scalar type represented by the targeted instruction id.
- uint32_t operand_type_id = instruction_iterator->type_id();
- // 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);
-
- // Target ids are the two scalar ids from the original instruction.
- uint32_t target_id1 = instruction_iterator->GetSingleWordInOperand(0);
- uint32_t target_id2 = instruction_iterator->GetSingleWordInOperand(1);
+ // 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
@@ -80,16 +71,23 @@ void FuzzerPassWrapVectorSynonym::Apply() {
return;
}
if (!fuzzerutil::CanMakeSynonymOf(
- GetIRContext(), *GetTransformationContext(),
- *GetIRContext()->get_def_use_mgr()->GetDef(target_id1))) {
+ GetIRContext(), *GetTransformationContext(), *operand1)) {
return;
}
if (!fuzzerutil::CanMakeSynonymOf(
- GetIRContext(), *GetTransformationContext(),
- *GetIRContext()->get_def_use_mgr()->GetDef(target_id2))) {
+ 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<uint32_t> vec1_components;
std::vector<uint32_t> vec2_components;
@@ -97,33 +95,42 @@ void FuzzerPassWrapVectorSynonym::Apply() {
// Populate components based on vector type and size.
for (uint32_t i = 0; i < vector_size; ++i) {
if (i == position) {
- vec1_components.emplace_back(target_id1);
- vec2_components.emplace_back(target_id2);
+ vec1_components.emplace_back(operand1->result_id());
+ vec2_components.emplace_back(operand2->result_id());
} else {
vec1_components.emplace_back(
- FindOrCreateZeroConstant(operand_type_id, true));
+ FindOrCreateZeroConstant(operand1->type_id(), true));
vec2_components.emplace_back(
- FindOrCreateZeroConstant(operand_type_id, true));
+ FindOrCreateZeroConstant(operand2->type_id(), true));
}
}
// Add two OpCompositeConstruct to the module with result id returned.
- const uint32_t vector_type_id =
- FindOrCreateVectorType(operand_type_id, vector_size);
+ // 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(
- vector_type_id, vec1_components, instruction_descriptor,
- result_id1));
+ 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(
- vector_type_id, vec2_components, instruction_descriptor,
- result_id2));
+ 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.
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index ea7cde7f..1d368a9f 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -2018,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/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index e4697a1a..54aa14a2 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -604,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/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h
index 429f341e..46c21881 100644
--- a/source/fuzz/protobufs/spirvfuzz_protobufs.h
+++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -24,6 +24,7 @@
#if defined(__clang__)
#pragma clang diagnostic push
#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"
diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
index 14555315..8d21d233 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/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,93 +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 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 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<uint32_t> TransformationReplaceIdWithSynonym::GetFreshIds()
const {
return std::unordered_set<uint32_t>();
diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h
index 1ac636b4..4570fcef 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.h
+++ b/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/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp
index d0eddd44..490bcd78 100644
--- a/source/fuzz/transformation_wrap_vector_synonym.cpp
+++ b/source/fuzz/transformation_wrap_vector_synonym.cpp
@@ -70,16 +70,29 @@ bool TransformationWrapVectorSynonym::IsApplicable(
return false;
}
- // The 2 vectors must be the same valid vector type.
+ // The 2 vectors must have compatible vector types.
auto vec1_type_id = vec1->type_id();
auto vec2_type_id = vec2->type_id();
- if (vec1_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;
}
- if (ir_context->get_def_use_mgr()->GetDef(vec1_type_id)->opcode() !=
- SpvOpTypeVector) {
+ // 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;
}
@@ -124,9 +137,11 @@ void TransformationWrapVectorSynonym::Apply(
// Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1
// %result_id2.
- auto vec_type_id = ir_context->get_def_use_mgr()
- ->GetDef(message_.vector_operand1())
- ->type_id();
+ 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<opt::Instruction>(
ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(),
std::move(in_operands));
diff --git a/source/fuzz/transformation_wrap_vector_synonym.h b/source/fuzz/transformation_wrap_vector_synonym.h
index 008211a2..94437fe5 100644
--- a/source/fuzz/transformation_wrap_vector_synonym.h
+++ b/source/fuzz/transformation_wrap_vector_synonym.h
@@ -38,11 +38,14 @@ class TransformationWrapVectorSynonym : public Transformation {
// 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 the same vector type
- // that is supported by this transformation.
+ // - |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;
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 8da4a98d..c5ca5625 100644
--- a/source/link/linker.cpp
+++ b/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/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt
index f9cae28a..1feae3f9 100644
--- a/source/lint/CMakeLists.txt
+++ b/source/lint/CMakeLists.txt
@@ -13,9 +13,11 @@
# 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")))
diff --git a/source/lint/lint_divergent_derivatives.cpp b/source/lint/lint_divergent_derivatives.cpp
new file mode 100644
index 00000000..512847b0
--- /dev/null
+++ b/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 <cassert>
+#include <sstream>
+#include <string>
+
+#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/source/lint/linter.cpp b/source/lint/linter.cpp
index 0f847953..e4ed04ea 100644
--- a/source/lint/linter.cpp
+++ b/source/lint/linter.cpp
@@ -14,6 +14,13 @@
#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 {
@@ -32,20 +39,22 @@ Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {}
Linter::~Linter() {}
void Linter::SetMessageConsumer(MessageConsumer consumer) {
- impl_->message_consumer = consumer;
+ impl_->message_consumer = std::move(consumer);
}
-const MessageConsumer& Linter::consumer() const {
+const MessageConsumer& Linter::Consumer() const {
return impl_->message_consumer;
}
bool Linter::Run(const uint32_t* binary, size_t binary_size) {
- (void)binary;
- (void)binary_size;
+ std::unique_ptr<opt::IRContext> context =
+ BuildModule(SPV_ENV_VULKAN_1_2, Consumer(), binary, binary_size);
+ if (context == nullptr) return false;
- consumer()(SPV_MSG_INFO, "", {0, 0, 0}, "Hello, world!");
+ bool result = true;
+ result &= lint::lints::CheckDivergentDerivatives(context.get());
- return true;
+ return result;
}
} // namespace spvtools
diff --git a/source/lint/lints.h b/source/lint/lints.h
new file mode 100644
index 00000000..a1995d2f
--- /dev/null
+++ b/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/source/operand.cpp b/source/operand.cpp
index bff36a26..6d83e81e 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -579,8 +579,8 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
std::function<bool(unsigned)> 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_VULKAN_DEBUGINFO_100) {
+ // forward references ever
+ if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
return [](unsigned) { return false; };
}
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 63af5c1d..7d522fb5 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -39,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
@@ -100,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
@@ -148,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
@@ -205,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/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 90d30e9a..0b54d5e8 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/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<const char*>(&inst.GetInOperand(0).words[0]);
+ if (0 == std::strncmp(extension_name, "NonSemantic.", 12) &&
+ 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100",
+ 32)) {
+ 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<BasicBlock*>& structuredOrder) {
- block2headerBranch_.clear();
- header2nextHeaderBranch_.clear();
- branch2merge_.clear();
- structured_order_index_.clear();
- std::stack<Instruction*> 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<Instruction> 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,253 +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<Instruction*>(param));
- },
- false);
-
- // Compute map from block to controlling conditional branch
- std::list<BasicBlock*> 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<BasicBlock*> 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<bool> assume_branches_live;
- std::stack<uint32_t> 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->GetCommonDebugOpcode() ==
- CommonDebugInfoDebugDeclare) {
- uint32_t varId =
- liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
- ProcessLoad(func, varId);
- // If DebugValue with Deref, process as load of variable
- } else if (liveInst->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
- 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);
- }
- }
-
- // 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();
- }
+ InitializeWorkList(func, structured_order);
+ ProcessWorkList(func);
+ return KillDeadInstructions(func, structured_order);
+}
- // 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<BasicBlock*>& 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();
@@ -588,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<uint32_t> live_variables = GetLoadedVariables(inst);
+ for (uint32_t var_id : live_variables) {
+ ProcessLoad(func, var_id);
+ }
+}
+
+std::vector<uint32_t> 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<uint32_t> 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<uint32_t> AggressiveDCEPass::GetLoadedVariablesFromFunctionCall(
+ const Instruction* inst) {
+ assert(inst->opcode() == SpvOpFunctionCall);
+ std::vector<uint32_t> 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<BasicBlock*>& 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()) {
@@ -602,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
@@ -649,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.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() {
@@ -690,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
@@ -717,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<const Function*> 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();
@@ -797,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;
}
@@ -812,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;
@@ -839,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);
@@ -873,13 +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.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,
@@ -894,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
@@ -902,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<Operand> new_operands;
@@ -920,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));
}
}
@@ -935,8 +912,6 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
return modified;
}
-AggressiveDCEPass::AggressiveDCEPass() = default;
-
Pass::Status AggressiveDCEPass::Process() {
// Initialize extensions allowlist
InitExtensions();
@@ -998,8 +973,118 @@ void AggressiveDCEPass::InitExtensions() {
"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<Instruction*>(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/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index f02e729f..1b3fd1e8 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -44,7 +44,9 @@ class AggressiveDCEPass : public MemPass {
using GetBlocksFunction =
std::function<std::vector<BasicBlock*>*(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<BasicBlock*>& 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<BasicBlock*>& structured_order);
+
+ // 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);
+
+ // Kills any instructions in |func| that have not been marked as live.
+ bool KillDeadInstructions(const Function* func,
+ std::list<BasicBlock*>& 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<uint32_t> 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<uint32_t> 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);
- // True if current function is an entry point
- bool func_is_entry_point_;
+ // Marks all of the OpFunctionParameter instructions in |func| as live.
+ void MarkFunctionParameterAsLive(const Function* func);
- // True if current function is entry point and has no function calls.
- bool private_like_local_;
+ // 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<uint32_t, bool> 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<Instruction*> 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<BasicBlock*, Instruction*> 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<BasicBlock*, Instruction*> header2nextHeaderBranch_;
-
- // Maps basic block to their index in the structured order traversal.
- std::unordered_map<BasicBlock*, uint32_t> structured_order_index_;
-
- // Map from branch to its associated merge instruction, if any
- std::unordered_map<Instruction*, Instruction*> branch2merge_;
-
- // Store instructions to variables of private storage
- std::vector<Instruction*> private_stores_;
-
// Live Instructions
utils::BitVector live_insts_;
diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp
index 04e47f1c..ef7f31fe 100644
--- a/source/opt/block_merge_pass.cpp
+++ b/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/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 15e8c6ff..8ae8020a 100644
--- a/source/opt/block_merge_util.cpp
+++ b/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/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp
index d84f13f5..8b896d50 100644
--- a/source/opt/ccp_pass.cpp
+++ b/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/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp
index facfc24b..142897a2 100644
--- a/source/opt/combine_access_chains.cpp
+++ b/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/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp
index 67091531..8815b8c6 100644
--- a/source/opt/compact_ids_pass.cpp
+++ b/source/opt/compact_ids_pass.cpp
@@ -86,9 +86,12 @@ Pass::Status CompactIdsPass::Process() {
},
true);
- if (modified)
+ if (modified) {
context()->module()->SetIdBound(
static_cast<uint32_t>(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/source/opt/constants.cpp b/source/opt/constants.cpp
index a3dac5d7..020e248b 100644
--- a/source/opt/constants.cpp
+++ b/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;
}
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index 6b3b540a..b127eabe 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/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/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index b647dd4a..c6e84d1b 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/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/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index f505d8a5..62ed5e77 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -40,6 +40,10 @@ bool IsDebugDeclareOrValue(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;
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 0054f576..356dbcb3 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/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<BasicBlock*> live_blocks;
modified |= MarkLiveBlocks(func, &live_blocks);
diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp
index fb5c1634..d877f0f9 100644
--- a/source/opt/dead_insert_elim_pass.cpp
+++ b/source/opt/dead_insert_elim_pass.cpp
@@ -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/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 2e8e1327..060e0d93 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -18,7 +18,7 @@
#include "source/opt/ir_context.h"
-// Constants for OpenCL.DebugInfo.100 & NonSemantic.Vulkan.DebugInfo.100
+// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100
// extension instructions.
static const uint32_t kOpLineOperandLineIndex = 1;
@@ -86,7 +86,7 @@ uint32_t DebugInfoManager::GetDbgSetImportId() {
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
if (setId == 0) {
setId =
- context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo();
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
}
return setId;
}
@@ -117,14 +117,14 @@ void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
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->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugFunctionDefinition) {
+ } else if (inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
auto fn_id = inst->GetSingleWordOperand(
kDebugFunctionDefinitionOperandOpFunctionIndex);
auto fn_inst = GetDbgInst(inst->GetSingleWordOperand(
kDebugFunctionDefinitionOperandDebugFunctionIndex));
- assert(fn_inst && fn_inst->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugFunction);
+ 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");
@@ -146,6 +146,25 @@ 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<Instruction> 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) {
uint32_t setId = GetDbgSetImportId();
@@ -155,10 +174,10 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
spv_operand_type_t line_number_type =
spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER;
- // In NonSemantic.Vulkan.DebugInfo.100, all constants are IDs of OpConstant,
+ // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant,
// not literals.
if (setId ==
- context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo())
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo())
line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID;
uint32_t line_number = 0;
@@ -194,10 +213,18 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
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) {
- uint32_t line_id =
- context()->get_constant_mgr()->GetUIntConst(line_number);
- line_number = line_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);
}
}
@@ -307,7 +334,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
}));
} else {
uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst(
- NonSemanticVulkanDebugInfo100Deref);
+ NonSemanticShaderDebugInfo100Deref);
deref_operation = std::unique_ptr<Instruction>(
new Instruction(context(), SpvOpExtInst,
@@ -316,7 +343,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
{SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
{SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(
- NonSemanticVulkanDebugInfo100DebugOperation)}},
+ NonSemanticShaderDebugInfo100DebugOperation)}},
{SPV_OPERAND_TYPE_ID, {deref_id}},
}));
}
@@ -574,8 +601,8 @@ Instruction* DebugInfoManager::AddDebugValueForDecl(
}
uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) {
- assert(inst->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugOperation &&
+ assert(inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugOperation &&
"inst must be Vulkan DebugOperation");
return context()
->get_constant_mgr()
@@ -606,7 +633,7 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
}
} else {
uint32_t operation_const = GetVulkanDebugOperation(operation);
- if (operation_const != NonSemanticVulkanDebugInfo100Deref) {
+ if (operation_const != NonSemanticShaderDebugInfo100Deref) {
return 0;
}
}
@@ -682,8 +709,8 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
RegisterDbgInst(inst);
if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction ||
- inst->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugFunctionDefinition) {
+ inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
RegisterDbgFunction(inst);
}
@@ -695,10 +722,10 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
}
if (deref_operation_ == nullptr &&
- inst->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugOperation) {
+ inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugOperation) {
uint32_t operation_const = GetVulkanDebugOperation(inst);
- if (operation_const == NonSemanticVulkanDebugInfo100Deref) {
+ if (operation_const == NonSemanticShaderDebugInfo100Deref) {
deref_operation_ = inst;
}
}
@@ -759,8 +786,11 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
{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(
@@ -818,8 +848,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
fn_id_to_dbg_fn_.erase(fn_id);
}
- if (instr->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugFunction) {
+ if (instr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunction) {
auto fn_id = instr->GetSingleWordOperand(
kDebugFunctionDefinitionOperandOpFunctionIndex);
fn_id_to_dbg_fn_.erase(fn_id);
@@ -851,11 +881,10 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
deref_operation_ = &*dbg_instr_itr;
break;
} else if (instr != &*dbg_instr_itr &&
- dbg_instr_itr->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugOperation) {
+ dbg_instr_itr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugOperation) {
uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr);
-
- if (operation_const == NonSemanticVulkanDebugInfo100Deref) {
+ if (operation_const == NonSemanticShaderDebugInfo100Deref) {
deref_operation_ = &*dbg_instr_itr;
break;
}
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index 679ae138..df34b30f 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -68,7 +68,7 @@ class DebugInlinedAtContext {
};
// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and
-// NonSemantic.Vulkan.DebugInfo.100 extension instructions.
+// NonSemantic.Shader.DebugInfo.100 extension instructions.
class DebugInfoManager {
public:
// Constructs a debug information manager from the given |context|.
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp
index 4bf026ef..2146c359 100644
--- a/source/opt/decoration_manager.cpp
+++ b/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<bool(const Instruction&)> f) {
diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h
index b753e6be..fe78f2ce 100644
--- a/source/opt/decoration_manager.h
+++ b/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/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp
index 0ec98cae..394b9fa1 100644
--- a/source/opt/def_use_manager.cpp
+++ b/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/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp
index 5e950069..bcbdde94 100644
--- a/source/opt/desc_sroa.cpp
+++ b/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<Instruction*> 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<Instruction*> access_chain_work_list;
std::vector<Instruction*> 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<uint32_t>(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<uint32_t>(num_members, 0)})
- .first;
- }
+ uint32_t number_of_elements =
+ descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
+ replacement_vars =
+ replacement_variables_
+ .insert({var, std::vector<uint32_t>(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<Instruction> 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<Operand> 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<uint32_t>(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<Instruction> 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<std::unique_ptr<Instruction>> 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/source/opt/desc_sroa.h b/source/opt/desc_sroa.h
index cd72fd30..fea06255 100644
--- a/source/opt/desc_sroa.h
+++ b/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/source/opt/desc_sroa_util.cpp b/source/opt/desc_sroa_util.cpp
new file mode 100644
index 00000000..1954e2cc
--- /dev/null
+++ b/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/source/opt/desc_sroa_util.h b/source/opt/desc_sroa_util.h
new file mode 100644
index 00000000..2f45c0c2
--- /dev/null
+++ b/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/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp
index 173df620..a24ba8f4 100644
--- a/source/opt/eliminate_dead_members_pass.cpp
+++ b/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/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp
index db3abddf..39a4a348 100644
--- a/source/opt/feature_manager.cpp
+++ b/source/opt/feature_manager.cpp
@@ -80,8 +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_Vulkan100DebugInfo_ =
- module->GetExtInstImportId("NonSemantic.Vulkan.DebugInfo.100");
+ extinst_importid_Shader100DebugInfo_ =
+ module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100");
}
bool operator==(const FeatureManager& a, const FeatureManager& b) {
@@ -109,8 +109,8 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) {
return false;
}
- if (a.extinst_importid_Vulkan100DebugInfo_ !=
- b.extinst_importid_Vulkan100DebugInfo_) {
+ if (a.extinst_importid_Shader100DebugInfo_ !=
+ b.extinst_importid_Shader100DebugInfo_) {
return false;
}
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h
index 4720c6dc..68c8e9a2 100644
--- a/source/opt/feature_manager.h
+++ b/source/opt/feature_manager.h
@@ -55,8 +55,8 @@ class FeatureManager {
return extinst_importid_OpenCL100DebugInfo_;
}
- uint32_t GetExtInstImportId_Vulkan100DebugInfo() const {
- return extinst_importid_Vulkan100DebugInfo_;
+ uint32_t GetExtInstImportId_Shader100DebugInfo() const {
+ return extinst_importid_Shader100DebugInfo_;
}
friend bool operator==(const FeatureManager& a, const FeatureManager& b);
@@ -97,9 +97,9 @@ class FeatureManager {
// for performance.
uint32_t extinst_importid_OpenCL100DebugInfo_ = 0;
- // Common NonSemanticVulkan100DebugInfo external instruction import ids,
+ // Common NonSemanticShader100DebugInfo external instruction import ids,
// cached for performance.
- uint32_t extinst_importid_Vulkan100DebugInfo_ = 0;
+ uint32_t extinst_importid_Shader100DebugInfo_ = 0;
};
} // namespace opt
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index 6ae078fb..4904f186 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -14,6 +14,7 @@
#include "source/opt/folding_rules.h"
+#include <climits>
#include <limits>
#include <memory>
#include <utility>
@@ -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<uint32_t> words;
-#define FOLD_OP(op) \
- if (width == 64) { \
- if (type->IsSigned()) { \
- int64_t val = input1->GetS64() op input2->GetS64(); \
- words = ExtractInts(static_cast<uint64_t>(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<uint32_t>(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(*);
@@ -971,25 +968,17 @@ FoldingRule MergeDivMulArithmetic() {
FoldingRule MergeDivNegateArithmetic() {
return [](IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
- assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv);
+ 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) {
@@ -1467,90 +1456,121 @@ FoldingRule IntMultipleBy1() {
};
}
-FoldingRule CompositeConstructFeedingExtract() {
- return [](IRContext* context, Instruction* inst,
- const std::vector<const analysis::Constant*>&) {
- // 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();
-
- // If there are no index operands, then this rule cannot do anything.
- if (inst->NumInOperands() <= 1) {
- return false;
- }
+// 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();
+
+ 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<Operand> 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<Operand> 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}});
-
- // 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)}});
- }
+ 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})};
+ }
- } 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<Operand> 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<const analysis::Constant*>&) {
+ // 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<Operand> 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
@@ -2509,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());
@@ -2561,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());
diff --git a/source/opt/function.h b/source/opt/function.h
index 9e1c7274..917bf584 100644
--- a/source/opt/function.h
+++ b/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<Instruction> def_inst_;
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp
index 4284069a..49206617 100644
--- a/source/opt/if_conversion.cpp
+++ b/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/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp
index f24f744d..bef45017 100644
--- a/source/opt/inline_exhaustive_pass.cpp
+++ b/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/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp
index 6ccaf908..fe9c6799 100644
--- a/source/opt/inline_opaque_pass.cpp
+++ b/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/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index a6bdaaff..2cc31258 100644
--- a/source/opt/inline_pass.cpp
+++ b/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));
@@ -405,8 +405,8 @@ bool InlinePass::InlineEntryBlock(
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->GetVulkan100DebugOpcode() ==
- NonSemanticVulkanDebugInfo100DebugFunctionDefinition) {
+ if (callee_inst_itr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
++callee_inst_itr;
continue;
}
@@ -441,6 +441,11 @@ std::unique_ptr<BasicBlock> 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/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
index 06acc7ea..e2336d36 100644
--- a/source/opt/inst_buff_addr_check_pass.cpp
+++ b/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/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index ec7bb684..a8232239 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/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/source/opt/instruction.cpp b/source/opt/instruction.cpp
index 577c0a67..2461e41e 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -16,7 +16,6 @@
#include <initializer_list>
-#include "NonSemanticVulkanDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
#include "source/disassemble.h"
#include "source/opt/fold.h"
@@ -31,9 +30,10 @@ 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 / NonSemantic.Vulkan.DebugInfo.100
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
// extension instructions.
const uint32_t kExtInstSetIdInIdx = 0;
const uint32_t kExtInstInstructionInIdx = 1;
@@ -74,8 +74,6 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
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<uint32_t> words(
@@ -83,6 +81,8 @@ 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,
@@ -159,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;
}
@@ -400,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;
@@ -512,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);
}
@@ -523,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<Instruction>&& inst) {
inst.get()->InsertBefore(this);
return inst.release();
@@ -624,22 +680,22 @@ OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
GetSingleWordInOperand(kExtInstInstructionInIdx));
}
-NonSemanticVulkanDebugInfo100Instructions Instruction::GetVulkan100DebugOpcode()
+NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
const {
if (opcode() != SpvOpExtInst) {
- return NonSemanticVulkanDebugInfo100InstructionsMax;
+ return NonSemanticShaderDebugInfo100InstructionsMax;
}
- if (!context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) {
- return NonSemanticVulkanDebugInfo100InstructionsMax;
+ if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
}
if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
- context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) {
- return NonSemanticVulkanDebugInfo100InstructionsMax;
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
}
- return NonSemanticVulkanDebugInfo100Instructions(
+ return NonSemanticShaderDebugInfo100Instructions(
GetSingleWordInOperand(kExtInstInstructionInIdx));
}
@@ -650,16 +706,16 @@ CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
const uint32_t opencl_set_id =
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
- const uint32_t vulkan_set_id =
- context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo();
+ const uint32_t shader_set_id =
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
- if (!opencl_set_id && !vulkan_set_id) {
+ 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 != vulkan_set_id) {
+ if (used_set_id != opencl_set_id && used_set_id != shader_set_id) {
return CommonDebugInfoInstructionsMax;
}
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 7cdfc44d..ce568f66 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -22,7 +22,7 @@
#include <utility>
#include <vector>
-#include "NonSemanticVulkanDebugInfo100.h"
+#include "NonSemanticShaderDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
#include "source/common_debug_info.h"
#include "source/latest_version_glsl_std_450_header.h"
@@ -252,11 +252,6 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// 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<Instruction>& 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);
@@ -306,8 +301,21 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// 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();
}
@@ -456,6 +464,10 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// 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;
@@ -552,13 +564,13 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// OpenCLDebugInfo100InstructionsMax.
OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
- // Returns debug opcode of a NonSemantic.Vulkan.DebugInfo.100 instruction. If
- // it is not a NonSemantic.Vulkan.DebugInfo.100 instruction, just returns
- // NonSemanticVulkanDebugInfo100InstructionsMax.
- NonSemanticVulkanDebugInfo100Instructions GetVulkan100DebugOpcode() 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.Vulkan.DebugInfo.100 instruction. Since these overlap, we
+ // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we
// return the OpenCLDebugInfo code
CommonDebugInfoInstructions GetCommonDebugOpcode() const;
@@ -566,10 +578,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
bool IsOpenCL100DebugInstr() const {
return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
}
- // Returns true if it is a NonSemantic.Vulkan.DebugInfo.100 instruction.
- bool IsVulkan100DebugInstr() const {
- return GetVulkan100DebugOpcode() !=
- NonSemanticVulkanDebugInfo100InstructionsMax;
+
+ // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction.
+ bool IsShader100DebugInstr() const {
+ return GetShader100DebugOpcode() !=
+ NonSemanticShaderDebugInfo100InstructionsMax;
}
bool IsCommonDebugInstr() const {
return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax;
@@ -608,9 +621,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
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<Instruction> dbg_line_insts_;
// DebugScope that wraps this instruction.
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index fe5feff5..4433cf0d 100644
--- a/source/opt/ir_builder.h
+++ b/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<int32_t>(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<uint32_t>(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 <typename T>
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/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 90873461..612a831a 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -30,7 +30,7 @@ static const int kSpvDecorateBuiltinInIdx = 2;
static const int kEntryPointInterfaceInIdx = 3;
static const int kEntryPointFunctionIdInIdx = 1;
-// Constants for OpenCL.DebugInfo.100 / NonSemantic.Vulkan.DebugInfo.100
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
// extension instructions.
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
@@ -171,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);
@@ -218,6 +220,8 @@ Instruction* IRContext::KillInst(Instruction* inst) {
void IRContext::CollectNonSemanticTree(
Instruction* inst, std::unordered_set<Instruction*>* to_kill) {
if (!inst->HasResultId()) return;
+ // Debug[No]Line result id is not used, so we are done
+ if (inst->IsDebugLineInst()) return;
std::vector<Instruction*> work_list;
std::unordered_set<Instruction*> seen;
work_list.push_back(inst);
@@ -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/source/opt/ir_context.h b/source/opt/ir_context.h
index 3b89d85c..65853476 100644
--- a/source/opt/ir_context.h
+++ b/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),
@@ -769,6 +771,8 @@ class IRContext {
// The instruction decoration manager for |module_|.
std::unique_ptr<analysis::DecorationManager> decoration_mgr_;
+
+ // The feature manager for |module_|.
std::unique_ptr<FeatureManager> feature_mgr_;
// A map from instructions to the basic block they belong to. This mapping is
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index bfdd59b3..a82b530e 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -17,9 +17,9 @@
#include <utility>
#include "DebugInfo.h"
-#include "NonSemanticVulkanDebugInfo100.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"
@@ -38,25 +38,37 @@ 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<SpvOp>(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<SpvOp>(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<SpvOp>(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 ||
inst->ext_inst_type ==
- SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) {
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
const CommonDebugInfoInstructions ext_inst_key =
CommonDebugInfoInstructions(ext_inst_index);
if (ext_inst_key == CommonDebugInfoDebugScope) {
@@ -97,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<Instruction>(
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<Instruction>(
+ 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();
@@ -243,15 +261,15 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
}
}
} else if (inst->ext_inst_type ==
- SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) {
- const NonSemanticVulkanDebugInfo100Instructions ext_inst_key =
- NonSemanticVulkanDebugInfo100Instructions(ext_inst_index);
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+ NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
switch (ext_inst_key) {
- case NonSemanticVulkanDebugInfo100DebugDeclare:
- case NonSemanticVulkanDebugInfo100DebugValue:
- case NonSemanticVulkanDebugInfo100DebugScope:
- case NonSemanticVulkanDebugInfo100DebugNoScope:
- case NonSemanticVulkanDebugInfo100DebugFunctionDefinition: {
+ 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 "
diff --git a/source/opt/iterator.h b/source/opt/iterator.h
index 2280582d..847e1bbd 100644
--- a/source/opt/iterator.h
+++ b/source/opt/iterator.h
@@ -142,7 +142,7 @@ inline IteratorRange<IteratorType> make_range(const IteratorType& begin,
template <typename IteratorType>
inline IteratorRange<IteratorType> make_range(IteratorType&& begin,
IteratorType&& end) {
- return {std::move(begin), std::move(end)};
+ return {std::forward<IteratorType>(begin), std::forward<IteratorType>(end)};
}
// Returns a (begin, end) iterator pair for the given container.
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index e8dbdfc4..da9ba8cc 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -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<const char*>(&inst.GetInOperand(0).words[0]);
+ if (0 == std::strncmp(extension_name, "NonSemantic.", 12) &&
+ 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100",
+ 32)) {
+ return false;
+ }
+ }
return true;
}
@@ -421,6 +435,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
"SPV_KHR_subgroup_uniform_control_flow",
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
+ "SPV_KHR_non_semantic_info",
});
}
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index a742e3b1..5fd4f658 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -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<const char*>(&inst.GetInOperand(0).words[0]);
+ if (0 == std::strncmp(extension_name, "NonSemantic.", 12) &&
+ 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100",
+ 32)) {
+ 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;
}
@@ -273,6 +287,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
"SPV_KHR_subgroup_uniform_control_flow",
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
+ "SPV_KHR_non_semantic_info",
});
}
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 9701d10e..051bcada 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/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<const char*>(&inst.GetInOperand(0).words[0]);
+ if (0 == std::strncmp(extension_name, "NonSemantic.", 12) &&
+ 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100",
+ 32)) {
+ 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;
}
@@ -126,6 +140,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
"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) {
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index df68bd20..aff191fe 100644
--- a/source/opt/loop_unroller.cpp
+++ b/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/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 72442a99..ca4889b7 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -86,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;
}
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp
index f1601049..a962a7cc 100644
--- a/source/opt/merge_return_pass.cpp
+++ b/source/opt/merge_return_pass.cpp
@@ -111,7 +111,9 @@ bool MergeReturnPass::ProcessStructured(
}
RecordImmediateDominators(function);
- AddSingleCaseSwitchAroundFunction();
+ if (!AddSingleCaseSwitchAroundFunction()) {
+ return false;
+ }
std::list<BasicBlock*> 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/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h
index 06a3e7b5..4096ce7d 100644
--- a/source/opt/merge_return_pass.h
+++ b/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/source/opt/module.cpp b/source/opt/module.cpp
index 6585012f..f97defbd 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -151,16 +151,15 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
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) {
@@ -169,11 +168,22 @@ void Module::ToBinary(std::vector<uint32_t>* 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<uint16_t>(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<uint16_t>(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<uint16_t>(SpvOpNoLine));
+ }
last_line_inst = nullptr;
}
}
@@ -181,7 +191,7 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
if (opcode == SpvOpLabel) {
between_label_and_phi_var = true;
} else if (opcode != SpvOpVariable && opcode != SpvOpPhi &&
- opcode != SpvOpLine && opcode != SpvOpNoLine) {
+ !spvtools::opt::IsOpLineInst(opcode)) {
between_label_and_phi_var = false;
}
@@ -190,7 +200,7 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
if (scope != last_scope) {
// Can only emit nonsemantic instructions after all phi instructions
// in a block so don't emit scope instructions before phi instructions
- // for Vulkan.NonSemantic.DebugInfo.100.
+ // for NonSemantic.Shader.DebugInfo.100.
if (!between_label_and_phi_var ||
context()
->get_feature_mgr()
@@ -206,18 +216,19 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
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/source/opt/module.h b/source/opt/module.h
index 43b55ae1..0360b7d5 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -104,7 +104,7 @@ class Module {
inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
// Appends a debug info extension (OpenCL.DebugInfo.100,
- // NonSemantic.Vulkan.DebugInfo.100, or DebugInfo) instruction to this module.
+ // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module.
inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d);
// Appends an annotation instruction to this module.
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 858c95ee..e74db26f 100644
--- a/source/opt/optimizer.cpp
+++ b/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") {
@@ -746,9 +764,9 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() {
MakeUnique<opt::SSARewritePass>());
}
-Optimizer::PassToken CreateAggressiveDCEPass() {
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::AggressiveDCEPass>());
+ MakeUnique<opt::AggressiveDCEPass>(preserve_interface));
}
Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
@@ -879,9 +897,10 @@ Optimizer::PassToken CreateVectorDCEPass() {
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
}
-Optimizer::PassToken CreateReduceLoadSizePass() {
+Optimizer::PassToken CreateReduceLoadSizePass(
+ double load_replacement_threshold) {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::ReduceLoadSize>());
+ MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
}
Optimizer::PassToken CreateCombineAccessChainsPass() {
@@ -941,6 +960,11 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() {
MakeUnique<opt::GraphicsRobustAccessPass>());
}
+Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
+}
+
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::DescriptorScalarReplacement>());
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index 09b78af9..017aad10 100644
--- a/source/opt/pass.cpp
+++ b/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/source/opt/passes.h b/source/opt/passes.h
index da837f2a..f3c30d57 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -66,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/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp
index 87d2c4c0..e9b80874 100644
--- a/source/opt/reduce_load_size.cpp
+++ b/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
@@ -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<double>(elements_used.size()) /
static_cast<double>(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/source/opt/reduce_load_size.h b/source/opt/reduce_load_size.h
index ccac49be..b3238453 100644
--- a/source/opt/reduce_load_size.h
+++ b/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<uint32_t, bool> should_replace_cache_;
diff --git a/source/opt/redundancy_elimination.cpp b/source/opt/redundancy_elimination.cpp
index 362e54dc..398225bb 100644
--- a/source/opt/redundancy_elimination.cpp
+++ b/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/source/opt/reflect.h b/source/opt/reflect.h
index c7d46df5..c2ffb0be 100644
--- a/source/opt/reflect.h
+++ b/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/source/opt/relax_float_ops_pass.cpp b/source/opt/relax_float_ops_pass.cpp
index 73f16ddf..3fcf8795 100644
--- a/source/opt/relax_float_ops_pass.cpp
+++ b/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/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp
new file mode 100644
index 00000000..1082e679
--- /dev/null
+++ b/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<uint32_t, uint32_t>& 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<Instruction*> 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<Instruction*> final_users;
+ CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
+ for (auto* inst : final_users) {
+ std::deque<Instruction*> 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<Instruction*>* final_users) const {
+ std::queue<Instruction*> 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<Instruction*>
+ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts(
+ Instruction* user_of_image_insts) const {
+ std::unordered_set<uint32_t> seen_inst_ids;
+ std::queue<Instruction*> 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<Instruction*> 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<Instruction*>& insts_to_be_cloned,
+ uint32_t branch_target_id,
+ std::unordered_map<uint32_t, uint32_t>* 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<Instruction*>& insts_to_be_cloned,
+ std::unordered_map<uint32_t, uint32_t>* 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<Instruction> 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<uint32_t, uint32_t>& 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<Instruction*>& 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<uint32_t> phi_operands;
+ std::vector<uint32_t> case_block_ids;
+ for (uint32_t idx = 0; idx < number_of_elements; ++idx) {
+ std::unordered_map<uint32_t, uint32_t> old_ids_to_new_ids_for_cloned_insts;
+ std::unique_ptr<BasicBlock> 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<BasicBlock> 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<Instruction>(
+ 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<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+ std::unique_ptr<Instruction> 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<uint32_t>* 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<uint32_t>& case_block_ids) const {
+ InstructionBuilder builder{context(), parent_block,
+ kAnalysisDefUseAndInstrToBlockMapping};
+ std::vector<std::pair<Operand::OperandData, uint32_t>> cases;
+ for (uint32_t i = 0; i < static_cast<uint32_t>(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<uint32_t>& phi_operands,
+ const std::vector<uint32_t>& case_block_ids,
+ uint32_t default_block_id) const {
+ std::vector<uint32_t> 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/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h
new file mode 100644
index 00000000..e18222c8
--- /dev/null
+++ b/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 <cstdio>
+#include <memory>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#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<Instruction*>* 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<Instruction*> 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<Instruction*>& 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<uint32_t, uint32_t>* 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<Instruction*>& insts_to_be_cloned,
+ std::unordered_map<uint32_t, uint32_t>* 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<uint32_t, uint32_t>& 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<Instruction*>& insts_to_be_cloned,
+ uint32_t branch_target_id,
+ std::unordered_map<uint32_t, uint32_t>* 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<uint32_t>* 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<uint32_t>& 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<uint32_t>& phi_operands,
+ const std::vector<uint32_t>& 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/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp
index 38b7539b..e3b9d3e4 100644
--- a/source/opt/replace_invalid_opc.cpp
+++ b/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<const char*>(
&file_name->GetInOperand(0).words[0]);
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index 8adca7b9..4d6a7aad 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/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;
@@ -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(
@@ -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/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index 9e9f0739..0928830c 100644
--- a/source/opt/scalar_replacement_pass.h
+++ b/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/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index 4c8d116f..4def2b09 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -85,6 +85,10 @@ std::vector<uint32_t> 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<uint32_t> ParseDefaultValueBitPattern(
const std::vector<uint32_t>& input_bit_pattern,
const analysis::Type* type) {
@@ -98,12 +102,33 @@ std::vector<uint32_t> ParseDefaultValueBitPattern(
}
return result;
} else if (const auto* IT = type->AsInteger()) {
- if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
- return std::vector<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp
index 319ceecf..43ec15f8 100644
--- a/source/opt/simplification_pass.cpp
+++ b/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/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 81770d77..29ab6123 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/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/source/opt/vector_dce.cpp b/source/opt/vector_dce.cpp
index 3c9f9446..28d94a07 100644
--- a/source/opt/vector_dce.cpp
+++ b/source/opt/vector_dce.cpp
@@ -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<VectorDCE::WorkListItem>* 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;
diff --git a/source/opt/vector_dce.h b/source/opt/vector_dce.h
index 0df9aee1..4d30b926 100644
--- a/source/opt/vector_dce.h
+++ b/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/source/spirv_constant.h b/source/spirv_constant.h
index 39771ccb..8636806c 100644
--- a/source/spirv_constant.h
+++ b/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/source/spirv_target_env.cpp b/source/spirv_target_env.cpp
index f20ebb4f..187ab61e 100644
--- a/source/spirv_target_env.cpp
+++ b/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/source/spirv_target_env.h b/source/spirv_target_env.h
index a804d615..cc06deca 100644
--- a/source/spirv_target_env.h
+++ b/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/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp
index 2716cca9..e5b1eece 100644
--- a/source/spirv_validator_options.cpp
+++ b/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/source/spirv_validator_options.h b/source/spirv_validator_options.h
index baaa5359..a357c031 100644
--- a/source/spirv_validator_options.h
+++ b/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/source/text_handler.cpp b/source/text_handler.cpp
index c31f34a6..46b98456 100644
--- a/source/text_handler.cpp
+++ b/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/source/util/hex_float.h b/source/util/hex_float.h
index cfc40fa6..be28eae3 100644
--- a/source/util/hex_float.h
+++ b/source/util/hex_float.h
@@ -1005,6 +1005,9 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& 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<T, Traits>& 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<T, Traits>& 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<int_type>(written_exponent * 10);
written_exponent =
@@ -1059,6 +1068,11 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& 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<int_type>(written_exponent * exponent_sign);
exponent = static_cast<int_type>(exponent + written_exponent);
diff --git a/source/val/construct.cpp b/source/val/construct.cpp
index 53008690..251e2bba 100644
--- a/source/val/construct.cpp
+++ b/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/source/val/function.cpp b/source/val/function.cpp
index 249c8664..9ad68e86 100644
--- a/source/val/function.cpp
+++ b/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/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp
index 13720f0e..8e6c373e 100644
--- a/source/val/validate_adjacency.cpp
+++ b/source/val/validate_adjacency.cpp
@@ -63,7 +63,7 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) {
// 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_VULKAN_DEBUGINFO_100) {
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
adjacency_status = PHI_AND_VAR_INVALID;
}
break;
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 85d2b751..3a77552e 100644
--- a/source/val/validate_annotation.cpp
+++ b/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<SpvDecoration>(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<uint32_t>(1);
- if (decoration == SpvDecorationSpecId) {
- const auto target_id = inst->GetOperandAs<uint32_t>(0);
- const auto target = _.FindDef(target_id);
- if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) {
- return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpDecorate SpecId decoration target <id> '"
- << _.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 <id> '" << _.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<SpvBuiltIn>(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<SpvStorageClass>(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<SpvDecoration>(1);
+ const auto target_id = inst->GetOperandAs<uint32_t>(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<uint32_t>(1);
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(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<SpvDecoration>(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/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index da023b80..cfa15d9f 100644
--- a/source/val/validate_atomics.cpp
+++ b/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/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index a6e624f7..57dde8ad 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -3993,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.
//
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 36f632a8..7842e56d 100644
--- a/source/val/validate_cfg.cpp
+++ b/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<uint32_t>(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/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index c483635b..3cdb471c 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -129,18 +129,30 @@ std::vector<uint32_t> 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<bool> 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<bool> hasOffset;
+ std::vector<uint32_t> 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<uint32_t>(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;
@@ -985,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/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp
index 067cc964..25b941ab 100644
--- a/source/val/validate_derivatives.cpp
+++ b/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/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index 5ea23e1f..dccbe149 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -20,7 +20,7 @@
#include "spirv/unified1/NonSemanticClspvReflection.h"
-#include "NonSemanticVulkanDebugInfo100.h"
+#include "NonSemanticShaderDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
#include "source/common_debug_info.h"
#include "source/diagnostic.h"
@@ -98,7 +98,7 @@ spv_result_t ValidateOperandForDebugInfo(
return SPV_SUCCESS;
}
-// For NonSemantic.Vulkan.DebugInfo.100 check that the operand of a debug info
+// 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.
@@ -140,7 +140,7 @@ bool DoesDebugInfoOperandMatchExpectation(
if (debug_inst->opcode() != SpvOpExtInst ||
(debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 &&
debug_inst->ext_inst_type() !=
- SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) ||
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) ||
!expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) {
return false;
}
@@ -706,7 +706,7 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
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_VULKAN_DEBUGINFO_100;
+ 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(
@@ -2707,7 +2707,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
}
} else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
ext_inst_type ==
- SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) {
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
if (!_.IsVoidType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
@@ -2716,7 +2716,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
}
const bool vulkanDebugInfo =
- ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100;
+ ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
auto num_words = inst->words().size();
@@ -2965,7 +2965,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
CHECK_CONST_UINT_OPERAND("Line", 8);
CHECK_CONST_UINT_OPERAND("Column", 9);
- // NonSemantic.Vulkan.DebugInfo doesn't have the Parent operand
+ // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
if (vulkanDebugInfo) {
CHECK_OPERAND("Offset", SpvOpConstant, 10);
CHECK_OPERAND("Size", SpvOpConstant, 11);
@@ -3023,7 +3023,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
CHECK_CONST_UINT_OPERAND("Flags", 12);
CHECK_CONST_UINT_OPERAND("Scope Line", 13);
- // NonSemantic.Vulkan.DebugInfo.100 doesn't include a reference to the
+ // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the
// OpFunction
if (vulkanDebugInfo) {
if (num_words == 15) {
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index e5968d06..64f6ba7b 100644
--- a/source/val/validate_image.cpp
+++ b/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/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index 9d395fb4..dad98673 100644
--- a/source/val/validate_instruction.cpp
+++ b/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<spv_target_env>(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/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp
index d3ef5386..7ccb6371 100644
--- a/source/val/validate_interfaces.cpp
+++ b/source/val/validate_interfaces.cpp
@@ -199,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
num_components *= type->GetOperandAs<uint32_t>(2);
break;
+ case SpvOpTypeArray:
+ // Skip the array.
+ return NumConsumedComponents(_,
+ _.FindDef(type->GetOperandAs<uint32_t>(1)));
default:
// This is an error that is validated elsewhere.
break;
@@ -430,17 +434,36 @@ spv_result_t GetLocationsForVariable(
continue;
}
- 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;
+ 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;
+ }
}
}
}
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index e6f4fcad..d5823219 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -17,7 +17,7 @@
#include <cassert>
#include "DebugInfo.h"
-#include "NonSemanticVulkanDebugInfo100.h"
+#include "NonSemanticShaderDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
#include "source/diagnostic.h"
#include "source/opcode.h"
@@ -51,15 +51,17 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _,
local_debug_info = true;
}
} else if (inst->ext_inst_type() ==
- SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) {
- const NonSemanticVulkanDebugInfo100Instructions ext_inst_key =
- NonSemanticVulkanDebugInfo100Instructions(ext_inst_index);
- if (ext_inst_key == NonSemanticVulkanDebugInfo100DebugScope ||
- ext_inst_key == NonSemanticVulkanDebugInfo100DebugNoScope ||
- ext_inst_key == NonSemanticVulkanDebugInfo100DebugDeclare ||
- ext_inst_key == NonSemanticVulkanDebugInfo100DebugValue ||
+ 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 ==
- NonSemanticVulkanDebugInfo100DebugFunctionDefinition) {
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
local_debug_info = true;
}
} else {
@@ -256,15 +258,17 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
local_debug_info = true;
}
} else if (inst->ext_inst_type() ==
- SPV_EXT_INST_TYPE_NONSEMANTIC_VULKAN_DEBUGINFO_100) {
- const NonSemanticVulkanDebugInfo100Instructions ext_inst_key =
- NonSemanticVulkanDebugInfo100Instructions(ext_inst_index);
- if (ext_inst_key == NonSemanticVulkanDebugInfo100DebugScope ||
- ext_inst_key == NonSemanticVulkanDebugInfo100DebugNoScope ||
- ext_inst_key == NonSemanticVulkanDebugInfo100DebugDeclare ||
- ext_inst_key == NonSemanticVulkanDebugInfo100DebugValue ||
+ 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 ==
- NonSemanticVulkanDebugInfo100DebugFunctionDefinition) {
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
local_debug_info = true;
}
} else {
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index 79f82d8d..96352687 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/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<SpvExecutionMode>(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/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 612fc5c2..4376b52c 100644
--- a/source/val/validate_type.cpp
+++ b/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 <id> '" << _.getIdName(rows_id)
+ << "OpTypeCooperativeMatrixNV Cols <id> '" << _.getIdName(cols_id)
<< "' is not a constant instruction with scalar integer type.";
}
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index c9ac3ae7..8d1a0d3f 100644
--- a/source/val/validation_state.cpp
+++ b/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 {
@@ -1826,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/source/val/validation_state.h b/source/val/validation_state.h
index 2fe96621..2ddfa4a9 100644
--- a/source/val/validation_state.h
+++ b/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;
diff --git a/source/wasm/README.md b/source/wasm/README.md
new file mode 100644
index 00000000..aca0f70c
--- /dev/null
+++ b/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/source/wasm/build.sh b/source/wasm/build.sh
new file mode 100755
index 00000000..f02ae525
--- /dev/null
+++ b/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/source/wasm/package.json b/source/wasm/package.json
new file mode 100644
index 00000000..78273538
--- /dev/null
+++ b/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/source/wasm/spirv-tools.cpp b/source/wasm/spirv-tools.cpp
new file mode 100644
index 00000000..90407f32
--- /dev/null
+++ b/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 <iostream>
+#include <string>
+#include <vector>
+
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
+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<spv_target_env>(env));
+ core.SetMessageConsumer(print_msg_to_stderr);
+
+ std::vector<uint32_t> spirv;
+ const uint32_t* ptr = reinterpret_cast<const uint32_t*>(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<spv_target_env>(env));
+ core.SetMessageConsumer(print_msg_to_stderr);
+
+ std::vector<uint32_t> spirv;
+ if (!core.Assemble(source, &spirv, options)) spirv.clear();
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(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<uint32_t>(SPV_ENV_UNIVERSAL_1_0));
+ constant("SPV_ENV_VULKAN_1_0", static_cast<uint32_t>(SPV_ENV_VULKAN_1_0));
+ constant("SPV_ENV_UNIVERSAL_1_1", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_1));
+ constant("SPV_ENV_OPENCL_2_1", static_cast<uint32_t>(SPV_ENV_OPENCL_2_1));
+ constant("SPV_ENV_OPENCL_2_2", static_cast<uint32_t>(SPV_ENV_OPENCL_2_2));
+ constant("SPV_ENV_OPENGL_4_0", static_cast<uint32_t>(SPV_ENV_OPENGL_4_0));
+ constant("SPV_ENV_OPENGL_4_1", static_cast<uint32_t>(SPV_ENV_OPENGL_4_1));
+ constant("SPV_ENV_OPENGL_4_2", static_cast<uint32_t>(SPV_ENV_OPENGL_4_2));
+ constant("SPV_ENV_OPENGL_4_3", static_cast<uint32_t>(SPV_ENV_OPENGL_4_3));
+ constant("SPV_ENV_OPENGL_4_5", static_cast<uint32_t>(SPV_ENV_OPENGL_4_5));
+ constant("SPV_ENV_UNIVERSAL_1_2", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_2));
+ constant("SPV_ENV_OPENCL_1_2", static_cast<uint32_t>(SPV_ENV_OPENCL_1_2));
+ constant("SPV_ENV_OPENCL_EMBEDDED_1_2", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_1_2));
+ constant("SPV_ENV_OPENCL_2_0", static_cast<uint32_t>(SPV_ENV_OPENCL_2_0));
+ constant("SPV_ENV_OPENCL_EMBEDDED_2_0", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_2_0));
+ constant("SPV_ENV_OPENCL_EMBEDDED_2_1", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_2_1));
+ constant("SPV_ENV_OPENCL_EMBEDDED_2_2", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_2_2));
+ constant("SPV_ENV_UNIVERSAL_1_3", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_3));
+ constant("SPV_ENV_VULKAN_1_1", static_cast<uint32_t>(SPV_ENV_VULKAN_1_1));
+ constant("SPV_ENV_WEBGPU_0", static_cast<uint32_t>(SPV_ENV_WEBGPU_0));
+ constant("SPV_ENV_UNIVERSAL_1_4", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_4));
+ constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast<uint32_t>(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+ constant("SPV_ENV_UNIVERSAL_1_5", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_5));
+ constant("SPV_ENV_VULKAN_1_2", static_cast<uint32_t>(SPV_ENV_VULKAN_1_2));
+
+
+ constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_NONE));
+ constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_PRINT));
+ constant("SPV_BINARY_TO_TEXT_OPTION_COLOR", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_COLOR));
+ constant("SPV_BINARY_TO_TEXT_OPTION_INDENT", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_INDENT));
+ constant("SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET));
+ constant("SPV_BINARY_TO_TEXT_OPTION_NO_HEADER", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER));
+ constant("SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES));
+
+ constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast<uint32_t>(SPV_TEXT_TO_BINARY_OPTION_NONE));
+ constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast<uint32_t>(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS));
+} \ No newline at end of file
diff --git a/source/wasm/spirv-tools.d.ts b/source/wasm/spirv-tools.d.ts
new file mode 100644
index 00000000..9c197973
--- /dev/null
+++ b/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<SpirvTools>;
diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp
index 6771c404..0ad3e742 100644
--- a/test/fuzz/fuzzerutil_test.cpp
+++ b/test/fuzz/fuzzerutil_test.cpp
@@ -1549,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/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index cebce8a0..b33dd48c 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -2173,262 +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, 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(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicLoad, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#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),
- "Signedness check should not occur on a pointer operand.");
-#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, 3, int_type, uint_type));
-
- // OpAtomicStore
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 3, int_type, uint_type));
-
- // OpAtomicCompareExchange
-#ifndef NDEBUG
- ASSERT_DEATH(
- TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicCompareExchange, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type));
-
- // OpAtomicIIncrement
-#ifndef NDEBUG
- ASSERT_DEATH(
- TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIIncrement, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type));
-
-// OpAtomicIDecrement
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicStore, 2, int_type, uint_type));
-
-// OpAtomicIAdd
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIAdd, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIAdd, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIAdd, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicIAdd, 3, int_type, uint_type));
-
-// OpAtomicISub
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicISub, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicISub, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicISub, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicISub, 3, int_type, uint_type));
-
-// OpAtomicSMin
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMin, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMin, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMin, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMin, 3, int_type, uint_type));
-
-// OpAtomicUMin
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMin, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMin, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMin, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMin, 3, int_type, uint_type));
-
-// OpAtomicSMax
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMax, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMax, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMax, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicSMax, 3, int_type, uint_type));
-
-// OpAtomicUMax
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMax, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMax, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMax, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicUMax, 3, int_type, uint_type));
-
-// OpAtomicAnd
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicAnd, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicAnd, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicAnd, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicAnd, 3, int_type, uint_type));
-
-// OpAtomicOr
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicOr, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicOr, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicOr, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicOr, 3, int_type, uint_type));
-
-// OpAtomicXor
-#ifndef NDEBUG
- ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicXor, 0, int_type, uint_type),
- "Signedness check should not occur on a pointer operand.");
-#endif
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicXor, 1, int_type, uint_type));
- ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicXor, 2, int_type, uint_type));
- ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
- context.get(), SpvOpAtomicXor, 3, int_type, uint_type));
-}
-
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/transformation_wrap_vector_synonym_test.cpp b/test/fuzz/transformation_wrap_vector_synonym_test.cpp
index dae78a5d..0d1009b3 100644
--- a/test/fuzz/transformation_wrap_vector_synonym_test.cpp
+++ b/test/fuzz/transformation_wrap_vector_synonym_test.cpp
@@ -1389,6 +1389,165 @@ TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) {
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<FactManager>(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<FactManager>(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/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index ec09b2b0..2aef4e8f 100644
--- a/test/fuzzers/BUILD.gn
+++ b/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/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt
index 6b0926ea..50c45895 100644
--- a/test/fuzzers/CMakeLists.txt
+++ b/test/fuzzers/CMakeLists.txt
@@ -26,19 +26,31 @@ function(add_spvtools_libfuzzer_target)
${spirv-tools_BINARY_DIR}
)
set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools libFuzzer targets")
- target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer")
- target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer")
+ 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 LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
- add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
- add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
- add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
- add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
- add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
- add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+ 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/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp
new file mode 100644
index 00000000..801a9ffe
--- /dev/null
+++ b/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 <algorithm>
+#include <array>
+#include <cassert>
+
+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 <typename I>
+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<I>(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 <int SIZE_OF_SIZE_T>
+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 <typename T>
+void HashCombine(size_t* hash, const T& value) {
+ constexpr size_t offset = HashCombineOffset<sizeof(size_t)>::value();
+ *hash ^= std::hash<T>()(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<size_t>(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<spv_target_env>(
+ RandomUInt(&engine_, 0u, static_cast<unsigned int>(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<int64_t>(size);
+ int64_t hash_begin_i64 =
+ std::min(kHashDesiredLeadingSkipBytes,
+ std::max<int64_t>(size_i64 - kHashDesiredMinBytes, 0));
+ int64_t hash_end_i64 =
+ std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64);
+ size_t hash_begin = static_cast<size_t>(hash_begin_i64);
+ size_t hash_size = static_cast<size_t>(hash_end_i64) - hash_begin;
+ return HashBuffer(data + hash_begin, hash_size);
+}
+
+} // namespace fuzzers
+} // namespace spvtools
diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h
new file mode 100644
index 00000000..b121fe8c
--- /dev/null
+++ b/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 <cstdint>
+#include <random>
+
+#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/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp
index 8cecb05f..ba3f5b97 100644
--- a/test/fuzzers/spvtools_as_fuzzer.cpp
+++ b/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;
-
- const spv_context context =
- spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
- if (context == nullptr) 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();
+ }
- data += sizeof(spv_target_env);
- size -= sizeof(spv_target_env);
+ const spv_context context = spvContextCreate(target_env);
+ if (context == nullptr) {
+ return 0;
+ }
std::vector<uint32_t> input;
+ input.resize(size >> 2);
+ size_t count = 0;
+ for (size_t i = 0; (i + 3) < size; i += 4) {
+ input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
+ (data[i + 3]) << 24;
+ }
std::vector<char> input_str;
size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
diff --git a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp
index 76ba4d9e..3a97db41 100644
--- a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp
+++ b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp
@@ -16,16 +16,18 @@
#include <vector>
#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;
-
- const spv_context context =
- spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
- if (context == nullptr) return 0;
+ if (size < 1) {
+ 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<uint32_t> input;
input.resize(size >> 2);
diff --git a/test/fuzzers/spvtools_dis_fuzzer.cpp b/test/fuzzers/spvtools_dis_fuzzer.cpp
index ca9a52d8..0cb0eff5 100644
--- a/test/fuzzers/spvtools_dis_fuzzer.cpp
+++ b/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;
-
- const spv_context context =
- spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
- if (context == nullptr) return 0;
+ if (size < 4) {
+ // There are not enough bytes to constitute a binary that can be
+ // disassembled.
+ 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<uint32_t> input;
input.resize(size >> 2);
diff --git a/test/fuzzers/spvtools_fuzz_fuzzer.cpp b/test/fuzzers/spvtools_fuzz_fuzzer.cpp
new file mode 100644
index 00000000..d43920cf
--- /dev/null
+++ b/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 <cstdint>
+#include <vector>
+
+#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<uint32_t> 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<spvtools::opt::IRContext> 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<spvtools::fuzz::fuzzerutil::ModuleSupplier> donor_suppliers = {
+ [&initial_binary, message_consumer, target_env,
+ &validator_options]() -> std::unique_ptr<spvtools::opt::IRContext> {
+ std::unique_ptr<spvtools::opt::IRContext> 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<uint32_t>::max());
+ auto fuzzer_context = spvtools::MakeUnique<spvtools::fuzz::FuzzerContext>(
+ spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>(seed),
+ spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), false);
+
+ auto transformation_context =
+ spvtools::MakeUnique<spvtools::fuzz::TransformationContext>(
+ spvtools::MakeUnique<spvtools::fuzz::FactManager>(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/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp
index b45a98c3..6f4d7e8b 100644
--- a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp
@@ -16,9 +16,15 @@
#include <vector>
#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/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp
index 6c3bd6ab..9c47d7d7 100644
--- a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp
@@ -16,9 +16,15 @@
#include <vector>
#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/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/test/fuzzers/spvtools_opt_size_fuzzer.cpp
index 68c79747..10fac42a 100644
--- a/test/fuzzers/spvtools_opt_size_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_size_fuzzer.cpp
@@ -16,9 +16,15 @@
#include <vector>
#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/test/fuzzers/spvtools_val_fuzzer.cpp b/test/fuzzers/spvtools_val_fuzzer.cpp
index 5dc4303b..fd6396cd 100644
--- a/test/fuzzers/spvtools_val_fuzzer.cpp
+++ b/test/fuzzers/spvtools_val_fuzzer.cpp
@@ -16,9 +16,15 @@
#include <vector>
#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/test/hex_float_test.cpp b/test/hex_float_test.cpp
index c422f756..ffdb8bda 100644
--- a/test/hex_float_test.cpp
+++ b/test/hex_float_test.cpp
@@ -1325,6 +1325,76 @@ TEST(FloatProxy, Lowest) {
Eq(std::numeric_limits<double>::lowest()));
}
+template <typename T>
+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<FloatProxy<T>>(value)) {}
+
+ std::string literal;
+ bool expect_success;
+ std::string expected_suffix;
+ HexFloat<FloatProxy<T>> expected_value;
+};
+
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StreamParseCase<T>& fspc) {
+ os << "StreamParseCase(" << fspc.literal
+ << ", expect_succes:" << int(fspc.expect_success) << ","
+ << fspc.expected_suffix << "," << fspc.expected_value << ")";
+ return os;
+}
+
+using FloatStreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
+
+TEST_P(FloatStreamParseTest, Samples) {
+ std::stringstream input(GetParam().literal);
+ HexFloat<FloatProxy<float>> 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<StreamParseCase<float>>{
+ {"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<StreamParseCase<float>>{
+ // 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/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 6f83dfeb..bc0ee055 100644
--- a/test/operand_capabilities_test.cpp
+++ b/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<EnumCapabilityCase>{
- 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/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 11299755..bc44e8d6 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -85,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/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 5b4291da..f228c8cb 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -7063,6 +7063,287 @@ TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) {
SinglePassRunAndMatch<AggressiveDCEPass>(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<AggressiveDCEPass>(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<AggressiveDCEPass>(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<AggressiveDCEPass>(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<const char*> 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<AggressiveDCEPass>(
+ 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<AggressiveDCEPass>(
+ 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<AggressiveDCEPass>(
+ 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<AggressiveDCEPass>(
+ 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<AggressiveDCEPass>(text, text, false);
+}
} // namespace
} // namespace opt
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index 140a5c09..6903c4e7 100644
--- a/test/opt/block_merge_test.cpp
+++ b/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<BlockMergePass>(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<BlockMergePass>(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<BlockMergePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index ef734353..212e0514 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -1208,6 +1208,32 @@ TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) {
auto result = SinglePassRunAndMatch<CCPPass>(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<CCPPass>(text, text, false);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp
index aed14c9d..5be3ba63 100644
--- a/test/opt/combine_access_chains_test.cpp
+++ b/test/opt/combine_access_chains_test.cpp
@@ -768,6 +768,32 @@ OpFunctionEnd
SinglePassRunAndMatch<CombineAccessChains>(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<CombineAccessChains>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index ca6ee583..6a06de84 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/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<ConvertToHalfPass>(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<ConvertToHalfPass>(test, true);
+ EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp
index 72bc7f6f..a4599f0f 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -1814,6 +1814,31 @@ OpFunctionEnd
SinglePassRunAndMatch<CopyPropagateArrays>(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<CopyPropagateArrays>(text, text, false);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp
index f89befb4..9c1ef2a3 100644
--- a/test/opt/dead_branch_elim_test.cpp
+++ b/test/opt/dead_branch_elim_test.cpp
@@ -3403,6 +3403,71 @@ OpFunctionEnd
SinglePassRunAndMatch<DeadBranchElimPass>(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<DeadBranchElimPass>(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<DeadBranchElimPass>(text, text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
diff --git a/test/opt/dead_insert_elim_test.cpp b/test/opt/dead_insert_elim_test.cpp
index 9ea948a4..268e6590 100644
--- a/test/opt/dead_insert_elim_test.cpp
+++ b/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<DeadInsertElimPass>(before, after, true, true);
+}
+
TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) {
// Dead insert eliminated with phi in insertion chain.
//
diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp
index 49407fd5..e87d0bea 100644
--- a/test/opt/debug_info_manager_test.cpp
+++ b/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<IRContext> 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/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp
index b35ad474..dcb625d6 100644
--- a/test/opt/desc_sroa_test.cpp
+++ b/test/opt/desc_sroa_test.cpp
@@ -770,6 +770,69 @@ TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) {
SinglePassRunAndMatch<DescriptorScalarReplacement>(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<DescriptorScalarReplacement>(shader, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp
index 78874330..e277999e 100644
--- a/test/opt/eliminate_dead_member_test.cpp
+++ b/test/opt/eliminate_dead_member_test.cpp
@@ -626,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<opt::EliminateDeadMembersPass>(
+ 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/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index da5b017d..0487e78c 100644
--- a/test/opt/fold_test.cpp
+++ b/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<uint32_t>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
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<bool>(
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<bool>(
@@ -6052,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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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<bool>(
+ 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)
));
@@ -6420,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<bool>(
+ 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<bool>(
+ 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/test/opt/inline_opaque_test.cpp b/test/opt/inline_opaque_test.cpp
index 8cb8925c..e4db4325 100644
--- a/test/opt/inline_opaque_test.cpp
+++ b/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<InlineOpaquePass>(
+ 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/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index 29399013..d22f027c 100644
--- a/test/opt/inline_test.cpp
+++ b/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<InlineExhaustivePass>(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<InlineExhaustivePass>(before, after, false, true);
+}
+
TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) {
const std::string text =
R"(
@@ -3259,6 +3383,82 @@ TEST_F(InlineTest, DebugSimple) {
SinglePassRunAndMatch<InlineExhaustivePass>(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<InlineExhaustivePass>(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<InlineExhaustivePass>(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<InlineExhaustivePass>(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<InlineExhaustivePass>(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<InlineExhaustivePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Empty modules
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index 41ead67a..95114b23 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/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<InstBuffAddrCheckPass>(
+ defs + decorates + globals + main + output_funcs, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index 475dd235..ccdd032e 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/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/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp
index 8e1cee61..28b8a07d 100644
--- a/test/opt/local_single_block_elim.cpp
+++ b/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<LocalSingleBlockLoadStoreElimPass>(
+ predefs_before + before, predefs_before + after, true, true);
+}
+
TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) {
// #version 140
//
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index c94ff372..5d910c4e 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/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<LocalSingleStoreElimPass>(predefs + before,
+ predefs + after, true, true);
+}
+
TEST_F(LocalSingleStoreElimTest, ThreeStores) {
// Three stores to multiple loads of v is not optimized.
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index ca9aba33..4b7542fe 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -2165,6 +2165,140 @@ OpFunctionEnd
SinglePassRunAndMatch<SSARewritePass>(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<SSARewritePass>(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<SSARewritePass>(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<SSARewritePass>(text, text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// No optimization in the presence of
diff --git a/test/opt/loop_optimizations/unroll_assumptions.cpp b/test/opt/loop_optimizations/unroll_assumptions.cpp
index 0f933021..159e4a14 100644
--- a/test/opt/loop_optimizations/unroll_assumptions.cpp
+++ b/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<PartialUnrollerTestPass<2>>(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<LoopUnroller>(text, text, false);
+ SinglePassRunAndCheck<PartialUnrollerTestPass<1>>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index 6a3cb6ee..ac0dfde7 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -378,6 +378,185 @@ OpFunctionEnd)";
SinglePassRunAndMatch<LoopUnroller>(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<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ Module* module = context->module();
+ EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
+ << text << std::endl;
+
+ LoopUnroller loop_unroller;
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
template <int factor>
class PartialUnrollerTestPass : public Pass {
public:
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index fd97efab..21960d17 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -2567,6 +2567,39 @@ TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) {
SinglePassRunAndMatch<MergeReturnPass>(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<MergeReturnPass>(text, /* skip_nop = */ true);
+ EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp
index 7672e8f3..abb5cde6 100644
--- a/test/opt/reduce_load_size_test.cpp
+++ b/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<ReduceLoadSize>(test, false);
+ SinglePassRunAndMatch<ReduceLoadSize>(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<ReduceLoadSize>(test, false);
+ SinglePassRunAndMatch<ReduceLoadSize>(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<ReduceLoadSize>(test, test, true, false);
+ SinglePassRunAndCheck<ReduceLoadSize>(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<ReduceLoadSize>(test, test, true, false);
+ SinglePassRunAndCheck<ReduceLoadSize>(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<ReduceLoadSize>(test, test, true, false);
+ SinglePassRunAndCheck<ReduceLoadSize>(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<ReduceLoadSize>(test, false, 1.1);
}
} // namespace
diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp
index 474f4661..28eda73e 100644
--- a/test/opt/redundancy_elimination_test.cpp
+++ b/test/opt/redundancy_elimination_test.cpp
@@ -335,6 +335,32 @@ TEST_F(RedundancyEliminationTest, OpenCLDebugInfo100) {
SinglePassRunAndMatch<RedundancyEliminationPass>(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<RedundancyEliminationPass>(text, text, false);
+}
+
} // namespace
} // namespace opt
-} // namespace spvtools
+} // namespace spvtools \ No newline at end of file
diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp
index 14cde0b9..b9cb0de0 100644
--- a/test/opt/relax_float_ops_test.cpp
+++ b/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<RelaxFloatOpsPass>(
+ defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true,
+ true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp
new file mode 100644
index 00000000..ca625812
--- /dev/null
+++ b/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 <string>
+
+#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<ReplaceDescArrayAccessUsingVarIndex>(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<ReplaceDescArrayAccessUsingVarIndex>(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<ReplaceDescArrayAccessUsingVarIndex>(text, true);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 8115f5fb..8cb888c9 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/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<ScalarReplacementPass>(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<ScalarReplacementPass>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp
index 5e63862e..f1dd50ee 100644
--- a/test/opt/set_spec_const_default_value_test.cpp
+++ b/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/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp
index 7a9696ea..7727f567 100644
--- a/test/opt/simplification_test.cpp
+++ b/test/opt/simplification_test.cpp
@@ -360,6 +360,31 @@ OpFunctionEnd
SinglePassRunAndMatch<SimplificationPass>(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<SimplificationPass>(text, text, false);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index 7f64ffd7..2cd3c7df 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/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/test/opt/vector_dce_test.cpp b/test/opt/vector_dce_test.cpp
index 9bdad375..b14e2256 100644
--- a/test/opt/vector_dce_test.cpp
+++ b/test/opt/vector_dce_test.cpp
@@ -1351,6 +1351,72 @@ OpFunctionEnd
SinglePassRunAndMatch<VectorDCE>(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<VectorDCE>(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<VectorDCE>(text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp
index 57f0a6ce..99d9ed68 100644
--- a/test/text_to_binary_test.cpp
+++ b/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/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 39f9a098..64eba446 100644
--- a/test/val/CMakeLists.txt
+++ b/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
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp
new file mode 100644
index 00000000..889c76ca
--- /dev/null
+++ b/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 <string>
+#include <vector>
+
+#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<bool>;
+
+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<std::string>;
+
+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<std::string>;
+
+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<std::string>;
+
+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<std::string>;
+
+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<std::string>;
+
+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<std::string>;
+
+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<std::string>;
+
+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<std::tuple<std::string, std::string>>;
+
+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<std::tuple<std::string, std::string>>;
+
+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<std::string>;
+
+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/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp
index b82fc97e..856ad02c 100644
--- a/test/val/val_arithmetics_test.cpp
+++ b/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 <id> "
+ "'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 <id> '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 <id> '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 <id> '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/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index b7f6948e..d1a030a8 100644
--- a/test/val/val_atomics_test.cpp
+++ b/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/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 84952645..dff9adfe 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -2861,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() {
@@ -3482,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());
@@ -3781,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) {
@@ -3801,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/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index 82f8d381..c432c3cf 100644
--- a/test/val/val_capability_test.cpp
+++ b/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<std::string>{"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<std::string>{"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<std::string>{"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<std::string>{"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<std::string>{"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<std::string>{"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<std::string>{"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<std::string>{"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),
- AllCapabilities()),
-std::make_pair(std::string(kOpenCLMemoryModel) +
- "OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn WorkgroupSize\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 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 LocalInvocationId\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 GlobalInvocationId\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 LocalInvocationIndex\n"
+ "OpDecorate %var 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/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 6ae2ee6d..311cfa77 100644
--- a/test/val/val_cfg_test.cpp
+++ b/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/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index b9a413e3..f2953edc 100644
--- a/test/val/val_decoration_test.cpp
+++ b/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) {
@@ -7791,6 +7773,70 @@ OpFunctionEnd
"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/test/val/val_derivatives_test.cpp b/test/val/val_derivatives_test.cpp
index 606abb93..0a846610 100644
--- a/test/val/val_derivatives_test.cpp
+++ b/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<std::string>;
TEST_P(ValidateHalfDerivatives, ScalarFailure) {
diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp
index d3b9f038..307a8009 100644
--- a/test/val/val_ext_inst_debug_test.cpp
+++ b/test/val/val_ext_inst_debug_test.cpp
@@ -354,7 +354,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugSourceInFunction) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst,
@@ -436,7 +436,7 @@ TEST_P(ValidateLocalDebugInfoOutOfFunction, VulkanDebugInfo100DebugScope) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string constants = R"(
@@ -738,7 +738,7 @@ INSTANTIATE_TEST_SUITE_P(OpenCLAndVkDebugInfo100, ValidateXDebugInfo,
)",
R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)",
}));
@@ -798,7 +798,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugCompilationUnitFail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string constants = R"(
@@ -872,7 +872,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailName) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -941,7 +941,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailSize) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1107,7 +1107,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifier) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1141,7 +1141,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifierFail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1398,7 +1398,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1436,7 +1436,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayWithVariableSize) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1466,7 +1466,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailBaseType) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1499,7 +1499,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCount) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1534,7 +1534,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1569,7 +1569,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1610,7 +1610,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1762,7 +1762,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVector) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1792,7 +1792,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1825,7 +1825,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentZero) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1858,7 +1858,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentFive) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -1967,7 +1967,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypedef) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -2001,7 +2001,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugTypedef, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -2152,7 +2152,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionAndParams) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -2184,7 +2184,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailReturn) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -2219,7 +2219,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailParam) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -2349,7 +2349,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugTypeEnum) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -2383,7 +2383,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugTypeEnum, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -2705,7 +2705,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -2753,7 +2753,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -2822,7 +2822,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -3029,7 +3029,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -3069,7 +3069,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -3133,7 +3133,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -3290,7 +3290,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugLexicalBlock) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -3321,7 +3321,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugLexicalBlock, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -3363,7 +3363,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailScope) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -3394,7 +3394,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailInlinedAt) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -3505,7 +3505,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugLocalVariable) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -3540,7 +3540,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugLocalVariable, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -3736,7 +3736,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugDeclare) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string body = R"(
@@ -3753,7 +3753,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugDeclareParam) {
CompileSuccessfully(R"(
OpCapability Shader
OpExtension "SPV_KHR_non_semantic_info"
- %1 = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %in_var_COLOR
%4 = OpString "test.hlsl"
@@ -3843,7 +3843,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugDeclare, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const auto& param = GetParam();
@@ -3913,7 +3913,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugExpression) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
@@ -3929,7 +3929,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugExpressionFail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
@@ -4159,7 +4159,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4198,7 +4198,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4237,7 +4237,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4274,7 +4274,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4315,7 +4315,7 @@ main() {}
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4522,7 +4522,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariable) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4555,7 +4555,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableStaticMember) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4587,7 +4587,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableDebugInfoNone) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4618,7 +4618,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariableConst) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
@@ -4652,7 +4652,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugGlobalVariable, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
@@ -4806,7 +4806,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAt) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string body = R"(
@@ -4844,7 +4844,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string body = R"(
@@ -4883,7 +4883,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail2) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string body = R"(
@@ -5042,7 +5042,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugValue) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string body = R"(
@@ -5084,7 +5084,7 @@ TEST_F(ValidateVulkan100DebugInfo, DebugValueWithVariableIndex) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const std::string body = R"(
@@ -5122,7 +5122,7 @@ TEST_P(ValidateVulkan100DebugInfoDebugValue, Fail) {
const std::string extension = R"(
OpExtension "SPV_KHR_non_semantic_info"
-%DbgExt = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
)";
const auto& param = GetParam();
@@ -5153,7 +5153,7 @@ TEST_F(ValidateVulkan100DebugInfo, VulkanDebugInfoSample) {
ss << R"(
OpCapability Shader
OpExtension "SPV_KHR_non_semantic_info"
- %id_1 = OpExtInstImport "NonSemantic.Vulkan.DebugInfo.100"
+ %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
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
index b014ad62..2b6df04d 100644
--- a/test/val/val_ext_inst_test.cpp
+++ b/test/val/val_ext_inst_test.cpp
@@ -1580,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
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index dd4c952c..ac057493 100644
--- a/test/val/val_id_test.cpp
+++ b/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 <id> "
- "'1[%uint_3]' is not a scalar specialization "
- "constant."));
+ HasSubstr("SpecId decoration on target <id> "
+ "'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 <id> '1[%1]' is "
- "not a scalar specialization constant."));
+ HasSubstr("SpecId decoration on target <id> '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 <id> '1[%1]' is "
- "not a scalar specialization constant."));
+ HasSubstr("SpecId decoration on target <id> '1[%1]' "
+ "must be a scalar specialization constant"));
}
TEST_F(ValidateIdWithMessage, SpecIdTargetGood) {
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index 701e35e1..11b14fb0 100644
--- a/test/val/val_image_test.cpp
+++ b/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/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp
index be13cd71..a01fc19b 100644
--- a/test/val/val_interfaces_test.cpp
+++ b/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,68 @@ 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
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index d34c97f9..7ebd7c00 100644
--- a/test/val/val_layout_test.cpp
+++ b/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/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 616a88f6..2a884c4d 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -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
@@ -4372,6 +4374,29 @@ OpFunctionEnd
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/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index d060bb79..02a61327 100644
--- a/test/val/val_modes_test.cpp
+++ b/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
diff --git a/test/wasm/test.js b/test/wasm/test.js
new file mode 100644
index 00000000..7f0d8f3c
--- /dev/null
+++ b/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/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 28770cb9..04f81b8c 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -163,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
@@ -387,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[=<threshold>]
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 <threshold>,
+ we replace the load. <threshold> 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/tools/val/val.cpp b/tools/val/val.cpp
index 21a7d8f4..55321dab 100644
--- a/tools/val/val.cpp
+++ b/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/utils/roll_deps.sh b/utils/roll_deps.sh
index 7ecfdd3b..f61f2a31 100755
--- a/utils/roll_deps.sh
+++ b/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}"
+