Reland "Merge tag 'vulkan-sdk-1.4.350.1' into main" This is a reland of commit c6c58028313c7a46c9ad7661f21543e788b9cf57 Original change's description: > Merge tag 'vulkan-sdk-1.4.350.1' into main > > Bug: 510484520 > Change-Id: I14ecf0679e1a1468f39748261f2618fc365e2686 > Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/spirv-tools/+/1708910 > Reviewed-by: Craig Stout <cstout@google.com> Bug: 510484520 Change-Id: I9c143b1baac07e49a6e59f340fb3c4eda7588028 Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/spirv-tools/+/1721692 Reviewed-by: Craig Stout <cstout@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
diff --git a/.bazelversion b/.bazelversion index a8907c0..ba7f754 100644 --- a/.bazelversion +++ b/.bazelversion
@@ -1 +1 @@ -7.0.2 +7.4.0
diff --git a/.github/workflows/autoroll.yml b/.github/workflows/autoroll.yml index eb0b0fe..7494ac8 100644 --- a/.github/workflows/autoroll.yml +++ b/.github/workflows/autoroll.yml
@@ -9,6 +9,7 @@ jobs: update-dependencies: + if: github.repository == 'KhronosGroup/SPIRV-Tools' permissions: contents: write pull-requests: write @@ -16,7 +17,7 @@ runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Checkout the depot tools they are needed by roll_deps.sh - name: Checkout depot tools
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 38dd1b3..8e52db3 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml
@@ -7,31 +7,33 @@ branches: - 'main' pull_request: + types: [opened, synchronize, reopened, unlabeled] jobs: build: + if: github.event.action != 'unlabeled' || github.event.label.name == 'kokoro:run' timeout-minutes: 120 strategy: matrix: - os: [ubuntu-latest, windows-2019] + os: [ubuntu-latest, windows-2025] runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: '0' - name: Download dependencies run: python3 utils/git-sync-deps - name: Mount Bazel cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.bazel/cache key: bazel-cache-${{ runner.os }} - name: Build All run: bazel --output_user_root=~/.bazel/cache build //... - name: Test All - run: bazel --output_user_root=~/.bazel/cache test //... + run: bazel --output_user_root=~/.bazel/cache test --test_output=errors //... # iOS is 10x expensive to run on GitHub machines, so only run if we know something else passed # The steps are unfortunately duplicated because github actions requires 2 jobs for a dependency @@ -41,17 +43,17 @@ runs-on: macos-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: '0' - name: Download dependencies run: python3 utils/git-sync-deps - name: Mount Bazel cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.bazel/cache key: bazel-cache-${{ runner.os }} - name: Build All run: bazel --output_user_root=~/.bazel/cache build //... - name: Test All - run: bazel --output_user_root=~/.bazel/cache test //... + run: bazel --output_user_root=~/.bazel/cache test --test_output=errors //...
diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index b856e72..22c87b2 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml
@@ -14,8 +14,8 @@ runs-on: macos-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: lukka/get-cmake@71b7adfe2603f48bb9ed50d2b01a72499ae94885 # v3.31.0 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: lukka/get-cmake@7bfc9baacbbdcb5e37957ad05c3546b3e222be3c # v4.02 - name: Download dependencies run: python3 utils/git-sync-deps # NOTE: The MacOS SDK ships universal binaries. CI should reflect this.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e0d865..c52b3cc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml
@@ -13,7 +13,7 @@ prepare-release-job: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Prepare CHANGELOG for version run: | python utils/generate_changelog.py CHANGES "${{ github.ref_name }}" VERSION_CHANGELOG
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 8a2c9ec..decd4ed 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml
@@ -23,12 +23,12 @@ steps: - name: "Checkout code" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif @@ -40,7 +40,7 @@ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file path: results.sarif @@ -48,6 +48,6 @@ # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 + uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: sarif_file: results.sarif
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index cbe2143..7aa9679 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml
@@ -2,14 +2,20 @@ permissions: contents: read -on: [push, pull_request] +on: + push: + branches: + - 'main' + pull_request: + types: [opened, synchronize, reopened, unlabeled] jobs: build: + if: github.event.action != 'unlabeled' || github.event.label.name == 'kokoro:run' runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: '0' - name: Build web
diff --git a/.gitignore b/.gitignore index e85cea9..29e83c9 100644 --- a/.gitignore +++ b/.gitignore
@@ -1,6 +1,8 @@ .clang_complete .ycm_extra_conf.py* *.pyc +android_test/lib +android_test/app compile_commands.json /build*/ /buildtools/ @@ -11,6 +13,7 @@ /external/effcee /external/re2 /external/protobuf +/external/mimalloc /out /TAGS /third_party/llvm-build/
diff --git a/Android.mk b/Android.mk index 1414b52..6c2ecfa 100644 --- a/Android.mk +++ b/Android.mk
@@ -11,7 +11,6 @@ source/diagnostic.cpp \ source/disassemble.cpp \ source/ext_inst.cpp \ - source/enum_string_mapping.cpp \ source/extensions.cpp \ source/libspirv.cpp \ source/name_mapper.cpp \ @@ -25,6 +24,7 @@ source/spirv_target_env.cpp \ source/spirv_validator_options.cpp \ source/table.cpp \ + source/table2.cpp \ source/text.cpp \ source/text_handler.cpp \ source/to_string.cpp \ @@ -53,9 +53,12 @@ source/val/validate_debug.cpp \ source/val/validate_decorations.cpp \ source/val/validate_derivatives.cpp \ + source/val/validate_dot_product.cpp \ source/val/validate_extensions.cpp \ source/val/validate_execution_limitations.cpp \ source/val/validate_function.cpp \ + source/val/validate_graph.cpp \ + source/val/validate_group.cpp \ source/val/validate_id.cpp \ source/val/validate_image.cpp \ source/val/validate_interfaces.cpp \ @@ -67,16 +70,20 @@ source/val/validate_mode_setting.cpp \ source/val/validate_layout.cpp \ source/val/validate_literals.cpp \ + source/val/validate_logical_pointers.cpp \ source/val/validate_logicals.cpp \ source/val/validate_non_uniform.cpp \ + source/val/validate_pipe.cpp \ source/val/validate_primitives.cpp \ source/val/validate_ray_query.cpp \ source/val/validate_ray_tracing.cpp \ source/val/validate_ray_tracing_reorder.cpp \ source/val/validate_scopes.cpp \ source/val/validate_small_type_uses.cpp \ + source/val/validate_tensor.cpp \ source/val/validate_tensor_layout.cpp \ - source/val/validate_type.cpp + source/val/validate_type.cpp\ + source/val/validate_invalid_type.cpp SPVTOOLS_OPT_SRC_FILES := \ source/opt/aggressive_dead_code_elim_pass.cpp \ @@ -125,6 +132,7 @@ source/opt/fold_spec_constant_op_and_composite_pass.cpp \ source/opt/freeze_spec_constant_value_pass.cpp \ source/opt/function.cpp \ + source/opt/graph.cpp \ source/opt/graphics_robust_access_pass.cpp \ source/opt/if_conversion.cpp \ source/opt/inline_pass.cpp \ @@ -137,6 +145,7 @@ source/opt/invocation_interlock_placement_pass.cpp \ source/opt/ir_context.cpp \ source/opt/ir_loader.cpp \ + source/opt/legalize_multidim_array_pass.cpp \ source/opt/licm_pass.cpp \ source/opt/liveness.cpp \ source/opt/local_access_chain_convert_pass.cpp \ @@ -167,16 +176,19 @@ source/opt/redundancy_elimination.cpp \ source/opt/register_pressure.cpp \ source/opt/relax_float_ops_pass.cpp \ + source/opt/canonicalize_ids_pass.cpp \ source/opt/remove_dontinline_pass.cpp \ source/opt/remove_duplicates_pass.cpp \ source/opt/remove_unused_interface_variables_pass.cpp \ source/opt/replace_desc_array_access_using_var_index.cpp \ source/opt/replace_invalid_opc.cpp \ + source/opt/resolve_binding_conflicts_pass.cpp \ source/opt/scalar_analysis.cpp \ source/opt/scalar_analysis_simplification.cpp \ source/opt/scalar_replacement_pass.cpp \ source/opt/set_spec_constant_default_value_pass.cpp \ source/opt/simplification_pass.cpp \ + source/opt/split_combined_image_sampler_pass.cpp \ source/opt/spread_volatile_semantics.cpp \ source/opt/ssa_rewrite_pass.cpp \ source/opt/strength_reduction_pass.cpp \ @@ -196,52 +208,49 @@ source/opt/wrap_opkill.cpp # Locations of grammar files. -# -SPV_COREUNIFIED1_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/spirv.core.grammar.json -SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.std.450.grammar.json -SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json -SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json -SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json -SPV_VKDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json +GRAMMAR_DIR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1 define gen_spvtools_grammar_tables -$(call generate-file-dir,$(1)/core.insts-unified1.inc) -$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc \ -$(1)/glsl.std.450.insts.inc \ -$(1)/opencl.std.insts.inc \ +# $1 is the output directory, which is unique per ABI. +# Rules for creating grammar tables. They are statically compiled +# into the SPIRV-Tools code. +$(call generate-file-dir,$(1)/core_tables_body.inc) +$(1)/core_tables_body.inc \ +$(1)/core_tables_header.inc \ : \ - $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - $(SPV_COREUNIFIED1_GRAMMAR) \ - $(SPV_GLSL_GRAMMAR) \ - $(SPV_OpenCL_GRAMMAR) \ - $(SPV_DEBUGINFO_GRAMMAR) \ - $(SPV_CLDEBUGINFO100_GRAMMAR) - @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \ - --extinst-glsl-grammar=$(SPV_GLSL_GRAMMAR) \ - --extinst-opencl-grammar=$(SPV_OPENCL_GRAMMAR) \ - --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \ - --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \ - --core-insts-output=$(1)/core.insts-unified1.inc \ - --glsl-insts-output=$(1)/glsl.std.450.insts.inc \ - --opencl-insts-output=$(1)/opencl.std.insts.inc \ - --operand-kinds-output=$(1)/operand.kinds-unified1.inc \ - --output-language=c++ - @echo "[$(TARGET_ARCH_ABI)] Grammar (from unified1) : instructions & operands <= grammar JSON files" -$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-unified1.inc -$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-unified1.inc -$(LOCAL_PATH)/source/ext_inst.cpp: \ - $(1)/glsl.std.450.insts.inc \ - $(1)/opencl.std.insts.inc \ - $(1)/debuginfo.insts.inc \ - $(1)/opencl.debuginfo.100.insts.inc \ - $(1)/nonsemantic.shader.debuginfo.100.insts.inc \ - $(1)/spv-amd-gcn-shader.insts.inc \ - $(1)/spv-amd-shader-ballot.insts.inc \ - $(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \ - $(1)/spv-amd-shader-trinary-minmax.insts.inc -$(LOCAL_PATH)/source/opt/amd_ext_to_khr.cpp: \ - $(1)/spv-amd-shader-ballot.insts.inc + $(LOCAL_PATH)/utils/ggt.py \ + $(GRAMMAR_DIR)/extinst.debuginfo.grammar.json \ + $(GRAMMAR_DIR)/extinst.glsl.std.450.grammar.json \ + $(GRAMMAR_DIR)/extinst.nonsemantic.clspvreflection.grammar.json \ + $(GRAMMAR_DIR)/extinst.nonsemantic.shader.debuginfo.100.grammar.json \ + $(GRAMMAR_DIR)/extinst.nonsemantic.vkspreflection.grammar.json \ + $(GRAMMAR_DIR)/extinst.opencl.debuginfo.100.grammar.json \ + $(GRAMMAR_DIR)/extinst.opencl.std.100.grammar.json \ + $(GRAMMAR_DIR)/extinst.spv-amd-gcn-shader.grammar.json \ + $(GRAMMAR_DIR)/extinst.spv-amd-shader-ballot.grammar.json \ + $(GRAMMAR_DIR)/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json \ + $(GRAMMAR_DIR)/extinst.spv-amd-shader-trinary-minmax.grammar.json \ + $(GRAMMAR_DIR)/spirv.core.grammar.json + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/ggt.py \ + --core-tables-body-output=$(1)/core_tables_body.inc \ + --core-tables-header-output=$(1)/core_tables_header.inc \ + --spirv-core-grammar=$(GRAMMAR_DIR)/spirv.core.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.debuginfo.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.glsl.std.450.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.nonsemantic.clspvreflection.grammar.json \ + --extinst=SHDEBUG100_,$(GRAMMAR_DIR)/extinst.nonsemantic.shader.debuginfo.100.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.nonsemantic.vkspreflection.grammar.json \ + --extinst=CLDEBUG100_,$(GRAMMAR_DIR)/extinst.opencl.debuginfo.100.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.opencl.std.100.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-gcn-shader.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-shader-ballot.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json \ + --extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-shader-trinary-minmax.grammar.json + @echo "[$(TARGET_ARCH_ABI)] Grammar tables <= grammar JSON files" +# Make all source files depend on the generated core tables +$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \ + : $(1)/core_tables_body.inc \ + $(1)/core_tables_header.inc endef $(eval $(call gen_spvtools_grammar_tables,$(SPVTOOLS_OUT_PATH))) @@ -262,55 +271,13 @@ $(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \ : $(1)/$(2).h endef -# We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100 -$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR))) -$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR))) -$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR))) -define gen_spvtools_vendor_tables -$(call generate-file-dir,$(1)/$(2).insts.inc) -$(1)/$(2).insts.inc : \ - $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - $(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.$(2).grammar.json - @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - --extinst-vendor-grammar=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/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,$(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,"")) -$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-trinary-minmax,"")) -$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.clspvreflection,"")) -$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.vkspreflection,"")) +# Generate C++ headers for some extended instruction sets. +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(GRAMMAR_DIR)/extinst.debuginfo.grammar.json)) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(GRAMMAR_DIR)/extinst.opencl.debuginfo.100.grammar.json)) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(GRAMMAR_DIR)/extinst.nonsemantic.shader.debuginfo.100.grammar.json)) -define gen_spvtools_enum_string_mapping -$(call generate-file-dir,$(1)/extension_enum.inc.inc) -$(1)/extension_enum.inc $(1)/enum_string_mapping.inc: \ - $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - $(SPV_COREUNIFIED1_GRAMMAR) - @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ - --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \ - --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \ - --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \ - --extension-enum-output=$(1)/extension_enum.inc \ - --enum-string-mapping-output=$(1)/enum_string_mapping.inc \ - --output-language=c++ - @echo "[$(TARGET_ARCH_ABI)] Generate enum<->string mapping <= grammar JSON files" -# Generated header extension_enum.inc is transitively included by table.h, which is -# used pervasively. Capture the pervasive dependency. -$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \ - : $(1)/extension_enum.inc -$(LOCAL_PATH)/source/enum_string_mapping.cpp: $(1)/enum_string_mapping.inc -endef -$(eval $(call gen_spvtools_enum_string_mapping,$(SPVTOOLS_OUT_PATH))) define gen_spvtools_build_version_inc $(call generate-file-dir,$(1)/dummy_filename)
diff --git a/BUILD.bazel b/BUILD.bazel index 526d08e..d1a9d42 100644 --- a/BUILD.bazel +++ b/BUILD.bazel
@@ -5,14 +5,12 @@ "DEBUGINFO_GRAMMAR_JSON_FILE", "SHDEBUGINFO100_GRAMMAR_JSON_FILE", "TEST_COPTS", - "generate_core_tables", - "generate_enum_string_mapping", + "create_grammar_tables_target", + "ExtInst", "generate_extinst_lang_headers", - "generate_glsl_tables", - "generate_opencl_tables", - "generate_vendor_tables", "incompatible_with", ) +load("@rules_python//python:defs.bzl", "py_binary") package( default_visibility = ["//visibility:private"], @@ -29,8 +27,18 @@ ]) py_binary( - name = "generate_grammar_tables", - srcs = ["utils/generate_grammar_tables.py"], + # The script that generates compressed grammar tables for + # instructions and operands. + name = "ggt", + main = "utils/ggt.py", # The file found by $(location :ggt) + srcs = [ + "utils/ggt.py", + "utils/Table/__init__.py", + "utils/Table/Context.py", + "utils/Table/IndexRange.py", + "utils/Table/Operand.py", + "utils/Table/StringList.py", + ], ) py_binary( @@ -38,36 +46,25 @@ srcs = ["utils/generate_language_headers.py"], ) -generate_core_tables(version = "unified1") - -generate_enum_string_mapping(version = "unified1") - -generate_opencl_tables(version = "unified1") - -generate_glsl_tables(version = "unified1") - -generate_vendor_tables(extension = "spv-amd-shader-explicit-vertex-parameter") - -generate_vendor_tables(extension = "spv-amd-shader-trinary-minmax") - -generate_vendor_tables(extension = "spv-amd-gcn-shader") - -generate_vendor_tables(extension = "spv-amd-shader-ballot") - -generate_vendor_tables(extension = "debuginfo") - -generate_vendor_tables(extension = "nonsemantic.clspvreflection") - -generate_vendor_tables(extension = "nonsemantic.vkspreflection") - -generate_vendor_tables( - extension = "opencl.debuginfo.100", - operand_kind_prefix = "CLDEBUG100_", -) - -generate_vendor_tables( - extension = "nonsemantic.shader.debuginfo.100", - operand_kind_prefix = "SHDEBUG100_", +create_grammar_tables_target( + name="core", # unused + extinsts = [ + ExtInst("glsl.std.450", target="spirv_glsl_grammar_unified1"), + ExtInst("opencl.std.100", target="spirv_opencl_grammar_unified1"), + ExtInst("opencl.debuginfo.100", prefix="CLDEBUG100_"), + ExtInst("nonsemantic.shader.debuginfo", prefix="SHDEBUG100_"), + ExtInst("tosa.001000.1", target="spirv_ext_inst_tosa_001000_1", prefix="TOSA_"), + ExtInst("arm.motion-engine.100", target="spirv_ext_inst_arm_motion_engine_100"), + ] + [ExtInst(e) for e in [ + "spv-amd-shader-explicit-vertex-parameter", + "spv-amd-shader-trinary-minmax", + "spv-amd-gcn-shader", + "spv-amd-shader-ballot", + "debuginfo", + "nonsemantic.clspvreflection", + "nonsemantic.vkspreflection", + ] + ] ) generate_extinst_lang_headers( @@ -136,24 +133,14 @@ "source/*.cpp", "source/util/*.cpp", "source/val/*.cpp", + ], exclude = [ + "source/mimalloc.cpp" ]) + [ ":build_version_inc", - ":gen_core_tables_unified1", - ":gen_enum_string_mapping", + ":gen_compressed_tables", ":gen_extinst_lang_headers_DebugInfo", ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100", ":gen_extinst_lang_headers_OpenCLDebugInfo100", - ":gen_glsl_tables_unified1", - ":gen_opencl_tables_unified1", - ":gen_vendor_tables_debuginfo", - ":gen_vendor_tables_nonsemantic_clspvreflection", - ":gen_vendor_tables_nonsemantic_vkspreflection", - ":gen_vendor_tables_nonsemantic_shader_debuginfo_100", - ":gen_vendor_tables_opencl_debuginfo_100", - ":gen_vendor_tables_spv_amd_gcn_shader", - ":gen_vendor_tables_spv_amd_shader_ballot", - ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter", - ":gen_vendor_tables_spv_amd_shader_trinary_minmax", ":generators_inc", ], hdrs = [ @@ -191,9 +178,7 @@ cc_library( name = "spirv_tools_opt_internal", - srcs = glob(["source/opt/*.cpp"]) + [ - ":gen_vendor_tables_spv_amd_shader_ballot", - ], + srcs = glob(["source/opt/*.cpp"]), hdrs = glob(["source/opt/*.h"]) + [ "include/spirv-tools/optimizer.hpp", ], @@ -201,6 +186,7 @@ deps = [ ":spirv_tools_internal", "@spirv_headers//:spirv_common_headers", + "@spirv_headers//:spirv_c_headers", ], ) @@ -218,7 +204,7 @@ cc_library( name = "spirv_tools_link", srcs = glob(["source/link/*.cpp"]), - hdrs = ["include/spirv-tools/linker.hpp"], + hdrs = ["include/spirv-tools/linker.hpp", "source/link/fnvar.h"], copts = COMMON_COPTS, linkstatic = 1, visibility = ["//visibility:public"], @@ -450,6 +436,7 @@ ":tools_io", "@googletest//:gtest", "@googletest//:gtest_main", + "@spirv_headers//:spirv_common_headers", ], ) for f in glob( [ @@ -559,6 +546,7 @@ [cc_test( name = "opt_{testcase}_test".format(testcase = f[len("test/opt/"):-len("_test.cpp")]), size = "small", + timeout = "moderate" if f[len("test/opt/"):-len("_test.cpp")] == "fold" else "short", srcs = [f], copts = TEST_COPTS, linkstatic = 1, @@ -684,16 +672,35 @@ ":val_test_lib", "@googletest//:gtest", "@googletest//:gtest_main", + "@spirv_headers//:spirv_cpp11_headers", ], ) for f in glob( ["test/val/val_*_test.cpp"], exclude = [ "test/val/val_capability_test.cpp", + "test/val/val_ext_inst_debug_test.cpp", "test/val/val_limits_test.cpp", ], )] cc_test( + name = "val_ext_inst_debug_test", + size = "small", + srcs = ["test/val/val_ext_inst_debug_test.cpp"], + copts = TEST_COPTS, + linkstatic = 1, + deps = [ + ":spirv_tools_internal", + ":test_lib", + ":val_test_lib", + "@googletest//:gtest", + "@googletest//:gtest_main", + "@spirv_headers//:spirv_common_headers", + "@spirv_headers//:spirv_cpp11_headers", + ], +) + +cc_test( name = "val_capability_test", size = "large", timeout = "long",
diff --git a/BUILD.gn b/BUILD.gn index 3997b6f..9726251 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -33,169 +33,68 @@ spirv_headers = spirv_tools_spirv_headers_dir spirv_is_winuwp = is_win && target_os == "winuwp" -template("spvtools_core_tables") { - assert(defined(invoker.version), "Need version in $target_name generation.") +action("spvtools_core_tables") { + script = "utils/ggt.py" - action("spvtools_core_tables_" + target_name) { - script = "utils/generate_grammar_tables.py" + core_tables_body_file = "${target_gen_dir}/core_tables_body.inc" + core_tables_header_file = "${target_gen_dir}/core_tables_header.inc" - version = invoker.version + # Extended instruction set grammar files + grammar_dir = "${spirv_headers}/include/spirv/unified1" + f0=rebase_path("${grammar_dir}/extinst.debuginfo.grammar.json", root_build_dir) + f1=rebase_path("${grammar_dir}/extinst.glsl.std.450.grammar.json", root_build_dir) + f2=rebase_path("${grammar_dir}/extinst.nonsemantic.clspvreflection.grammar.json", root_build_dir) + f3=rebase_path("${grammar_dir}/extinst.nonsemantic.shader.debuginfo.100.grammar.json", root_build_dir) + f4=rebase_path("${grammar_dir}/extinst.nonsemantic.vkspreflection.grammar.json", root_build_dir) + f5=rebase_path("${grammar_dir}/extinst.opencl.debuginfo.100.grammar.json", root_build_dir) + f6=rebase_path("${grammar_dir}/extinst.opencl.std.100.grammar.json", root_build_dir) + f7=rebase_path("${grammar_dir}/extinst.spv-amd-gcn-shader.grammar.json", root_build_dir) + f8=rebase_path("${grammar_dir}/extinst.spv-amd-shader-ballot.grammar.json", root_build_dir) + f9=rebase_path("${grammar_dir}/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json", root_build_dir) + f10=rebase_path("${grammar_dir}/extinst.spv-amd-shader-trinary-minmax.grammar.json", root_build_dir) - core_json_file = - "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" - core_insts_file = "${target_gen_dir}/core.insts-$version.inc" - operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc" - debuginfo_insts_file = - "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" - cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" - - sources = [ - cldebuginfo100_insts_file, - core_json_file, - debuginfo_insts_file, - ] - outputs = [ - core_insts_file, - operand_kinds_file, - ] - args = [ - "--spirv-core-grammar", - rebase_path(core_json_file, root_build_dir), - "--core-insts-output", - rebase_path(core_insts_file, root_build_dir), - "--extinst-debuginfo-grammar", - rebase_path(debuginfo_insts_file, root_build_dir), - "--extinst-cldebuginfo100-grammar", - rebase_path(cldebuginfo100_insts_file, root_build_dir), - "--operand-kinds-output", - rebase_path(operand_kinds_file, root_build_dir), - "--output-language", - "c++" - ] - } -} - -template("spvtools_core_enums") { - assert(defined(invoker.version), "Need version in $target_name generation.") - - action("spvtools_core_enums_" + target_name) { - script = "utils/generate_grammar_tables.py" - - version = invoker.version - - core_json_file = - "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" - debuginfo_insts_file = - "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" - cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" - - extension_enum_file = "${target_gen_dir}/extension_enum.inc" - extension_map_file = "${target_gen_dir}/enum_string_mapping.inc" - - args = [ - "--spirv-core-grammar", - rebase_path(core_json_file, root_build_dir), - "--extinst-debuginfo-grammar", - rebase_path(debuginfo_insts_file, root_build_dir), - "--extinst-cldebuginfo100-grammar", - rebase_path(cldebuginfo100_insts_file, root_build_dir), - "--extension-enum-output", - rebase_path(extension_enum_file, root_build_dir), - "--enum-string-mapping-output", - rebase_path(extension_map_file, root_build_dir), - "--output-language", - "c++" - ] - inputs = [ - core_json_file, - debuginfo_insts_file, - cldebuginfo100_insts_file, - ] - outputs = [ - extension_enum_file, - extension_map_file, - ] - } -} - -template("spvtools_glsl_tables") { - assert(defined(invoker.version), "Need version in $target_name generation.") - - action("spvtools_glsl_tables_" + target_name) { - script = "utils/generate_grammar_tables.py" - - version = invoker.version - - core_json_file = - "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" - glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json" - debuginfo_insts_file = - "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" - cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" - - glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc" - - args = [ - "--spirv-core-grammar", - rebase_path(core_json_file, root_build_dir), - "--extinst-debuginfo-grammar", - rebase_path(debuginfo_insts_file, root_build_dir), - "--extinst-cldebuginfo100-grammar", - rebase_path(cldebuginfo100_insts_file, root_build_dir), - "--extinst-glsl-grammar", - rebase_path(glsl_json_file, root_build_dir), - "--glsl-insts-output", - rebase_path(glsl_insts_file, root_build_dir), - "--output-language", - "c++" - ] - inputs = [ - core_json_file, - glsl_json_file, - debuginfo_insts_file, - cldebuginfo100_insts_file, - ] - outputs = [ glsl_insts_file ] - } -} - -template("spvtools_opencl_tables") { - assert(defined(invoker.version), "Need version in $target_name generation.") - - action("spvtools_opencl_tables_" + target_name) { - script = "utils/generate_grammar_tables.py" - - version = invoker.version - - core_json_file = - "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" - opencl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json" - debuginfo_insts_file = - "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" - cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" - - opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc" - - args = [ - "--spirv-core-grammar", - rebase_path(core_json_file, root_build_dir), - "--extinst-debuginfo-grammar", - rebase_path(debuginfo_insts_file, root_build_dir), - "--extinst-cldebuginfo100-grammar", - rebase_path(cldebuginfo100_insts_file, root_build_dir), - "--extinst-opencl-grammar", - rebase_path(opencl_json_file, root_build_dir), - "--opencl-insts-output", - rebase_path(opencl_insts_file, root_build_dir), - ] - inputs = [ - core_json_file, - opencl_json_file, - debuginfo_insts_file, - cldebuginfo100_insts_file, - ] - outputs = [ opencl_insts_file ] - } + sources = [ + "utils/Table/__init__.py", + "utils/Table/Context.py", + "utils/Table/IndexRange.py", + "utils/Table/Operand.py", + "utils/Table/StringList.py", + "${grammar_dir}/extinst.debuginfo.grammar.json", + "${grammar_dir}/extinst.glsl.std.450.grammar.json", + "${grammar_dir}/extinst.nonsemantic.clspvreflection.grammar.json", + "${grammar_dir}/extinst.nonsemantic.shader.debuginfo.100.grammar.json", + "${grammar_dir}/extinst.nonsemantic.vkspreflection.grammar.json", + "${grammar_dir}/extinst.opencl.debuginfo.100.grammar.json", + "${grammar_dir}/extinst.opencl.std.100.grammar.json", + "${grammar_dir}/extinst.spv-amd-gcn-shader.grammar.json", + "${grammar_dir}/extinst.spv-amd-shader-ballot.grammar.json", + "${grammar_dir}/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json", + "${grammar_dir}/extinst.spv-amd-shader-trinary-minmax.grammar.json", + "${grammar_dir}/spirv.core.grammar.json", + ] + outputs = [ + core_tables_body_file, + core_tables_header_file, + ] + args = [ + "--spirv-core-grammar", + rebase_path("${grammar_dir}/spirv.core.grammar.json", root_build_dir), + "--extinst=,${f0}", + "--extinst=,${f1}", + "--extinst=,${f2}", + "--extinst=SHDEBUG100_,${f3}", + "--extinst=,${f4}", + "--extinst=CLDEBUG100_,${f5}", + "--extinst=,${f6}", + "--extinst=,${f7}", + "--extinst=,${f8}", + "--extinst=,${f9}", + "--extinst=,${f10}", + "--core-tables-body-output", + rebase_path(core_tables_body_file, root_build_dir), + "--core-tables-header-output", + rebase_path(core_tables_header_file, root_build_dir) + ] } template("spvtools_language_header") { @@ -218,30 +117,6 @@ } } -template("spvtools_vendor_table") { - 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 = - "${spirv_headers}/include/spirv/unified1/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" @@ -283,18 +158,6 @@ } } -spvtools_core_tables("unified1") { - version = "unified1" -} -spvtools_core_enums("unified1") { - version = "unified1" -} -spvtools_glsl_tables("glsl1-0") { - version = "1.0" -} -spvtools_opencl_tables("opencl1-0") { - version = "1.0" -} spvtools_language_header("debuginfo") { name = "DebugInfo" grammar_file = @@ -304,55 +167,9 @@ name = "OpenCLDebugInfo100" grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" } -spvtools_language_header("vkdebuginfo100") { - name = "NonSemanticShaderDebugInfo100" - grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json" -} - -spvtools_vendor_tables = [ - [ - "spv-amd-shader-explicit-vertex-parameter", - "...nil...", - ], - [ - "spv-amd-shader-trinary-minmax", - "...nil...", - ], - [ - "spv-amd-gcn-shader", - "...nil...", - ], - [ - "spv-amd-shader-ballot", - "...nil...", - ], - [ - "debuginfo", - "...nil...", - ], - [ - "opencl.debuginfo.100", - "CLDEBUG100_", - ], - [ - "nonsemantic.clspvreflection", - "...nil...", - ], - [ - "nonsemantic.vkspreflection", - "...nil...", - ], - [ - "nonsemantic.shader.debuginfo.100", - "SHDEBUG100_", - ], -] - -foreach(table_def, spvtools_vendor_tables) { - spvtools_vendor_table(table_def[0]) { - name = table_def[0] - operand_kind_prefix = table_def[1] - } +spvtools_language_header("vkdebuginfo") { + name = "NonSemanticShaderDebugInfo" + grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.grammar.json" } config("spvtools_public_config") { @@ -419,24 +236,18 @@ public_deps = [ ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", - ":spvtools_language_header_vkdebuginfo100", + ":spvtools_language_header_vkdebuginfo", ] } static_library("spvtools") { deps = [ - ":spvtools_core_tables_unified1", + ":spvtools_core_tables", ":spvtools_generators_inc", - ":spvtools_glsl_tables_glsl1-0", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", - ":spvtools_language_header_vkdebuginfo100", - ":spvtools_opencl_tables_opencl1-0", + ":spvtools_language_header_vkdebuginfo", ] - foreach(table_def, spvtools_vendor_tables) { - _target_def_name = table_def[0] - deps += [ ":spvtools_vendor_tables_${_target_def_name}" ] - } sources = [ "source/assembly_grammar.cpp", @@ -450,8 +261,6 @@ "source/disassemble.cpp", "source/disassemble.h", "source/enum_set.h", - "source/enum_string_mapping.cpp", - "source/enum_string_mapping.h", "source/ext_inst.cpp", "source/ext_inst.h", "source/extensions.cpp", @@ -488,6 +297,8 @@ "source/spirv_validator_options.h", "source/table.cpp", "source/table.h", + "source/table2.cpp", + "source/table2.h", "source/text.cpp", "source/text.h", "source/text_handler.cpp", @@ -501,10 +312,13 @@ "source/util/hex_float.h", "source/util/ilist.h", "source/util/ilist_node.h", + "source/util/index_range.h", "source/util/make_unique.h", "source/util/parse_number.cpp", "source/util/parse_number.h", "source/util/small_vector.h", + "source/util/span.h", + "source/util/status.h", "source/util/string_utils.cpp", "source/util/string_utils.h", "source/util/timer.cpp", @@ -512,7 +326,6 @@ ] public_deps = [ - ":spvtools_core_enums_unified1", ":spvtools_headers", "${spirv_headers}:spv_headers", ] @@ -551,15 +364,19 @@ "source/val/validate_debug.cpp", "source/val/validate_decorations.cpp", "source/val/validate_derivatives.cpp", + "source/val/validate_dot_product.cpp", "source/val/validate_execution_limitations.cpp", "source/val/validate_extensions.cpp", "source/val/validate_function.cpp", + "source/val/validate_graph.cpp", + "source/val/validate_group.cpp", "source/val/validate_id.cpp", "source/val/validate_image.cpp", "source/val/validate_instruction.cpp", "source/val/validate_interfaces.cpp", "source/val/validate_layout.cpp", "source/val/validate_literals.cpp", + "source/val/validate_logical_pointers.cpp", "source/val/validate_logicals.cpp", "source/val/validate_memory.cpp", "source/val/validate_memory_semantics.cpp", @@ -568,6 +385,7 @@ "source/val/validate_misc.cpp", "source/val/validate_mode_setting.cpp", "source/val/validate_non_uniform.cpp", + "source/val/validate_pipe.cpp", "source/val/validate_primitives.cpp", "source/val/validate_ray_query.cpp", "source/val/validate_ray_tracing.cpp", @@ -575,8 +393,10 @@ "source/val/validate_scopes.cpp", "source/val/validate_scopes.h", "source/val/validate_small_type_uses.cpp", + "source/val/validate_tensor.cpp", "source/val/validate_tensor_layout.cpp", "source/val/validate_type.cpp", + "source/val/validate_invalid_type.cpp", "source/val/validation_state.cpp", "source/val/validation_state.h", ] @@ -585,7 +405,7 @@ ":spvtools", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", - ":spvtools_language_header_vkdebuginfo100", + ":spvtools_language_header_vkdebuginfo", ] public_deps = [ ":spvtools_headers" ] @@ -691,6 +511,8 @@ "source/opt/freeze_spec_constant_value_pass.h", "source/opt/function.cpp", "source/opt/function.h", + "source/opt/graph.cpp", + "source/opt/graph.h", "source/opt/graphics_robust_access_pass.cpp", "source/opt/graphics_robust_access_pass.h", "source/opt/if_conversion.cpp", @@ -717,6 +539,8 @@ "source/opt/ir_loader.cpp", "source/opt/ir_loader.h", "source/opt/iterator.h", + "source/opt/legalize_multidim_array_pass.cpp", + "source/opt/legalize_multidim_array_pass.h", "source/opt/licm_pass.cpp", "source/opt/licm_pass.h", "source/opt/liveness.cpp", @@ -785,10 +609,14 @@ "source/opt/remove_duplicates_pass.h", "source/opt/remove_unused_interface_variables_pass.cpp", "source/opt/remove_unused_interface_variables_pass.h", + "source/opt/canonicalize_ids_pass.h", + "source/opt/canonicalize_ids_pass.cpp", "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/resolve_binding_conflicts_pass.cpp", + "source/opt/resolve_binding_conflicts_pass.h", "source/opt/scalar_analysis.cpp", "source/opt/scalar_analysis.h", "source/opt/scalar_analysis_nodes.h", @@ -799,6 +627,8 @@ "source/opt/set_spec_constant_default_value_pass.h", "source/opt/simplification_pass.cpp", "source/opt/simplification_pass.h", + "source/opt/split_combined_image_sampler_pass.cpp", + "source/opt/split_combined_image_sampler_pass.h", "source/opt/spread_volatile_semantics.cpp", "source/opt/spread_volatile_semantics.h", "source/opt/ssa_rewrite_pass.cpp", @@ -839,12 +669,11 @@ deps = [ ":spvtools", ":spvtools_language_header_debuginfo", - ":spvtools_vendor_tables_spv-amd-shader-ballot", ] public_deps = [ ":spvtools_headers", ":spvtools_language_header_cldebuginfo100", - ":spvtools_language_header_vkdebuginfo100", + ":spvtools_language_header_vkdebuginfo", ] if (build_with_chromium) { @@ -855,7 +684,11 @@ } static_library("spvtools_link") { - sources = [ "source/link/linker.cpp" ] + sources = [ + "source/link/linker.cpp", + "source/link/fnvar.cpp", + "source/link/fnvar.h" + ] deps = [ ":spvtools", ":spvtools_opt", @@ -1400,6 +1233,7 @@ "test/libspirv_macros_test.cpp", "test/name_mapper_test.cpp", "test/named_id_test.cpp", + "test/op_unknown_test.cpp", "test/opcode_make_test.cpp", "test/opcode_require_capabilities_test.cpp", "test/opcode_split_test.cpp", @@ -1442,7 +1276,7 @@ ":spvtools", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", - ":spvtools_language_header_vkdebuginfo100", + ":spvtools_language_header_vkdebuginfo", ":spvtools_tools_io", ":spvtools_val", "//testing/gmock",
diff --git a/CHANGES b/CHANGES index 007e901..80bf112 100644 --- a/CHANGES +++ b/CHANGES
@@ -1,5 +1,266 @@ Revision history for SPIRV-Tools +v2026.2 2026-04-24 + - General + - Fix build for NonSemanticShaderDebugInfo in GN (#6647) + - Support SPV_KHR_abort and SPV_KHR_constant_data (#6625) + - Support versioned NonSemanticShaderDebugInfo imports (#6555) + - Fix C++20 compilation issue with large integer literals (#6557) + - Remove SVA (Shader Validation Assistant) (#6550) + - Improved Kokoro build configuration and storage usage (#6573, #6551) + - Various dependency rolls and CI updates + - Optimizer + - Fold OpCompositeExtract feeding from OpCopyLogical or OpLoad (#6614) + - Fix DebugValue placements and initialization (#6599, #6626) + - Support OpConstantCompositeReplicateEXT in constant manager (#6616) + - Aggressive DCE support for untyped pointers and OpUntypedVariableKHR (#6602, #6607) + - Optimize SPV_EXT_opacity_micromap and SPV_EXT_shader_invocation_reorder (#6571) + - Add LegalizeMultidimArrayPass and improve access chain reordering (#6535, #6545) + - Added various folding and reassociation rules (#6453, #6504, #6454, #6456) + - Support for optimizing SPV_NV_push_constant_bank (#6547) + - Validator + - Add validation for 4-bit integer types (#6644) + - Support and validate SPV_KHR_constant_data (OpConstantDataKHR, OpSpecConstantDataKHR) (#6646, #6643, #6642) + - Validate NonSemanticShaderDebugInfo 101 and versioned imports (#6635, #6555) + - Improve validation for Intel SPIR-V extensions and various KHR/EXT extensions (#6232, #6589, #6583, etc.) + - Extensive validation coverage for OpSpecConstantOp (#6596, #6585, #6582, #6579, #6578, #6576) + - Check output location uniqueness for GeometryStreams (#6630) + - Improved validation for variables, constants, and types (#6634, #6598, #6595, #6568) + - Added validation for several new opcodes and capabilities (OpGroupAsyncCopy, OpLifetimeStart/Stop, etc.) (#6519, #6514, #6515) + - Assembler/Disassembler + - Add --handle-unknown-opcodes flag to spirv-dis (#6604) + - Handle leading + in hex float literals in assembler (#6565) + - Validate bit width of float types with explicit encodings in assembler (#6562) + +v2026.1 2026-01-22 + - General + - spirv-tools: Fix infinite recursion in SmallVector::operator== on MSVC (#6470) + - Add support for SPV_NV_push_constant_bank (#6507) + - Enable UseMultiToolTask for msbuild performance (#6472) + - Fix asan error in stack usage (#6434) + - Fix crash in array length validation (#6500) + - Fix infinite loop in logical pointer validation (#6450) + - Implement SPV_EXT_descriptor_heap (#6503) + - Optimizer + - Adding constant folding for NMin, NMax and NClamp (#6433) + - Also remap DebugScope instructions (#6501) + - Change ValueNumber to use spivOpcodeIsCommutativeBinaryOperator (#6431) + - Extending factoring folding rules to support subtraction (#6432) + - Extending negation folding rules (#6444) + - Folding Rules For Common Comparisons (#6437) + - Folding rules for negation and cancellation (#6452) + - Harden CFGCleanupPass against ID overflow (#6439) + - Harden CombineAccessChainsPass against ID overflow (#6440) + - Harden CopyPropagateArraysPass against ID overflow (#6441) + - Remove lingering debug-scopes. (#6416) + - add adce test for debug declare (#6462) + - fix adce when ns debug info present (#6217) (#6420) + - Validator + - Handle OpTypeVectorIdEXT in validate_interfaces.cpp (#6492) + - Validate SPV_EXT_long_vector (#6471) + - Validate floats/int Image Operands are 32-bit (#6477) + - Add Mesh Shading Read Output check (#6426) + - Add Mesh/Task to check for LocalSize (#6459) + - Add missing LOD non-zero MS (#6496) + - Add width to IsFloatScalarType (#6489) + - Better atomic storage class message (#6425) + - Better error message when types are the same (#6427) + - Check Mesh Topology builtin by entry point (#6457) + - Check for CoopMat scope (#6442) + - Combine Image Coordinate checks (#6494) + - Fix crash with bad OpUntypedArrayLengthKHR (#6493) + - Label VUID 10823 (#6488) + - Provide RuntimeDescriptorArray hint in message (#6448) + - Use new StorageClassToString util (#6460) + - Use validation_state.cpp helpers (#6478) + - Validate ImageQuerySizeLod is 32-bit (#6479) + - Validate Mesh Shader output are an array (#6449) + - add missing relational ops to InvalidTypePass (#6495) + - Diff + - diff: Match OpExtInst in debug and types/values sections (#6430) + +v2025.5 2025-11-18 + - General + - Validator + - Optimizer + - spirv-opt: Add a folding rule for `OpBitReverse` and fix `OpBitCast` to support lower to higher bit conversions. (#6321) + - spirv-opt: Add basic support for graphs (#6351) + - spirv-opt: Adding folding rules for bitwise and (#6361) + - spirv-opt: Adding normal form to ValueNumber calculation (#6371) + - spirv-opt: Cache IsReadOnlyLoad in ValueNumberTable (#6385) + - spirv-opt: Clone decorations when unrolling loops (#6373) + - spirv-opt: Correct ADCE bug keeping dead DebugValue Value operands (#6368) + - spirv-opt: Fix constant folding for `OpBitcast` on signed integers. (#6378) + - spirv-opt: Fix id overflow in ConvertToSampledImage (#6339) + - spirv-opt: Handle ID overflow in AggressiveDCEPass (#6366) + - spirv-opt: Handle ID overflow in SplitCombinedImageSamplerPass (#6406) + - spirv-opt: Handle id overflow in CCP and constant folding (#6367) + - spirv-opt: Handle id overflow in MergeReturnPass (#6340) + - spirv-opt: Handle id overflow in amd_ext_to_khr (#6403) + - Add `SPV_KHR_maximal_reconvergence` to the allowlist (#6372) + - Adding folding rules for commutive bitwise operations (xor/or/and). (#6358) + - [OPT] Move definition of Pass::Status to Utils (#6402) + - Validator: + - spirv-val: Add LocalInvocationIndex checks (#6395) + - spirv-val: Add validation for SPV_KHR_fma (#6388) + - spirv-val: Break up the 3000 line ValidateExtInst function (#6364) + - spirv-val: Fix VUID churn for 10685 (#6389) + - spirv-val: Improve Explicit Layout message (#6331) + - spirv-val: Use spvOpcodeString consistently (#6393) + - Relax validation rule for DebugGlobalVariable variable (#6382) + - Validate SPV_EXT_shader_64bit_indexing (#6376) + - Validate logical pointer restrictions (#6240) + - [tools/val] fix u8str for real this time (#6341) + - Add VK_EXT_shader_uniform_buffer_unsized_array (#6386) + - Assembler + - Add as/dis support for Arm.MotionEngine.100 extended instruction set (#6284) + - spirv-as: fix command line for stdin as input (#6398) + +v2025.4 2025-09-22 + - General + - Add support for SPV_INTEL_function_variants (#6211, #6195) + - Add support for SPV_ARM_graph (#6177) + - Add support for SPV_INTEL_inline_assembly (#6210) + - Add support for SPV_QCOM_cooperative_matrix_conversion (#6252) + - Add BFloat16 encoding support (#6222) + - Allow mimalloc in static builds (#6267) + - Allow processing of directories in spirv-val (#6292) + - Optimizer + - Handle ID overflow in many passes (#6310, #6325, #6302, #6309, #6306, #6305, #6304, #6299, #6301, #6312) + - Add support for OpTypeGraphARM and tensors to type manager (#6247, #6202) + - Add Geometry capability to the trim pass (#6278) + - Support CCP for 16bit integer min and max operations. (#6280, #6287) + - Fix ADCE DebugDeclare DebugValue treatment (#6179) + - Fix lerp and fmix folding with half types (#6293, #6297) + - Fix trimming pass with OpNop (#6314) + - Support OpSpecConstantCompositeReplicateEXT in FoldSpecConstantOpAndCompositePass (#6315) + - Fix OpUndef placement in ADCE (#6154) + - Add null checks for MakeSClampInst and GetValueForType (#6324) + - Fix crash if shader uses linkage decoration (#6191) + - Validator + - Update memory semantics rules to match the specification (#6096) + - Validate PhysicalStorageBuffer alignment (#6266) + - Validate DescriptorSet and Binding decorations for all relevant variables (#6242) + - Validate that OpDecorateId IDs are well-ordered (#6227) + - Add validation for SPV_QCOM_image_processing (#6268) + - Add validation for Patch Decoration (#6219) + - Add validation for PrimitiveID (#6209) + - Add Workgroup Size check for Compute Derivatives (#6294) + - Allow compare-exchange semantics equal=Release and unequal=Acquire (#6286) + - Accept graphs with no inputs and/or no body (#6291) + - Fix GetLargestScalarType with untyped PSB (#6327) + - Fix Mesh Builtin checking for signed int and multi-entrypoint shaders (#6238, #6313) + - Fix OpTensor{Read,Write}ARM for tensors with a spec constant rank (#6206) + - Accept NonReadable/NonWritable on tensor variables in UniformConstant (#6184) + - Fix location of DebugFunctionDefinition instruction (#6198) + - Print SPIR-V version info in error message (#6283) + - Assembler + - Fix docs: need '-' arg to specify stdin (#6194) + - Disassembler + - Fix missing color on result ID (#6272) + - Diff + - Add spirv-diff to list of install targets (#6258) + +v2025.3 2025-06-23 + - General + - Add mimalloc to improve multithreaded performance (#6188) + - python: Use type annotations compatible with python 3.8 (#6119) + - fix clang-20 build issue (#6103) + - Optimizer + - Add --canonicalize-ids pass (#6174) + - Keep instructions used by the DebugBuildIdentifier (#6189) + - opt: Pass DebugDeclare scope to DebugValue (#6178) + - Add SPV_NV_linear_swept_spheres to allow list. (#6168) + - Add initial support for SPV_EXT_float8 (#6170) + - Keep DebugBuildIdentifier during dce (#6166) + - Add SPV_NV_cluster_acceleration_structure to allow lists (#6163) + - [OPT] prevent private_to_local_pass optimizing double pointer (#6161) + - [OPT] Use conservative default case for `GetPtr` (#6158) + - [OPT] Set the BB for the debug instruction when moved. (#6153) + - Add support for SPV_ARM_tensors (#6134) + - [opt] Move debug instruction when neccessary in copy prop arrays. (#6142) + - [OPT] Remove recursion from redundancy_elimination (#6141) + - opt: add SPV_NV_shader_invocation_reorder to allowlist (#6122) + - Validator + - spirv-val: tidy up validation of type constraints for IDs (#6185) + - Add validation support for MeshEXT based on the spec update PR https://github.com/KhronosGroup/Vulkan-Docs/pull/2475 (#6171) + - spirv-val: Label maintenance9 new VUID (#6176) + - spirv-val: add positive test for FP8 cooperative matrices (#6175) + - spirv-val: Give hints when user is forgetting feature bit (#6164) + - val: Fix CullPrimitiveEXT array of bool (#6155) + - [spirv-val] Add the validation checks for SPV_QCOM_tile_shading (#6130) + - spirv-val: allow Float16 in OpenCL environments (#6110) + - spirv-val: Update 1.4.312 VUID churn (#6082) + - Assembler + - Add minimal as/dis support for TOSA.001000.1 extended instruction set (#6183) + - Disassembler + - spirv-dis: Set a hard limit on last_instruction_comment_alignment_ (#6149) + +v2025.2 2025-04-22 + - General + - Add SPV_KHR_bfloat16 support (#6057) + - Optimizer + - Fold bitwise operator and arithmetic with 0 (#6013) + - Support scalar replacement of large structs (#6019) + - Support optimization of OpCopyLogical (#6016) + - add pass to split combined image samplers (#6035) + - value numbering: preserve loads of image, sampler, sampled image (#6059) + - Delete decoration for OpPhi when unrolling (#6064) + - Add QuadControlKHR to trim pass and allow lists (#6068) + - In copy propagate arrays, debug instructions are not stores. (#6078) + - Minimal opt support for SPV_KHR_untyped_pointers (#6087) + - Validator + - Add validation for invalid layout decoration usage (#6012, #6020) + - Add Vulkan Aligned PowerOfTwo check (#6027) + - Validate PhysicalStorageBuffer Stage Interface (#6000) + - Update location/component conflict validation (#5993) + - add resolve-binding-conflicts pass (#6044) + - Check that layouts match runtime array requirement (#6048) + - Validation for relaxed control barrier with storage class semantics (#5984) + - Validate version requirement for Vulkan Memory Model (#6042) + - Add support for pointer types in vector when using extension SPV_INTEL_masked_gather_scatter (#6041) + - Restrict VUID 09557 to Vulkan environments (#6080) + - Add Vulkan 1.3 and 1.4 capability checks (#6063) + - Assembler + - Add OpUnknown pseudo-instruction (#6024) + - Diff + - Try to pair functions by their complete type. (#6021) + +v2025.1 2025-02-28 + - General + - diff: Fix crash in OpString matching (#5988) + - Add SPV_AMDX_shader_enqueue version 2 support (#5838) + - add support for SPV_INTEL_subgroup_matrix_multiply_accumulate (#5928) + - update cmake_minimum_required to 3.22.1 (#5925) + - Add OpImageSampleFootprintNV to IsAllowedSampledImageOperand (#5914) + - assembler: ensure progress when seeking the version string (#5910) + - Optimizer + - opt: keep all OpSource instructions (#5901) + - [opt] Fix bug opt::InstructionBuilder::AddVariable (#6007) + - [OPT] Add SPV_KHR_ray_tracing to allow list (#5941) + - opt: keep all OpSource instructions (#5901) + - Validator + - spirv-val: Add AllowVulkan32BitBitwise option (#6001) + - Fix untyped pointer comparison validation (#6004) + - spirv-val: Update VUIDs for 308 header (#5990) + - spirv-val: fix env parsing for vk1.1spv1.4 (#5985) + - Add validation for SPV_NV_linear_swept_spheres. (#5975) + - Add validation SPV_NV_cluster_acceleration_structure. (#5974) + - Improve the instruction diagnostic for some access chain errors (#5978) + - Update physical storage buffer validation to match SPIR-V 1.6.5 (#5977) + - Validate SPV_NV_cooperative_vector (#5972) + - Fix layout checks with untyped pointers (#5970) + - spirv-val: Update mislabeled VUIDs (#5964) + - More explicit layout validation (#5958) + - spirv-val: Add VK_KHR_maintenance8 support (#5951) + - Add EXT_mesh_shader validation support (#5640) + - spirv-val: Remove OpenCL ivec3 req (#5940) + - spirv-val: Validate zero product workgroup size (#5407) + - Relax DebugLine validation (#5916) + - Linker + - linker: remove LinkOnceODR decorations when linking executables (#5979) + - fix: handle LinkOnceODR correctly (#5938) + v2024.4 2024-12-04 - General - Add FPEncoding operand type. (#5726)
diff --git a/CMakeLists.txt b/CMakeLists.txt index e50fb56..b69ec9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.17.2) +cmake_minimum_required(VERSION 3.22.1) project(spirv-tools) @@ -112,13 +112,19 @@ set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror) endif() elseif(MSVC) - set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819) + set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819 /wd4251 /W2 /WX) if(${SPIRV_WERROR}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX) endif() endif() +if (PROJECT_IS_TOP_LEVEL) + # enable parallel builds for msbuild + list(APPEND CMAKE_VS_GLOBALS UseMultiToolTask=true) + list(APPEND CMAKE_VS_GLOBALS EnforceProcessCountAcrossBuilds=true) +endif() + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON) @@ -240,6 +246,20 @@ # Tests require Python3 find_host_package(Python3 REQUIRED) +# Check type annotations in Perl code. Assumes mypy. +add_custom_target(spirv-tools-check-python-types + COMMAND mypy Table + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils +) +set_target_properties(spirv-tools-check-python-types + PROPERTIES EXCLUDE_FROM_ALL ON) + +# Run Python unit tests +add_custom_target(spirv-tools-check-python-tests + COMMAND Python3::Interpreter -m unittest discover -v -p "*test.py" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils +) + # Check for symbol exports on Linux. # At the moment, this check will fail on the OSX build machines for the Android NDK. # It appears they don't have objdump.
diff --git a/DEPS b/DEPS index a119d1a..02212bc 100644 --- a/DEPS +++ b/DEPS
@@ -3,18 +3,20 @@ vars = { 'github': 'https://github.com', - 'abseil_revision': '7316f5616bad0a794b2a75901cc20b0099718085', + 'abseil_revision': '351086314d46e73d430296c9eca1f6d6c0372cd1', - 'effcee_revision': '2c97e5689ed8d7ab6ae5820f884f03a601ae124b', + 'effcee_revision': 'ae38e040cbb7e83efa8bfbb4967e5b8c8c89b55a', - 'googletest_revision': '35d0c365609296fa4730d62057c487e3cfa030ff', + 'googletest_revision': 'd72f9c8aea6817cdf1ca0ac10887f328de7f3da2', # Use protobufs before they gained the dependency on abseil 'protobuf_revision': 'v21.12', - 're2_revision': '6dcd83d60f7944926bfd308cc13979fc53dd69ca', + 're2_revision': '972a15cedd008d846f1a39b2e88ce48d7f166cbd', - 'spirv_headers_revision': '3f17b2af6784bfa2c5aa5dbb8e0e74a607dd8b3b', + 'spirv_headers_revision': 'ad9184e76a66b1001c29db9b0a3e87f646c64de0', + + 'mimalloc_revision': 'b1963961a5cdb1996c9ad2e356014089b7a94ea3', } deps = { @@ -36,5 +38,8 @@ 'external/spirv-headers': Var('github') + '/KhronosGroup/SPIRV-Headers.git@' + Var('spirv_headers_revision'), + + 'external/mimalloc': + Var('github') + '/microsoft/mimalloc.git@' + Var('mimalloc_revision'), }
diff --git a/MODULE.bazel b/MODULE.bazel index 2e28e9e..1641063 100644 --- a/MODULE.bazel +++ b/MODULE.bazel
@@ -19,7 +19,7 @@ ) bazel_dep(name = "rules_python", - version = "0.34.0") + version = "1.5.1") # https://rules-python.readthedocs.io/en/stable/toolchains.html#library-modules-with-dev-only-python-usage python = use_extension( @@ -31,3 +31,5 @@ python.toolchain(python_version = "3.12", is_default = True, ignore_root_user_error = True) + +bazel_dep(name = "rules_cc", version = "0.1.1")
diff --git a/README.md b/README.md index d1ce2fb..fdc6f1d 100644 --- a/README.md +++ b/README.md
@@ -33,7 +33,7 @@ <img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[](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"/>[](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"/>[](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2019_release.html) +<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_release.html) [More downloads](docs/downloads.md) @@ -146,6 +146,7 @@ decorations. * Normalization * Compact IDs + * Canonicalize IDs * CFG cleanup * Flatten decorations * Merge returns @@ -300,6 +301,7 @@ git clone https://github.com/google/effcee.git spirv-tools/external/effcee git clone https://github.com/google/re2.git spirv-tools/external/re2 git clone https://github.com/abseil/abseil-cpp.git spirv-tools/external/abseil_cpp + git clone https://github.com/microsoft/mimalloc.git spirv-tools/external/mimalloc #### Dependency on Effcee @@ -312,6 +314,26 @@ RE2 sources to appear in `external/re2`, and Abseil sources to appear in `external/abseil_cpp`. +#### Dependency on mimalloc + +SPIRV-Tools may be configured to use the [mimalloc][mimalloc] library to improve memory +allocation performance. + +In the CMake build, usage of mimalloc is controlled by the `SPIRV_TOOLS_USE_MIMALLOC` +option. This variable defaults on `ON` when building for Windows and `OFF` when building +for other platforms. Enabling this option on non-Windows platforms is supported and is +expected to work normally, but this has not been tested as thoroughly and extensively as +the Windows version. In the future, the `SPIRV_TOOLS_USE_MIMALLOC` option may default to +`ON` for non-Windows platforms as well. + +In order to avoid unexpectedly changing allocation behavior of applications that link +SPIRV-Tools libraries statically, mimalloc is disabled by default on static libraries. +The option 'SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD' can be used to force the usage of +mimalloc on static libraries. + +*Note*: mimalloc is currently only supported when building with CMake. When using Bazel, +mimalloc is not used. + ### Source code organization * `example`: demo code of using SPIRV-Tools APIs @@ -325,6 +347,7 @@ * `external/abseil_cpp`: Location of [Abseil][abseil-cpp] sources, if Abseil is not already configured by an enclosing project. (The RE2 project already requires Abseil.) +* `external/mimalloc`: Intended location for [mimalloc][mimalloc] sources, not provided * `include/`: API clients should add this directory to the include search path * `external/spirv-headers`: Intended location for [SPIR-V headers][spirv-headers], not provided @@ -432,7 +455,7 @@ - [Python 3](http://www.python.org/): for utility scripts and running the test suite. - [Bazel](https://bazel.build/) (optional): if building the source with Bazel, -you need to install Bazel Version 7.0.2 on your machine. Other versions may +you need to install Bazel Version 7.4.0 on your machine. Other versions may also work, but are not verified. - [Emscripten SDK](https://emscripten.org) (optional): if building the WebAssembly module. @@ -440,20 +463,16 @@ SPIRV-Tools is regularly tested with the following compilers: On Linux -- GCC version 9.3 -- Clang version 10.0 +- GCC version 13 +- Clang version 18 On MacOS -- AppleClang 11.0 +- AppleClang 15.0 On Windows -- Visual Studio 2017 -- Visual Studio 2019 - Visual Studio 2022 -Note: Visual Studio 2017 has incomplete c++17 support. We might stop -testing it soon. Other compilers or later versions may work, but they are not -tested. +Note: Other compilers or later versions may work, but they are not tested. ### CMake options @@ -804,6 +823,7 @@ [effcee]: https://github.com/google/effcee [re2]: https://github.com/google/re2 [abseil-cpp]: https://github.com/abseil/abseil-cpp +[mimalloc]: https://github.com/microsoft/mimalloc [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
diff --git a/build_defs.bzl b/build_defs.bzl index 76bf3e7..c18768f 100644 --- a/build_defs.bzl +++ b/build_defs.bzl
@@ -38,6 +38,8 @@ "-Wno-self-assign", "-Wno-shadow", "-Wno-unused-parameter", + # Work around looseness in protobuf parse table generated code + "-Wno-implicit-int-conversion", ], }) @@ -47,6 +49,7 @@ for constraint in incompatible_constraints }])) +SPIRV_CORE_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_core_grammar_unified1" DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1" CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1" SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1" @@ -57,161 +60,84 @@ merged.update(d) return merged -def generate_core_tables(version): - if not version: - fail("Must specify version", "version") +def ExtInst(name, target = "", prefix = ""): + """ + Returns a dictionary specifying the info needed to + process an extended instruction set. + Args: + name: The extension name; forms part of the .json grammar file. + target: if non-empty, the name of the bazel target in spirv-headers + that names the JSON grammar file for the extended instrution set. + If empty, the target name is derived from 'name'. + prefix: The optional prefix for names of operand enums. + + Returns a dictionary with keys 'name', 'target', 'prefix' and the + corresponding values. + """ + return {"name": name, "target": target, "prefix": prefix} + +def _extinst_grammar_target(e): + """ + Args: e, as returned from extinst + Returns the SPIRV-Headers target for the given extended instruction set spec. + """ + target = e["target"] + name = e["name"] + if len(target) > 0: + return "@spirv_headers//:{}".format(target) + name_part = name.replace("-", "_").replace(".", "_") + return "@spirv_headers//:spirv_ext_inst_{}_grammar_unified1".format(name_part) + +def create_grammar_tables_target(name, extinsts): + """ + Creates a ":gen_compressed_tables" target for SPIR-V instruction + set grammar tables. + + Args: + name: unused. Required by convention. + extinsts: list of extended instruction specs. + Each spec is a dictionary, as returned from 'extinst'. + """ grammars = dict( - core_grammar = "@spirv_headers//:spirv_core_grammar_{}".format(version), - debuginfo_grammar = DEBUGINFO_GRAMMAR_JSON_FILE, - cldebuginfo_grammar = CLDEBUGINFO100_GRAMMAR_JSON_FILE, + core_grammar = SPIRV_CORE_GRAMMAR_JSON_FILE, ) - outs = dict( - core_insts_output = "core.insts-{}.inc".format(version), - operand_kinds_output = "operand.kinds-{}.inc".format(version), + core_tables_header_output = "core_tables_header.inc", + core_tables_body_output = "core_tables_body.inc", ) + extinst_args = [] + for e in extinsts: + extinst_args.append("--extinst={},$(location {})".format(e["prefix"], _extinst_grammar_target(e))) cmd = ( - "$(location :generate_grammar_tables)" + + "$(location :ggt)" + " --spirv-core-grammar=$(location {core_grammar})" + - " --extinst-debuginfo-grammar=$(location {debuginfo_grammar})" + - " --extinst-cldebuginfo100-grammar=$(location {cldebuginfo_grammar})" + - " --core-insts-output=$(location {core_insts_output})" + - " --operand-kinds-output=$(location {operand_kinds_output})" + - " --output-language=c++" + " --core-tables-body-output=$(location {core_tables_body_output})" + + " --core-tables-header-output=$(location {core_tables_header_output})" + + " " + " ".join(extinst_args) ).format(**_merge_dicts([grammars, outs])) native.genrule( - name = "gen_core_tables_" + version, - srcs = grammars.values(), + name = "gen_compressed_tables", + srcs = grammars.values() + [_extinst_grammar_target(e) for e in extinsts], outs = outs.values(), cmd = cmd, cmd_bat = cmd, - tools = [":generate_grammar_tables"], - visibility = ["//visibility:private"], - ) - -def generate_enum_string_mapping(version): - if not version: - fail("Must specify version", "version") - - grammars = dict( - core_grammar = "@spirv_headers//:spirv_core_grammar_{}".format(version), - debuginfo_grammar = DEBUGINFO_GRAMMAR_JSON_FILE, - cldebuginfo_grammar = CLDEBUGINFO100_GRAMMAR_JSON_FILE, - ) - - outs = dict( - extension_enum_ouput = "extension_enum.inc", - enum_string_mapping_output = "enum_string_mapping.inc", - ) - - cmd = ( - "$(location :generate_grammar_tables)" + - " --spirv-core-grammar=$(location {core_grammar})" + - " --extinst-debuginfo-grammar=$(location {debuginfo_grammar})" + - " --extinst-cldebuginfo100-grammar=$(location {cldebuginfo_grammar})" + - " --extension-enum-output=$(location {extension_enum_ouput})" + - " --enum-string-mapping-output=$(location {enum_string_mapping_output})" + - " --output-language=c++" - ).format(**_merge_dicts([grammars, outs])) - - native.genrule( - name = "gen_enum_string_mapping", - srcs = grammars.values(), - outs = outs.values(), - cmd = cmd, - cmd_bat = cmd, - tools = [":generate_grammar_tables"], - visibility = ["//visibility:private"], - ) - -def generate_opencl_tables(version): - if not version: - fail("Must specify version", "version") - - grammars = dict( - opencl_grammar = "@spirv_headers//:spirv_opencl_grammar_{}".format(version), - ) - - outs = dict( - opencl_insts_output = "opencl.std.insts.inc", - ) - - cmd = ( - "$(location :generate_grammar_tables)" + - " --extinst-opencl-grammar=$(location {opencl_grammar})" + - " --opencl-insts-output=$(location {opencl_insts_output})" - ).format(**_merge_dicts([grammars, outs])) - - native.genrule( - name = "gen_opencl_tables_" + version, - srcs = grammars.values(), - outs = outs.values(), - cmd = cmd, - cmd_bat = cmd, - tools = [":generate_grammar_tables"], - visibility = ["//visibility:private"], - ) - -def generate_glsl_tables(version): - if not version: - fail("Must specify version", "version") - - grammars = dict( - gsls_grammar = "@spirv_headers//:spirv_glsl_grammar_{}".format(version), - ) - outs = dict( - gsls_insts_outs = "glsl.std.450.insts.inc", - ) - - cmd = ( - "$(location :generate_grammar_tables)" + - " --extinst-glsl-grammar=$(location {gsls_grammar})" + - " --glsl-insts-output=$(location {gsls_insts_outs})" + - " --output-language=c++" - ).format(**_merge_dicts([grammars, outs])) - - native.genrule( - name = "gen_glsl_tables_" + version, - srcs = grammars.values(), - outs = outs.values(), - cmd = cmd, - cmd_bat = cmd, - tools = [":generate_grammar_tables"], - visibility = ["//visibility:private"], - ) - -def generate_vendor_tables(extension, operand_kind_prefix = ""): - if not extension: - fail("Must specify extension", "extension") - - extension_rule = extension.replace("-", "_").replace(".", "_") - grammars = dict( - vendor_grammar = "@spirv_headers//:spirv_ext_inst_{}_grammar_unified1".format(extension_rule), - ) - outs = dict( - vendor_insts_output = "{}.insts.inc".format(extension), - ) - cmd = ( - "$(location :generate_grammar_tables)" + - " --extinst-vendor-grammar=$(location {vendor_grammar})" + - " --vendor-insts-output=$(location {vendor_insts_output})" + - " --vendor-operand-kind-prefix={operand_kind_prefix}" - ).format(operand_kind_prefix = operand_kind_prefix, **_merge_dicts([grammars, outs])) - - native.genrule( - name = "gen_vendor_tables_" + extension_rule, - srcs = grammars.values(), - outs = outs.values(), - cmd = cmd, - cmd_bat = cmd, - tools = [":generate_grammar_tables"], + tools = [":ggt"], visibility = ["//visibility:private"], ) def generate_extinst_lang_headers(name, grammar = None): + """ + Creates a :gen_extinst_lang_headers_* target for a C++ header + the enums in a SPIR-V extended instruction set. + + Args: + name: the basename of the emitted header file. + grammar: the path to the JSON grammar file for the extended + instruction set. + """ if not grammar: fail("Must specify grammar", "grammar") outs = dict(
diff --git a/docs/downloads.md b/docs/downloads.md index 0454b9e..853c2be 100644 --- a/docs/downloads.md +++ b/docs/downloads.md
@@ -20,14 +20,12 @@ Download the latest builds of the [main](https://github.com/KhronosGroup/SPIRV-Tools/tree/main) 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) | | +| Platform | Processor | Compiler | Release build | Debug build | +| --- | --- | --- | --- | --- | +| Windows | x86-64 | VisualStudio 2022 (MSVC v143) | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_release.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_windows_vs2022_release.svg" alt="status of VS 2022 release build"></a> | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_debug.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_windows_vs2022_debug.svg" alt="status of VS 2022 debug build"></a> | +| Linux | x86-64 | GCC 9.4 | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_linux_gcc_release.svg" alt="status of Linux GCC build"></a> | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_linux_gcc_debug.svg" alt="status of Linux GCC debug build"></a> | +| macOS | x86-64 | Clang 15 | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg" alt="status of macOS Clang build"></a> | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_debug.svg" alt="status of macOS Clang build"></a> | -### 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) | | +Note: If you suspect something is wrong with the compiler versions mentioned, +check the scripts and configurations in the [kokoro](../kokoro) source tree, +or the results of the checks on the latest commits on the `main` branch.
diff --git a/docs/syntax.md b/docs/syntax.md index c135d01..ff53d53 100644 --- a/docs/syntax.md +++ b/docs/syntax.md
@@ -101,7 +101,7 @@ The syntax for a floating point literal is the same as floating point constants in the C programming language, except: -* An optional leading minus (`-`) is part of the literal. +* An optional leading minus (`-`) or leading plus (`+`) is part of the literal. * An optional type specifier suffix is not allowed. Infinity and NaN values are expressed in hexadecimal float literals by using the maximum representable exponent for the bit width. @@ -125,6 +125,9 @@ Zero and normal values are printed in decimal form with enough digits to preserve all significand bits. +Hex float values that underflow are rounded to zero. If there is a leading +minus sign, underflow goes to negative zero. + ## Arbitrary Integers <a name="immediate"></a> @@ -215,6 +218,50 @@ that named ID being output. This may be valid SPIR-V, contrary to the presumed intention of the writer. +## OpUnknown +<a name="op-unknown"></a> + +HLSL has a feature that allows users to specify an exact SPIR-V type using [the +`SpirvType` template](https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md#types). +This feature allows the user to specify a type opcode that the compiler does +not know. In this case, it will be unable to generate assembly using the +correct mnemonic. + +In order to represent unknown opcodes in assembly format, the `OpUnknown` +pseudo-instruction may be used. The syntax is: + +``` +OpUnknown(<enumerant>, <WordCount>) <operand 1> ... +``` + +`enumerant` is the opcode enumerant, and `WordCount` is the number of words in +the instruction. These will be assembled into a single word representing the +opcode. Operands will be parsed according to the alternate parsing mode +described in [Arbitrary Integers](#op-unknown). Named enumerated values cannot +be handled by this mode and must be represented using the arbitrary integer +syntax. + +It must be used at the beginning of a new instruction, and if there is a result +ID it must explicitly be passed in as an operand. This is because the physical +layout of a SPIR-V instruction may include a result type operand before the +result ID operand, but it depends on the opcode and cannot be inferred for an +unknown operand. + +For example, a 32-bit signed integer type could be represented like this: + +``` +OpUnknown(21, 4) %int_t 32 1 +``` + +An OpStore instruction could be represented as: + +``` +OpUnknown(62, 3) %9 %12 +``` + +The enumerant and word count must be decimal integers. + + ## Notes * Some enumerants cannot be used by name, because the target instruction
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 1ccab19..eb4e69e 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt
@@ -26,6 +26,52 @@ set(${var} ${val} PARENT_SCOPE) endfunction() +if (DEFINED mimalloc_SOURCE_DIR) + # This allows flexible position of the mimalloc repo. + set(MIMALLOC_DIR ${mimalloc_SOURCE_DIR}) +else() + if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mimalloc) + set(MIMALLOC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/mimalloc) + endif() +endif() + +# Used on Windows by default, but allow opt-in on other platforms +if (WIN32) + set(SPIRV_TOOLS_USE_MIMALLOC_DEFAULT_VALUE ON) + set(SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD_DEFAULT_VALUE OFF) +else() + set(SPIRV_TOOLS_USE_MIMALLOC_DEFAULT_VALUE OFF) + set(SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD_DEFAULT_VALUE OFF) +endif() + +# To avoid unexpected side effects on users of the static library, mimalloc +# must be explicitly enabled when building static libraries. +include(CMakeDependentOption) +cmake_dependent_option(SPIRV_TOOLS_USE_MIMALLOC + "Executables and shared libraries use mimalloc instead of the default allocator" + ${SPIRV_TOOLS_USE_MIMALLOC_DEFAULT_VALUE} "MIMALLOC_DIR" OFF) + +cmake_dependent_option(SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD + "Static libraries use mimalloc instead of the default allocator" + ${SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD_DEFAULT_VALUE} SPIRV_TOOLS_USE_MIMALLOC OFF) + +if (SPIRV_TOOLS_USE_MIMALLOC) + if (NOT WIN32) + push_variable(MI_OVERRIDE 0) + endif() + push_variable(MI_BUILD_TESTS 0) + + add_subdirectory(${MIMALLOC_DIR} ${CMAKE_BINARY_DIR}/external/mimalloc EXCLUDE_FROM_ALL) + if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang) + target_compile_options(mimalloc-static PRIVATE -Wno-int-conversion) + endif() + + if (NOT WIN32) + pop_variable(MI_OVERRIDE) + endif() + pop_variable(MI_BUILD_TESTS) +endif() + if (DEFINED SPIRV-Headers_SOURCE_DIR) # This allows flexible position of the SPIRV-Headers repo. set(SPIRV_HEADER_DIR ${SPIRV-Headers_SOURCE_DIR})
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 100e61e..28fd916 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h
@@ -80,6 +80,8 @@ SPV_ERROR_INVALID_DATA = -14, // Indicates data rules validation failure. SPV_ERROR_MISSING_EXTENSION = -15, SPV_ERROR_WRONG_VERSION = -16, // Indicates wrong SPIR-V version + SPV_ERROR_FNVAR = + -17, // Error related to SPV_INTEL_function_variants extension SPV_FORCE_32_BIT_ENUM(spv_result_t) } spv_result_t; @@ -189,36 +191,24 @@ SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE, // SPIR-V Sec 3.FSR -// NOTE: New concrete enum values should be added at the end. + // NOTE: New concrete enum values should be added at the end. -// The "optional" and "variable" operand types are only used internally by -// the assembler and the binary parser. -// There are two categories: -// Optional : expands to 0 or 1 operand, like ? in regular expressions. -// Variable : expands to 0, 1 or many operands or pairs of operands. -// This is similar to * in regular expressions. + // The "optional" and "variable" operand types are only used internally by + // the assembler and the binary parser. + // There are two categories: + // Optional : expands to 0 or 1 operand, like ? in regular expressions. + // Variable : expands to 0, 1 or many operands or pairs of operands. + // This is similar to * in regular expressions. -// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED. -// The concept of "optional" and "variable" operand types are only intended -// for use as an implementation detail of parsing SPIR-V, either in text or -// binary form. Instead of using enum ranges, use characteristic function -// spvOperandIsConcrete. -// The use of enum value ranges in a public API makes it difficult to insert -// new values into a range without also breaking binary compatibility. -// -// Macros for defining bounds on optional and variable operand types. -// Any variable operand type is also optional. -// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_* -#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM -#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM -#define LAST_VARIABLE(ENUM) \ - ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \ - SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM + // Use characteristic function spvOperandIsConcrete to classify the + // operand types; when it returns false, the operand is optional or variable. + // + // Any variable operand type is also optional. // An optional operand represents zero or one logical operands. // In an instruction definition, this may only appear at the end of the // operand types. - FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID), + SPV_OPERAND_TYPE_OPTIONAL_ID, // An optional image operand type. SPV_OPERAND_TYPE_OPTIONAL_IMAGE, // An optional memory access type. @@ -243,7 +233,7 @@ // A variable operand represents zero or more logical operands. // In an instruction definition, this may only appear at the end of the // operand types. - FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID), + SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER, // A sequence of zero or more pairs of (typed literal integer, Id). // Expands to zero or more: @@ -251,7 +241,7 @@ // where the literal number must always be an integer of some sort. SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID, // A sequence of zero or more pairs of (Id, Literal integer) - LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER), + SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER, // The following are concrete enum types from the DebugInfo extended // instruction set. @@ -320,6 +310,32 @@ SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE, // Enum type from SPV_NV_cooperative_matrix2 SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS, + // Optional types from SPV_INTEL_subgroup_matrix_multiply_accumulate + SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS, + SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS, + + SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT, + SPV_OPERAND_TYPE_COMPONENT_TYPE, + + // From nonesmantic.clspvreflection + SPV_OPERAND_TYPE_KERNEL_PROPERTY_FLAGS, + + // From nonesmantic.shader.debuginfo.100 + SPV_OPERAND_TYPE_SHDEBUG100_BUILD_IDENTIFIER_FLAGS, + SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, + SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_COMPOSITE_TYPE, + SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_IMPORTED_ENTITY, + SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS, + SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION, + SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER, + + // SPV_ARM_tensors + SPV_OPERAND_TYPE_TENSOR_OPERANDS, + SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS, + + // SPV_INTEL_function_variants + SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY, + SPV_OPERAND_TYPE_VARIABLE_CAPABILITY, // This is a sentinel value, and does not represent an operand type. // It should come last. @@ -347,6 +363,8 @@ SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, + SPV_EXT_INST_TYPE_TOSA_001000_1, + SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise @@ -368,6 +386,18 @@ SPV_NUMBER_FLOATING, } spv_number_kind_t; +// Represent the encoding of floating point values +typedef enum spv_fp_encoding_t { + SPV_FP_ENCODING_UNKNOWN = + 0, // The encoding is not specified. Has to be deduced from bitwidth + SPV_FP_ENCODING_IEEE754_BINARY16, // half float + SPV_FP_ENCODING_IEEE754_BINARY32, // single float + SPV_FP_ENCODING_IEEE754_BINARY64, // double float + SPV_FP_ENCODING_BFLOAT16, + SPV_FP_ENCODING_FLOAT8_E4M3, + SPV_FP_ENCODING_FLOAT8_E5M2, +} spv_fp_encoding_t; + typedef enum spv_text_to_binary_options_t { SPV_TEXT_TO_BINARY_OPTION_NONE = SPV_BIT(0), // Numeric IDs in the binary will have the same values as in the source. @@ -396,6 +426,9 @@ // Reorder blocks to match the structured control flow of SPIR-V to increase // readability. SPV_BINARY_TO_TEXT_OPTION_REORDER_BLOCKS = SPV_BIT(9), + // Handle unknown opcodes and unknown extended instruction numbers by emitting + // them as OpUnknown instructions with raw integer operands. + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES = SPV_BIT(10), SPV_FORCE_32_BIT_ENUM(spv_binary_to_text_options_t) } spv_binary_to_text_options_t; @@ -403,7 +436,7 @@ // The default id bound is to the minimum value for the id limit // in the spir-v specification under the section "Universal Limits". -const uint32_t kDefaultMaxIdBound = 0x3FFFFF; +const static uint32_t kDefaultMaxIdBound = 0x3FFFFF; // Structures @@ -423,6 +456,8 @@ spv_number_kind_t number_kind; // The number of bits for a literal number type. uint32_t number_bit_width; + // The encoding used for floating point values + spv_fp_encoding_t fp_encoding; } spv_parsed_operand_t; // An instruction parsed from a binary SPIR-V module. @@ -729,6 +764,16 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId( spv_validator_options options, bool val); +// Allow Offset (in addition to ConstOffset) for texture operations. +// Was added for VK_KHR_maintenance8 +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowOffsetTextureOperand( + spv_validator_options options, bool val); + +// Allow base operands of some bit operations to be non-32-bit wide. +// Was added for VK_KHR_maintenance9 +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowVulkan32BitBitwise( + spv_validator_options options, bool val); + // Whether friendly names should be used in validation error messages. SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames( spv_validator_options options, bool val);
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index 6a64e93..1b3ed86 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp
@@ -126,6 +126,18 @@ spvValidatorOptionsSetAllowLocalSizeId(options_, val); } + // Allow Offset (in addition to ConstOffset) for texture + // operations. Was added for VK_KHR_maintenance8 + void SetAllowOffsetTextureOperand(bool val) { + spvValidatorOptionsSetAllowOffsetTextureOperand(options_, val); + } + + // Allow base operands of some bit operations to be non-32-bit wide. + // Was added for VK_KHR_maintenance9 + void SetAllowVulkan32BitBitwise(bool val) { + spvValidatorOptionsSetAllowVulkan32BitBitwise(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/linker.hpp b/include/spirv-tools/linker.hpp index 9037b94..99527ed 100644 --- a/include/spirv-tools/linker.hpp +++ b/include/spirv-tools/linker.hpp
@@ -67,12 +67,36 @@ allow_ptr_type_mismatch_ = allow_ptr_type_mismatch; } + std::string GetFnVarTargetsCsv() const { return fnvar_targets_csv_; } + void SetFnVarTargetsCsv(std::string fnvar_targets_csv) { + fnvar_targets_csv_ = fnvar_targets_csv; + } + + std::string GetFnVarArchitecturesCsv() const { + return fnvar_architectures_csv_; + } + void SetFnVarArchitecturesCsv(std::string fnvar_architectures_csv) { + fnvar_architectures_csv_ = fnvar_architectures_csv; + } + + bool GetHasFnVarCapabilities() const { return has_fnvar_capabilities_; } + void SetHasFnVarCapabilities(bool fnvar_capabilities) { + has_fnvar_capabilities_ = fnvar_capabilities; + } + + std::vector<std::string> GetInFiles() const { return in_files_; } + void SetInFiles(std::vector<std::string> in_files) { in_files_ = in_files; } + private: bool create_library_{false}; bool verify_ids_{false}; bool allow_partial_linkage_{false}; bool use_highest_version_{false}; bool allow_ptr_type_mismatch_{false}; + std::string fnvar_targets_csv_{""}; + std::string fnvar_architectures_csv_{""}; + bool has_fnvar_capabilities_ = false; + std::vector<std::string> in_files_{{}}; }; // Links one or more SPIR-V modules into a new SPIR-V module. That is, combine
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 9427b01..fd4527b 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp
@@ -240,7 +240,7 @@ private: struct SPIRV_TOOLS_LOCAL Impl; // Opaque struct for holding internal data. - std::unique_ptr<Impl> impl_; // Unique pointer to internal data. + std::unique_ptr<Impl> impl_; // Unique pointer to internal data. }; // Creates a null pass. @@ -645,6 +645,11 @@ // Works best after LICM and local multi store elimination pass. Optimizer::PassToken CreateLoopUnswitchPass(); +// Creates a pass to legalize multidimensional arrays for Vulkan. +// This pass will replace multidimensional arrays of resources with a single +// dimensional array. Combine-access-chains should be run before this pass. +Optimizer::PassToken CreateLegalizeMultidimArrayPass(); + // Create global value numbering pass. // This pass will look for instructions where the same value is computed on all // paths leading to the instruction. Those instructions are deleted. @@ -655,7 +660,7 @@ // element if those elements are accessed individually. The parameter is a // limit on the number of members in the composite variable that the pass will // consider replacing. -Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100); +Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 0); // Create a private to local pass. // This pass looks for variables declared in the private storage class that are @@ -968,6 +973,70 @@ // Creates a pass to add/remove maximal reconvergence execution mode. // This pass either adds or removes maximal reconvergence from all entry points. Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add); + +// Creates a pass to split combined image+sampler variables and function +// parameters into separate image and sampler parts. Binding numbers and +// other decorations are copied. +Optimizer::PassToken CreateSplitCombinedImageSamplerPass(); + +// Creates a pass to remap bindings to avoid conflicts, assuming the module +// is valid for Vulkan. A conflict exits when an entry point uses two distinct +// variables with the same descriptor set and binding. Vulkan allows one kind +// of conflict: when one varible is an image (or array of images), and the +// other is a sampler (or an array of samplers). + +// Conflicts are eliminated by incrementing the binding number of the sampler +// part, and then propagating that increment through variables with +// higher-numbered bindings until no conflict remains. This handles the case +// when multiple shaders may share the same resource variables; this can +// introduce holes in binding slots. +// +// Here's an example where shaders Alpha, Beta, Gamma, Delta collectively use +// resource variables %100, %101, %102, %103, %104 all with the same +// DescriptorSet and with Bindings as in the following table: +// +// Before: +// +// Binding: 0 1 2 3 +// Alpha: %100,%101 +// Beta: %100 %102 +// Gamma: %102 %103 +// Delta: %103 %104 +// +// The Alpha shader has a conflict where variables %100, %101 have the same +// descriptor set and binding. If %100 is a sampler resource variable, then +// the conflict is resolved by incrementing the binding number on %100 from 0 +// to 1. But this causes a new confict for shader Beta because it now uses +// both %100 and %102 with binding number 1. That conflict is resolved by +// incrementing the binding number on its variable that originally appeared +// second (i.e. %102), so %102 gets binding 2. This now produces a conflict +// for Gamma between %102 and %103 using binding number 2. Since %103 originally +// appeared second (in the view from Gamma), the algorithm bumps %103 to binding +// number %103. Now Delta has a conflict between %103 and %104, resulting in +// %104 getting the next binding number, 4. The picture afterward is: +// +// After: +// +// Binding: 0 1 2 3 4 +// Alpha: %101 %100 +// Beta: %100 %102 +// Gamma: %102 %103 +// Delta: %103 %104 +// +// +// This pass assumes binding numbers are not applid via decoration groups +// (OpDecorationGroup). +Optimizer::PassToken CreateResolveBindingConflictsPass(); + +// Create a pass to canonicalize IDs to improve compression of SPIR-V binary +// files. The resulting modules have an increased ID range (IDs are not as +// tightly packed around zero), but will compress better when multiple modules +// are compressed together, since the compressor's dictionary can find better +// cross module commonality. This pass should be run after most optimization +// passes except for +// --strip-debug because this pass will use OpName to canonicalize IDs. i.e. Run +// --strip-debug after this pass. +Optimizer::PassToken CreateCanonicalizeIdsPass(); } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/check-format/build-docker.sh b/kokoro/check-format/build-docker.sh new file mode 100755 index 0000000..b7af9ee --- /dev/null +++ b/kokoro/check-format/build-docker.sh
@@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright (c) 2025 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. + +# Fail on any error. +set -e + +# This is required to run any git command in the docker since owner will +# have changed between the clone environment, and the docker container. +# Marking the root of the repo as safe for ownership changes. +git config --global --add safe.directory "$PWD" + +echo $(date): Check formatting... +./utils/check_code_format.sh ${1:-main} +echo $(date): check completed.
diff --git a/kokoro/check-format/build.sh b/kokoro/check-format/build.sh old mode 100644 new mode 100755 index 96603e4..6f073c6 --- a/kokoro/check-format/build.sh +++ b/kokoro/check-format/build.sh
@@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2018 Google LLC. +# Copyright (c) 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,37 +12,16 @@ # 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. -# -# Android Build Script. # Fail on any error. set -e -# Display commands being run. -set -x -BUILD_ROOT=$PWD -SRC=$PWD/github/SPIRV-Tools +SCRIPT_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +SRC_ROOT="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" +TARGET_BRANCH="${KOKORO_GITHUB_PULL_REQUEST_TARGET_BRANCH-main}" -# This is required to run any git command in the docker since owner will -# have changed between the clone environment, and the docker container. -# Marking the root of the repo as safe for ownership changes. -git config --global --add safe.directory $SRC - -# Get clang-format-5.0.0. -# Once kokoro upgrades the Ubuntu VMs, we can use 'apt-get install clang-format' -curl -L http://releases.llvm.org/5.0.0/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04.tar.xz -o clang-llvm.tar.xz -tar xf clang-llvm.tar.xz -export PATH=$PWD/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04/bin:$PATH - -cd $SRC -git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers -git clone https://github.com/google/googletest external/googletest -cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. -git clone --depth=1 https://github.com/google/effcee external/effcee -git clone --depth=1 https://github.com/google/re2 external/re2 -# The --fail flag causes the command to fail on HTTP error response codes, like 404. -curl -L --fail https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py - -echo $(date): Check formatting... -./utils/check_code_format.sh; -echo $(date): check completed. +docker run --rm -i \ + --volume "${SRC_ROOT}:${SRC_ROOT}" \ + --workdir "${SRC_ROOT}" \ + "us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/formatter" \ + "${SCRIPT_DIR}/build-docker.sh" "${TARGET_BRANCH}"
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/check-format/continuous.cfg similarity index 94% rename from kokoro/check-format/presubmit_check_format.cfg rename to kokoro/check-format/continuous.cfg index 1993289..dec499f 100644 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/kokoro/check-format/continuous.cfg
@@ -12,5 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Presubmit build configuration. build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/check-format/presubmit.cfg similarity index 94% copy from kokoro/check-format/presubmit_check_format.cfg copy to kokoro/check-format/presubmit.cfg index 1993289..dec499f 100644 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/kokoro/check-format/presubmit.cfg
@@ -12,5 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Presubmit build configuration. build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/dxc-smoketest/build.sh old mode 100644 new mode 100755 similarity index 68% copy from kokoro/check-format/presubmit_check_format.cfg copy to kokoro/dxc-smoketest/build.sh index 1993289..0428f38 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/kokoro/dxc-smoketest/build.sh
@@ -1,10 +1,11 @@ +#!/bin/bash # Copyright (c) 2018 Google LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# 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, @@ -12,5 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Presubmit build configuration. -build_file: "SPIRV-Tools/kokoro/check-format/build.sh" +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-dxc-smoketest
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/dxc-smoketest/continuous.cfg similarity index 85% copy from kokoro/check-format/presubmit_check_format.cfg copy to kokoro/dxc-smoketest/continuous.cfg index 1993289..c52c216 100644 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/kokoro/dxc-smoketest/continuous.cfg
@@ -12,5 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Presubmit build configuration. -build_file: "SPIRV-Tools/kokoro/check-format/build.sh" +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/dxc-smoketest/build.sh" +
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/dxc-smoketest/presubmit.cfg similarity index 90% copy from kokoro/check-format/presubmit_check_format.cfg copy to kokoro/dxc-smoketest/presubmit.cfg index 1993289..6e0c861 100644 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/kokoro/dxc-smoketest/presubmit.cfg
@@ -13,4 +13,5 @@ # limitations under the License. # Presubmit build configuration. -build_file: "SPIRV-Tools/kokoro/check-format/build.sh" +build_file: "SPIRV-Tools/kokoro/dxc-smoketest/build.sh" +
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh index 4bb889a..592c50b 100644 --- a/kokoro/macos-clang-release-bazel/build.sh +++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -32,14 +32,16 @@ cd $SRC /usr/bin/python3 utils/git-sync-deps --treeless -# Get bazel 7.0.2 -gsutil cp gs://bazel/7.0.2/release/bazel-7.0.2-darwin-x86_64 . -chmod +x bazel-7.0.2-darwin-x86_64 +# Get bazel 7.4.0 +BAZEL_VER=7.4.0 +gcloud config set auth/disable_credentials True +gcloud storage cp gs://bazel/$BAZEL_VER/release/bazel-$BAZEL_VER-darwin-x86_64 . +chmod +x bazel-$BAZEL_VER-darwin-x86_64 echo $(date): Build everything... -./bazel-7.0.2-darwin-x86_64 build --cxxopt=-std=c++17 :all +./bazel-$BAZEL_VER-darwin-x86_64 build --cxxopt=-std=c++17 :all echo $(date): Build completed. echo $(date): Starting bazel test... -./bazel-7.0.2-darwin-x86_64 test --cxxopt=-std=c++17 :all +./bazel-$BAZEL_VER-darwin-x86_64 test --cxxopt=-std=c++17 :all echo $(date): Bazel test completed.
diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index b2c034d..f6cd513 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh
@@ -22,17 +22,17 @@ # This is required to run any git command in the docker since owner will # have changed between the clone environment, and the docker container. -# Marking the root of the repo as safe for ownership changes. -git config --global --add safe.directory $ROOT_DIR +# Mark all repositories as safe for ownership changes. +git config --global --add safe.directory '*' . /bin/using.sh # Declare the bash `using` function for configuring toolchains. using python-3.12 if [ $COMPILER = "clang" ]; then - using clang-10.0.0 + using clang-18 elif [ $COMPILER = "gcc" ]; then - using gcc-9 + using gcc-13 fi cd $ROOT_DIR @@ -45,13 +45,13 @@ mkdir "$dir" } -if [ $TOOL != "cmake-smoketest" ]; then +if [ $TOOL != "cmake-shaderc-smoketest" ] && [ $TOOL != "cmake-dxc-smoketest" ]; then # Get source for dependencies, as specified in the DEPS file /usr/bin/python3 utils/git-sync-deps --treeless fi if [ $TOOL = "cmake" ]; then - using cmake-3.17.2 + using cmake-3.31.2 using ninja-1.10.0 # Possible configurations are: @@ -115,8 +115,8 @@ ninja install cd $KOKORO_ARTIFACTS_DIR tar czf install.tgz install -elif [ $TOOL = "cmake-smoketest" ]; then - using cmake-3.17.2 +elif [ $TOOL = "cmake-shaderc-smoketest" ]; then + using cmake-3.31.2 using ninja-1.10.0 # Get shaderc. @@ -156,9 +156,46 @@ echo $(date): Starting ctest... ctest --output-on-failure -j4 echo $(date): ctest completed. +elif [ $TOOL = "cmake-dxc-smoketest" ]; then + using cmake-3.31.2 + using ninja-1.10.0 + + # Get shaderc. + DXC_DIR=/tmp/dxc + clean_dir "$DXC_DIR" + cd $DXC_DIR + git clone https://github.com/microsoft/DirectXShaderCompiler.git . + cd $DXC_DIR/external + + # Get DXC dependencies. Link the appropriate SPIRV-Tools. + git submodule update --init DirectX-Headers + rm -rf SPIRV-Tools + ln -s $ROOT_DIR SPIRV-Tools + git clone https://github.com/KhronosGroup/SPIRV-Headers.git SPIRV-Headers + + cd $DXC_DIR + mkdir build + cd $DXC_DIR/build + + # Invoke the build. + echo $(date): Configuring build... + cmake $DXC_DIR \ + -C $DXC_DIR/cmake/caches/PredefinedParams.cmake \ + -DCMAKE_BUILD_TYPE="Release" \ + -G Ninja + + echo $(date): Building ClangSPIRVTests... + ninja ClangSPIRVTests + + echo $(date): Testing ClangSPIRVTests... + tools/clang/unittests/SPIRV/ClangSPIRVTests + + echo $(date): Testing check-clang-codegenspirv... + ninja check-clang-codegenspirv + elif [ $TOOL = "cmake-android-ndk" ]; then - using cmake-3.17.2 - using ndk-r25c + using cmake-3.31.2 + using ndk-r27c using ninja-1.10.0 clean_dir "$ROOT_DIR/build" @@ -178,7 +215,7 @@ ninja echo $(date): Build completed. elif [ $TOOL = "android-ndk-build" ]; then - using ndk-r25c + using ndk-r27c clean_dir "$ROOT_DIR/build" cd "$ROOT_DIR/build" @@ -193,7 +230,7 @@ echo $(date): ndk-build completed. elif [ $TOOL = "bazel" ]; then - using bazel-7.0.2 + using bazel-7.4.0 echo $(date): Build everything... bazel build --cxxopt=-std=c++17 :all
diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh index 688ba79..cb6cf58 100644 --- a/kokoro/scripts/linux/build.sh +++ b/kokoro/scripts/linux/build.sh
@@ -52,7 +52,7 @@ --env KOKORO_ARTIFACTS_DIR="${KOKORO_ARTIFACTS_DIR}" \ --env BUILD_SHA="${BUILD_SHA}" \ --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ - "gcr.io/shaderc-build/radial-build:latest" + us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/cpp-builder RESULT=$? # This is important. If the permissions are not fixed, kokoro will fail
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat index fe15f2d..fbd5f97 100644 --- a/kokoro/scripts/windows/build.bat +++ b/kokoro/scripts/windows/build.bat
@@ -21,21 +21,15 @@ set BUILD_TYPE=%1 set VS_VERSION=%2 -:: Force usage of python 3.6 -set PATH=C:\python36;"C:\Program Files\cmake-3.23.1-windows-x86_64\bin";%PATH% +:: Force usage of python 3.12, cmake 3.31.2 +set PATH=C:\python312;c:\cmake-3.31.2\bin;%PATH% :: ######################################### :: set up msvc build env :: ######################################### -if %VS_VERSION% == 2017 ( - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 - echo "Using VS 2017..." - - :: RE2 does not support VS2017, we we must disable tests. - set BUILD_TESTS=NO -) else if %VS_VERSION% == 2019 ( - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 - echo "Using VS 2019..." +if %VS_VERSION% == 2022 ( + call "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" x64 + echo "Using VS 2022..." ) cd %SRC% @@ -63,6 +57,7 @@ set CMAKE_FLAGS=-DSPIRV_SKIP_TESTS=ON %CMAKE_FLAGS% ) +cmake --version cmake %CMAKE_FLAGS% .. if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
diff --git a/kokoro/shaderc-smoketest/build.sh b/kokoro/shaderc-smoketest/build.sh index 60c816d..5756e21 100755 --- a/kokoro/shaderc-smoketest/build.sh +++ b/kokoro/shaderc-smoketest/build.sh
@@ -19,4 +19,4 @@ set -x SCRIPT_DIR=`dirname "$BASH_SOURCE"` -source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-smoketest +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-shaderc-smoketest
diff --git a/kokoro/windows-msvc-2017-release/continuous.cfg b/kokoro/windows-msvc-2017-release/continuous.cfg deleted file mode 100644 index a9ac6ec..0000000 --- a/kokoro/windows-msvc-2017-release/continuous.cfg +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright (c) 2018 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Continuous build configuration. -build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-release/build.bat" - -action { - define_artifacts { - regex: "install.zip" - } -}
diff --git a/kokoro/windows-msvc-2017-release/presubmit.cfg b/kokoro/windows-msvc-2017-release/presubmit.cfg deleted file mode 100644 index 5efd429..0000000 --- a/kokoro/windows-msvc-2017-release/presubmit.cfg +++ /dev/null
@@ -1,16 +0,0 @@ -# Copyright (c) 2018 Google LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Presubmit build configuration. -build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-release/build.bat"
diff --git a/kokoro/windows-msvc-2019-release/build.bat b/kokoro/windows-msvc-2019-release/build.bat deleted file mode 100644 index 8212924..0000000 --- a/kokoro/windows-msvc-2019-release/build.bat +++ /dev/null
@@ -1,24 +0,0 @@ -:: Copyright (c) 2023 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. -:: -:: Windows Build Script. - -@echo on - -:: Find out the directory of the common build script. -set SCRIPT_DIR=%~dp0 - -:: Call with correct parameter -call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2019 -
diff --git a/kokoro/windows-msvc-2019-release/presubmit.cfg b/kokoro/windows-msvc-2019-release/presubmit.cfg deleted file mode 100644 index 4c578e0..0000000 --- a/kokoro/windows-msvc-2019-release/presubmit.cfg +++ /dev/null
@@ -1,16 +0,0 @@ -# Copyright (c) 2023 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/windows-msvc-2019-release/build.bat"
diff --git a/kokoro/windows-msvc-2019-debug/build.bat b/kokoro/windows-vs2022-amd64-debug/build.bat similarity index 92% rename from kokoro/windows-msvc-2019-debug/build.bat rename to kokoro/windows-vs2022-amd64-debug/build.bat index 7ad94c1..cac551c 100644 --- a/kokoro/windows-msvc-2019-debug/build.bat +++ b/kokoro/windows-vs2022-amd64-debug/build.bat
@@ -20,4 +20,4 @@ set SCRIPT_DIR=%~dp0 :: Call with correct parameter -call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2019 +call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2022
diff --git a/kokoro/windows-msvc-2019-debug/continuous.cfg b/kokoro/windows-vs2022-amd64-debug/continuous.cfg similarity index 86% rename from kokoro/windows-msvc-2019-debug/continuous.cfg rename to kokoro/windows-vs2022-amd64-debug/continuous.cfg index e3a7863..6aed088 100644 --- a/kokoro/windows-msvc-2019-debug/continuous.cfg +++ b/kokoro/windows-vs2022-amd64-debug/continuous.cfg
@@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Continuous build configuration. -build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-debug/build.bat" +build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-debug/build.bat" action { define_artifacts {
diff --git a/kokoro/windows-msvc-2019-debug/presubmit.cfg b/kokoro/windows-vs2022-amd64-debug/presubmit.cfg similarity index 85% rename from kokoro/windows-msvc-2019-debug/presubmit.cfg rename to kokoro/windows-vs2022-amd64-debug/presubmit.cfg index 0ed3594..69460b7 100644 --- a/kokoro/windows-msvc-2019-debug/presubmit.cfg +++ b/kokoro/windows-vs2022-amd64-debug/presubmit.cfg
@@ -12,5 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Presubmit build configuration. -build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-debug/build.bat" +build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-debug/build.bat"
diff --git a/kokoro/windows-msvc-2017-release/build.bat b/kokoro/windows-vs2022-amd64-release/build.bat similarity index 95% rename from kokoro/windows-msvc-2017-release/build.bat rename to kokoro/windows-vs2022-amd64-release/build.bat index 899fcbc..5294a90 100644 --- a/kokoro/windows-msvc-2017-release/build.bat +++ b/kokoro/windows-vs2022-amd64-release/build.bat
@@ -1,4 +1,4 @@ -:: Copyright (c) 2018 Google LLC. +:: Copyright (c) 2025 Google LLC :: :: Licensed under the Apache License, Version 2.0 (the "License"); :: you may not use this file except in compliance with the License. @@ -20,5 +20,5 @@ set SCRIPT_DIR=%~dp0 :: Call with correct parameter -call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2017 +call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2022
diff --git a/kokoro/windows-msvc-2019-release/continuous.cfg b/kokoro/windows-vs2022-amd64-release/continuous.cfg similarity index 81% rename from kokoro/windows-msvc-2019-release/continuous.cfg rename to kokoro/windows-vs2022-amd64-release/continuous.cfg index 624ccbd..977957a 100644 --- a/kokoro/windows-msvc-2019-release/continuous.cfg +++ b/kokoro/windows-vs2022-amd64-release/continuous.cfg
@@ -1,4 +1,4 @@ -# Copyright (c) 2023 Google LLC. +# Copyright (c) 2025 Google LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Continuous build configuration. -build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-release/build.bat" +build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-release/build.bat" action { define_artifacts {
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/windows-vs2022-amd64-release/presubmit.cfg similarity index 81% copy from kokoro/check-format/presubmit_check_format.cfg copy to kokoro/windows-vs2022-amd64-release/presubmit.cfg index 1993289..0cf2b85 100644 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/kokoro/windows-vs2022-amd64-release/presubmit.cfg
@@ -1,4 +1,4 @@ -# Copyright (c) 2018 Google LLC. +# Copyright (c) 2025 Google LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,5 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Presubmit build configuration. -build_file: "SPIRV-Tools/kokoro/check-format/build.sh" +build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-release/build.bat"
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b20357b..8e918de 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt
@@ -12,62 +12,65 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py") -set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_syntax.py") +set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/vim/generate_syntax.py") set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py") +set(GGT_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/ggt.py") # Pull in grammar files that have migrated to SPIRV-Headers -set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") -set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") -set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json") +set(GRAMMAR_DIR "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1") +set(SPIRV_CORE_GRAMMAR_JSON_FILE "${GRAMMAR_DIR}/spirv.core.grammar.json") +set(EI_debuginfo "${GRAMMAR_DIR}/extinst.debuginfo.grammar.json") +set(EI_cldebuginfo "${GRAMMAR_DIR}/extinst.opencl.debuginfo.100.grammar.json") +set(EI_glsl "${GRAMMAR_DIR}/extinst.glsl.std.450.grammar.json") +set(EI_opencl "${GRAMMAR_DIR}/extinst.opencl.std.100.grammar.json") +set(EI_amd_evp "${GRAMMAR_DIR}/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json") +set(EI_amd_trimm "${GRAMMAR_DIR}/extinst.spv-amd-shader-trinary-minmax.grammar.json") +set(EI_amd_gcn "${GRAMMAR_DIR}/extinst.spv-amd-gcn-shader.grammar.json") +set(EI_amd_ballot "${GRAMMAR_DIR}/extinst.spv-amd-shader-ballot.grammar.json") +set(EI_ns_debuginfo "${GRAMMAR_DIR}/extinst.nonsemantic.shader.debuginfo.grammar.json") +set(EI_ns_clspvreflect "${GRAMMAR_DIR}/extinst.nonsemantic.clspvreflection.grammar.json") +set(EI_ns_vkspreflect "${GRAMMAR_DIR}/extinst.nonsemantic.vkspreflection.grammar.json") +set(EI_tosa_001000_1 "${GRAMMAR_DIR}/extinst.tosa.001000.1.grammar.json") +set(EI_arm_motion_engine_100 "${GRAMMAR_DIR}/extinst.arm.motion-engine.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 -# later used by set_source_files_properties() calls. -# function() definitions are not suitable because they create new scopes. -macro(spvtools_core_tables CONFIG_VERSION) - set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") - set(GRAMMAR_INSTS_INC_FILE "${spirv-tools_BINARY_DIR}/core.insts-${CONFIG_VERSION}.inc") - set(GRAMMAR_KINDS_INC_FILE "${spirv-tools_BINARY_DIR}/operand.kinds-${CONFIG_VERSION}.inc") - add_custom_command(OUTPUT ${GRAMMAR_INSTS_INC_FILE} ${GRAMMAR_KINDS_INC_FILE} - COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} - --spirv-core-grammar=${GRAMMAR_JSON_FILE} - --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} - --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} - --core-insts-output=${GRAMMAR_INSTS_INC_FILE} - --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE} - --output-language=c++ - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} - ${GRAMMAR_JSON_FILE} - ${DEBUGINFO_GRAMMAR_JSON_FILE} - ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} - COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.") - list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE}) - list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE}) -endmacro(spvtools_core_tables) - -macro(spvtools_enum_string_mapping CONFIG_VERSION) - set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") - set(GRAMMAR_EXTENSION_ENUM_INC_FILE "${spirv-tools_BINARY_DIR}/extension_enum.inc") - set(GRAMMAR_ENUM_STRING_MAPPING_INC_FILE "${spirv-tools_BINARY_DIR}/enum_string_mapping.inc") - add_custom_command(OUTPUT ${GRAMMAR_EXTENSION_ENUM_INC_FILE} - ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} - COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} - --spirv-core-grammar=${GRAMMAR_JSON_FILE} - --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} - --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} - --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE} - --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} - --output-language=c++ - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} - ${GRAMMAR_JSON_FILE} - ${DEBUGINFO_GRAMMAR_JSON_FILE} - ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} - COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.") - list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE}) - list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}) -endmacro(spvtools_enum_string_mapping) +set(CORE_TABLES_BODY_INC_FILE ${spirv-tools_BINARY_DIR}/core_tables_body.inc) +set(CORE_TABLES_HEADER_INC_FILE ${spirv-tools_BINARY_DIR}/core_tables_header.inc) +add_custom_command(OUTPUT ${CORE_TABLES_BODY_INC_FILE} ${CORE_TABLES_HEADER_INC_FILE} + COMMAND Python3::Interpreter ${GGT_SCRIPT} + --core-tables-body-output=${CORE_TABLES_BODY_INC_FILE} + --core-tables-header-output=${CORE_TABLES_HEADER_INC_FILE} + --spirv-core-grammar=${SPIRV_CORE_GRAMMAR_JSON_FILE} + --extinst=,${EI_glsl} + --extinst=,${EI_opencl} + --extinst=CLDEBUG100_,${EI_cldebuginfo} + --extinst=SHDEBUG100_,${EI_ns_debuginfo} + --extinst=,${EI_amd_evp} + --extinst=,${EI_amd_trimm} + --extinst=,${EI_amd_gcn} + --extinst=,${EI_amd_ballot} + --extinst=,${EI_debuginfo} + --extinst=,${EI_ns_clspvreflect} + --extinst=,${EI_ns_vkspreflect} + --extinst=TOSA_,${EI_tosa_001000_1} + --extinst=,${EI_arm_motion_engine_100} + DEPENDS ${GGT_SCRIPT} + ${SPIRV_CORE_GRAMMAR_JSON_FILE} + ${EI_glsl} + ${EI_opencl} + ${EI_cldebuginfo} + ${EI_ns_debuginfo} + ${EI_amd_evp} + ${EI_amd_trimm} + ${EI_amd_gcn} + ${EI_amd_ballot} + ${EI_debuginfo} + ${EI_ns_clspvreflect} + ${EI_ns_vkspreflect} + ${EI_tosa_001000_1} + ${EI_arm_motion_engine_100} + COMMENT "Generate grammar tables") +add_custom_target(spirv-tools-tables DEPENDS ${CORE_TABLES_BODY_INC_FILE} ${CORE_TABLES_HEADER_INC_FILE}) macro(spvtools_vimsyntax CONFIG_VERSION CLVERSION) set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") @@ -77,60 +80,15 @@ add_custom_command(OUTPUT ${VIMSYNTAX_FILE} COMMAND Python3::Interpreter ${VIMSYNTAX_PROCESSING_SCRIPT} --spirv-core-grammar=${GRAMMAR_JSON_FILE} - --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${EI_debuginfo} --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} >${VIMSYNTAX_FILE} DEPENDS ${VIMSYNTAX_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} - ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${EI_debuinfo} COMMENT "Generate spvasm.vim: Vim syntax file for SPIR-V assembly.") endmacro(spvtools_vimsyntax) -macro(spvtools_glsl_tables CONFIG_VERSION) - set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") - set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.glsl.std.450.grammar.json") - set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/glsl.std.450.insts.inc") - add_custom_command(OUTPUT ${GRAMMAR_INC_FILE} - COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} - --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} - --glsl-insts-output=${GRAMMAR_INC_FILE} - --output-language=c++ - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${GLSL_GRAMMAR_JSON_FILE} - COMMENT "Generate info tables for GLSL extended instructions and operands v${CONFIG_VERSION}.") - list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) -endmacro(spvtools_glsl_tables) - -macro(spvtools_opencl_tables CONFIG_VERSION) - set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") - set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json") - set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/opencl.std.insts.inc") - add_custom_command(OUTPUT ${GRAMMAR_INC_FILE} - COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} - --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} - --opencl-insts-output=${GRAMMAR_INC_FILE} - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} - COMMENT "Generate info tables for OpenCL extended instructions and operands v${CONFIG_VERSION}.") - list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) -endmacro(spvtools_opencl_tables) - -macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) - set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") - set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json") - if(NOT EXISTS ${GRAMMAR_FILE}) - set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") - endif() - add_custom_command(OUTPUT ${INSTS_FILE} - COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} - --extinst-vendor-grammar=${GRAMMAR_FILE} - --vendor-insts-output=${INSTS_FILE} - --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX} - DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE} - COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.") - add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE}) - set_property(TARGET spv-tools-${SHORT_NAME} PROPERTY FOLDER "SPIRV-Tools build") - list(APPEND EXTINST_CPP_DEPENDS spv-tools-${SHORT_NAME}) -endmacro(spvtools_vendor_tables) - macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE) set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h) add_custom_command(OUTPUT ${OUT_H} @@ -144,22 +102,8 @@ list(APPEND EXTINST_CPP_DEPENDS spirv-tools-header-${NAME}) endmacro(spvtools_extinst_lang_headers) -spvtools_core_tables("unified1") -spvtools_enum_string_mapping("unified1") -spvtools_opencl_tables("unified1") -spvtools_glsl_tables("unified1") -spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "") -spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "") -spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") -spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") -spvtools_vendor_tables("debuginfo" "debuginfo" "") -spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") -spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_") -spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") -spvtools_vendor_tables("nonsemantic.vkspreflection" "vkspreflection" "") -spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) -spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) -spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("DebugInfo" ${EI_debuginfo}) +spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${EI_cldebuginfo}) spvtools_vimsyntax("unified1" "1.0") add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) @@ -182,9 +126,8 @@ # We need to wrap the .inc files with a custom target to avoid problems when # multiple targets depend on the same custom command. add_custom_target(core_tables - DEPENDS ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS}) -add_custom_target(enum_string_mapping - DEPENDS ${EXTENSION_H_DEPENDS} ${ENUM_STRING_MAPPING_CPP_DEPENDS}) + DEPENDS ${OPCODE_CPP_DEPENDS} + spirv-tools-tables) add_custom_target(extinst_tables DEPENDS ${EXTINST_CPP_DEPENDS}) @@ -205,13 +148,12 @@ DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} ${SPIRV_TOOLS_CHANGES_FILE} COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).") -# Convenience target for standalone generation of the build-version.inc file. -# This is not required for any dependence chain. -add_custom_target(spirv-tools-build-version - DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC}) -set_property(TARGET spirv-tools-build-version PROPERTY FOLDER "SPIRV-Tools build") -list(APPEND PCH_DEPENDS ${ENUM_STRING_MAPPING_CPP_DEPENDS} ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS} ${EXTENSION_H_DEPENDS} ${EXTINST_CPP_DEPENDS} ${SPIRV_TOOLS_BUILD_VERSION_INC}) +list(APPEND PCH_DEPENDS + ${CORE_TABLES_HEADER_INC_FILE} + ${OPCODE_CPP_DEPENDS} + ${EXTINST_CPP_DEPENDS} + ${SPIRV_TOOLS_BUILD_VERSION_INC}) set_source_files_properties( ${CMAKE_CURRENT_SOURCE_DIR}/pch_source.cpp PROPERTIES OBJECT_DEPENDS "${PCH_DEPENDS}") @@ -242,7 +184,6 @@ ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h - ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h ${CMAKE_CURRENT_SOURCE_DIR}/extensions.h ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h @@ -264,6 +205,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.h ${CMAKE_CURRENT_SOURCE_DIR}/table.h + ${CMAKE_CURRENT_SOURCE_DIR}/table2.h ${CMAKE_CURRENT_SOURCE_DIR}/text.h ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.h ${CMAKE_CURRENT_SOURCE_DIR}/to_string.h @@ -276,7 +218,6 @@ ${CMAKE_CURRENT_SOURCE_DIR}/binary.cpp ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.cpp ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp ${CMAKE_CURRENT_SOURCE_DIR}/extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libspirv.cpp @@ -293,6 +234,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/table2.cpp ${CMAKE_CURRENT_SOURCE_DIR}/text.cpp ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/to_string.cpp @@ -312,9 +254,12 @@ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_debug.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_decorations.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_derivatives.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_dot_product.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_execution_limitations.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_graph.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_group.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_id.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_image.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_interfaces.cpp @@ -322,12 +267,14 @@ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_layout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_literals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logicals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logical_pointers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory_semantics.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mesh_shading.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_misc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_pipe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_query.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing.cpp @@ -335,7 +282,9 @@ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_tensor_layout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_tensor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_invalid_type.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/construct.cpp @@ -349,6 +298,12 @@ ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.cpp) endif() +if (SPIRV_TOOLS_USE_MIMALLOC AND NOT SPIRV_TOOLS_BUILD_STATIC) + set(SPIRV_SOURCES + ${SPIRV_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/mimalloc.cpp) +endif() + # The software_version.cpp file includes build-version.inc. # Rebuild the software_version.cpp object file if it is older than # build-version.inc or whenever build-version.inc itself is out of @@ -379,12 +334,15 @@ ) set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries") spvtools_check_symbol_exports(${target}) - add_dependencies(${target} spirv-tools-build-version core_tables enum_string_mapping extinst_tables) + add_dependencies(${target} core_tables extinst_tables) endfunction() # Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and # unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility. add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES}) +if (SPIRV_TOOLS_USE_MIMALLOC) + target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static) +endif() spirv_tools_default_target_options(${SPIRV_TOOLS}-shared) set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden) target_compile_definitions(${SPIRV_TOOLS}-shared @@ -394,6 +352,9 @@ if(SPIRV_TOOLS_BUILD_STATIC) add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES}) + if (SPIRV_TOOLS_USE_MIMALLOC AND SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD) + target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static) + endif() spirv_tools_default_target_options(${SPIRV_TOOLS}-static) # The static target does not have the '-static' suffix. set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}") @@ -409,6 +370,9 @@ set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared) else() add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES}) + if (SPIRV_TOOLS_USE_MIMALLOC) + target_link_libraries(${SPIRV_TOOLS} PRIVATE mimalloc-static) + endif() spirv_tools_default_target_options(${SPIRV_TOOLS}) set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared) endif() @@ -417,12 +381,15 @@ find_library(LIBRT rt) if(LIBRT) foreach(target ${SPIRV_TOOLS_TARGETS}) - target_link_libraries(${target} rt) + target_link_libraries(${target} PUBLIC rt) endforeach() endif() endif() if(ENABLE_SPIRV_TOOLS_INSTALL) + if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV_TOOLS_TARGETS mimalloc-static) + endif() install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets) export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake) @@ -438,8 +405,3 @@ "endif()\n") install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) - -if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) - # Enable parallel builds across four cores for this lib - add_definitions(/MP4) -endif()
diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp index 0092d01..4886ee6 100644 --- a/source/assembly_grammar.cpp +++ b/source/assembly_grammar.cpp
@@ -23,6 +23,7 @@ #include "source/operand.h" #include "source/spirv_target_env.h" #include "source/table.h" +#include "source/table2.h" namespace spvtools { namespace { @@ -35,15 +36,12 @@ /// /// On success, the value is written to pValue. /// -/// @param[in] operandTable operand lookup table /// @param[in] type of the operand /// @param[in] textValue word of text to be parsed /// @param[out] pValue where the resulting value is written /// /// @return result code -spv_result_t spvTextParseMaskOperand(spv_target_env env, - const spv_operand_table operandTable, - const spv_operand_type_t type, +spv_result_t spvTextParseMaskOperand(const spv_operand_type_t type, const char* textValue, uint32_t* pValue) { if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT; size_t text_length = strlen(textValue); @@ -62,9 +60,9 @@ do { end = std::find(begin, text_end, separator); - spv_operand_desc entry = nullptr; - if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin, - end - begin, &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (auto error = + spvtools::LookupOperand(type, begin, end - begin, &entry)) { return error; } value |= entry->value; @@ -170,23 +168,20 @@ } // namespace -bool AssemblyGrammar::isValid() const { - return operandTable_ && opcodeTable_ && extInstTable_; -} - CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv( const spv::Capability* cap_array, uint32_t count) const { CapabilitySet cap_set; const auto version = spvVersionForTargetEnv(target_env_); for (uint32_t i = 0; i < count; ++i) { - spv_operand_desc entry = {}; - if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, - static_cast<uint32_t>(cap_array[i]), - &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (SPV_SUCCESS == + spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast<uint32_t>(cap_array[i]), &entry)) { // This token is visible in this environment if it's in an appropriate // core version, or it is enabled by a capability or an extension. if ((version >= entry->minVersion && version <= entry->lastVersion) || - entry->numExtensions > 0u || entry->numCapabilities > 0u) { + entry->extensions_range.count() > 0u || + entry->capabilities_range.count() > 0u) { cap_set.insert(cap_array[i]); } } @@ -194,28 +189,13 @@ return cap_set; } -spv_result_t AssemblyGrammar::lookupOpcode(const char* name, - spv_opcode_desc* desc) const { - return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc); -} - -spv_result_t AssemblyGrammar::lookupOpcode(spv::Op opcode, - spv_opcode_desc* desc) const { - return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc); -} - -spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, - const char* name, size_t name_len, - spv_operand_desc* desc) const { - return spvOperandTableNameLookup(target_env_, operandTable_, type, name, - name_len, desc); -} - -spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, - uint32_t operand, - spv_operand_desc* desc) const { - return spvOperandTableValueLookup(target_env_, operandTable_, type, operand, - desc); +const char* AssemblyGrammar::lookupOperandName(spv_operand_type_t type, + uint32_t operand) const { + const spvtools::OperandDesc* desc = nullptr; + if (spvtools::LookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) { + return "Unknown"; + } + return desc->name().data(); } spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name, @@ -245,25 +225,13 @@ spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type, const char* textValue, uint32_t* pValue) const { - return spvTextParseMaskOperand(target_env_, operandTable_, type, textValue, - pValue); -} -spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, - const char* textValue, - spv_ext_inst_desc* extInst) const { - return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst); -} - -spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, - uint32_t firstWord, - spv_ext_inst_desc* extInst) const { - return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst); + return spvTextParseMaskOperand(type, textValue, pValue); } void AssemblyGrammar::pushOperandTypesForMask( const spv_operand_type_t type, const uint32_t mask, spv_operand_pattern_t* pattern) const { - spvPushOperandTypesForMask(target_env_, operandTable_, type, mask, pattern); + spvPushOperandTypesForMask(type, mask, pattern); } } // namespace spvtools
diff --git a/source/assembly_grammar.h b/source/assembly_grammar.h index 36fdd08..08e10e7 100644 --- a/source/assembly_grammar.h +++ b/source/assembly_grammar.h
@@ -19,6 +19,7 @@ #include "source/latest_version_spirv_header.h" #include "source/operand.h" #include "source/table.h" +#include "source/util/span.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -28,54 +29,28 @@ class AssemblyGrammar { public: explicit AssemblyGrammar(const spv_const_context context) - : target_env_(context->target_env), - operandTable_(context->operand_table), - opcodeTable_(context->opcode_table), - extInstTable_(context->ext_inst_table) {} - - // Returns true if the internal tables have been initialized with valid data. - bool isValid() const; + : target_env_(context->target_env) {} // Returns the SPIR-V target environment. spv_target_env target_env() const { return target_env_; } // Removes capabilities not available in the current target environment and // returns the rest. + // TODO(crbug.com/266223071) Remove this. CapabilitySet filterCapsAgainstTargetEnv(const spv::Capability* cap_array, uint32_t count) const; - - // Fills in the desc parameter with the information about the opcode - // of the given name. Returns SPV_SUCCESS if the opcode was found, and - // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist. - spv_result_t lookupOpcode(const char* name, spv_opcode_desc* desc) const; - - // Fills in the desc parameter with the information about the opcode - // of the valid. Returns SPV_SUCCESS if the opcode was found, and - // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist. - spv_result_t lookupOpcode(spv::Op opcode, spv_opcode_desc* desc) const; - - // Fills in the desc parameter with the information about the given - // operand. Returns SPV_SUCCESS if the operand was found, and - // SPV_ERROR_INVALID_LOOKUP otherwise. - spv_result_t lookupOperand(spv_operand_type_t type, const char* name, - size_t name_len, spv_operand_desc* desc) const; - - // Fills in the desc parameter with the information about the given - // operand. Returns SPV_SUCCESS if the operand was found, and - // SPV_ERROR_INVALID_LOOKUP otherwise. - spv_result_t lookupOperand(spv_operand_type_t type, uint32_t operand, - spv_operand_desc* desc) const; + // Removes capabilities not available in the current target environment and + // returns the rest. + CapabilitySet filterCapsAgainstTargetEnv( + const spvtools::utils::Span<const spv::Capability>& caps) const { + return filterCapsAgainstTargetEnv(caps.begin(), + static_cast<uint32_t>(caps.size())); + } // Finds operand entry in the grammar table and returns its name. // Returns "Unknown" if not found. const char* lookupOperandName(spv_operand_type_t type, - uint32_t operand) const { - spv_operand_desc desc = nullptr; - if (lookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) { - return "Unknown"; - } - return desc->name; - } + uint32_t operand) const; // Finds the opcode for the given OpSpecConstantOp opcode name. The name // should not have the "Op" prefix. For example, "IAdd" corresponds to @@ -101,18 +76,6 @@ spv_result_t parseMaskOperand(const spv_operand_type_t type, const char* textValue, uint32_t* pValue) const; - // Writes the extended operand with the given type and text to the *extInst - // parameter. - // Returns SPV_SUCCESS if the value could be found. - spv_result_t lookupExtInst(spv_ext_inst_type_t type, const char* textValue, - spv_ext_inst_desc* extInst) const; - - // Writes the extended operand with the given type and first encoded word - // to the *extInst parameter. - // Returns SPV_SUCCESS if the value could be found. - spv_result_t lookupExtInst(spv_ext_inst_type_t type, uint32_t firstWord, - spv_ext_inst_desc* extInst) const; - // Inserts the operands expected after the given typed mask onto the end // of the given pattern. // @@ -129,9 +92,6 @@ private: const spv_target_env target_env_; - const spv_operand_table operandTable_; - const spv_opcode_table opcodeTable_; - const spv_ext_inst_table extInstTable_; }; } // namespace spvtools
diff --git a/source/binary.cpp b/source/binary.cpp index ed57498..01ab65c 100644 --- a/source/binary.cpp +++ b/source/binary.cpp
@@ -33,6 +33,7 @@ #include "source/operand.h" #include "source/spirv_constant.h" #include "source/spirv_endian.h" +#include "source/table2.h" #include "source/util/string_utils.h" spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, @@ -94,6 +95,13 @@ spv_result_t parse(const uint32_t* words, size_t num_words, spv_diagnostic* diagnostic); + // Sets whether to handle, rather than reject, unrecognized content: + // unknown opcodes, unknown extended instruction numbers in semantic sets, + // and known opcodes with unknown enum operands. When set, unknown + // instructions are re-emitted as raw OpUnknown data instead of returning + // an error. + void SetHandleUnknownOpcodes(bool value) { handle_unknown_opcodes_ = value; } + private: // All remaining methods work on the current module parse state. @@ -184,11 +192,15 @@ const spv_parsed_header_fn_t parsed_header_fn_; // Parsed header callback const spv_parsed_instruction_fn_t parsed_instruction_fn_; // Parsed instruction callback + // When true, unrecognized opcodes, ext inst numbers, and enum operands are + // passed to the callback as raw OpUnknown data instead of returning an error. + bool handle_unknown_opcodes_ = false; // Describes the format of a typed literal number. struct NumberType { spv_number_kind_t type; uint32_t bit_width; + spv_fp_encoding_t encoding; }; // The state used to parse a single SPIR-V binary module. @@ -218,6 +230,11 @@ // Is the SPIR-V binary in a different endianness from the host native // endianness? bool requires_endian_conversion; + // Set by parseOperand when LookupOperand fails for an enum operand and + // handle_unknown_opcodes_ is set. Signals parseInstruction to discard + // the partially-decoded instruction and re-emit it as raw OpUnknown data. + // Cleared by parseInstruction immediately before calling emitAsUnknown. + bool retry_instruction_as_unknown_ = false; // Maps a result ID to its type ID. By convention: // - a result ID that is a type definition maps to itself. @@ -317,15 +334,50 @@ return diagnostic() << "Invalid instruction word count: " << inst_word_count; } - spv_opcode_desc opcode_desc; - if (grammar_.lookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc)) + const spvtools::InstructionDesc* opcode_desc = nullptr; + const bool opcode_known = + spvtools::LookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc) == + SPV_SUCCESS; + if (!opcode_known && !handle_unknown_opcodes_) return diagnostic() << "Invalid opcode: " << inst.opcode; - // Advance past the opcode word. But remember the of the start - // of the instruction. + // Advance past the opcode word. But remember the start of the instruction. const size_t inst_offset = _.word_index; _.word_index++; + // Emits the instruction at inst_offset as raw data with no decoded operands. + auto emitAsUnknown = [&]() -> spv_result_t { + if (inst_offset + inst_word_count > _.num_words) { + return diagnostic() << "Truncated binary: instruction at word " + << inst_offset << " claims " << inst_word_count + << " words but binary ends at " << _.num_words; + } + // Repopulate endian_converted_words from scratch. The operand loop may + // have partially filled it before the unknown enum was detected. + _.endian_converted_words.clear(); + _.endian_converted_words.push_back(first_word); + if (_.requires_endian_conversion) { + for (uint16_t i = 1; i < inst_word_count; i++) { + _.endian_converted_words.push_back(peekAt(inst_offset + i)); + } + } + _.word_index = inst_offset + inst_word_count; + inst.words = _.requires_endian_conversion ? _.endian_converted_words.data() + : _.words + inst_offset; + inst.num_words = inst_word_count; + _.operands.clear(); + inst.operands = _.operands.data(); + inst.num_operands = 0; + if (parsed_instruction_fn_) { + if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error; + } + return SPV_SUCCESS; + }; + + if (!opcode_known) { + return emitAsUnknown(); + } + // Maintains the ordered list of expected operand types. // For many instructions we only need the {numTypes, operandTypes} // entries in opcode_desc. However, sometimes we need to modify @@ -334,16 +386,15 @@ // ExecutionMode), or for extended instructions that may have their // own operands depending on the selected extended instruction. _.expected_operands.clear(); - for (auto i = 0; i < opcode_desc->numTypes; i++) - _.expected_operands.push_back( - opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]); + + spvPushOperandTypes(opcode_desc->operands(), &_.expected_operands); while (_.word_index < inst_offset + inst_word_count) { const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset); if (_.expected_operands.empty()) { - return diagnostic() << "Invalid instruction Op" << opcode_desc->name - << " starting at word " << inst_offset - << ": expected no more operands after " + return diagnostic() << "Invalid instruction Op" + << opcode_desc->name().data() << " starting at word " + << inst_offset << ": expected no more operands after " << inst_word_index << " words, but stated word count is " << inst_word_count << "."; @@ -355,6 +406,10 @@ if (auto error = parseOperand(inst_offset, &inst, type, &_.endian_converted_words, &_.operands, &_.expected_operands)) { + if (_.retry_instruction_as_unknown_) { + _.retry_instruction_as_unknown_ = false; + return emitAsUnknown(); + } return error; } } @@ -362,15 +417,15 @@ if (!_.expected_operands.empty() && !spvOperandIsOptional(_.expected_operands.back())) { return diagnostic() << "End of input reached while decoding Op" - << opcode_desc->name << " starting at word " + << opcode_desc->name().data() << " starting at word " << inst_offset << ": expected more operands after " << inst_word_count << " words."; } if ((inst_offset + inst_word_count) != _.word_index) { - return diagnostic() << "Invalid word count: Op" << opcode_desc->name - << " starting at word " << inst_offset - << " says it has " << inst_word_count + return diagnostic() << "Invalid word count: Op" + << opcode_desc->name().data() << " starting at word " + << inst_offset << " says it has " << inst_word_count << " words, but found " << _.word_index - inst_offset << " words instead."; } @@ -385,8 +440,6 @@ assert(_.requires_endian_conversion || (_.endian_converted_words.size() == 1)); - recordNumberType(inst_offset, &inst); - if (_.requires_endian_conversion) { // We must wait until here to set this pointer, because the vector might // have been be resized while we accumulated its elements. @@ -398,6 +451,8 @@ } inst.num_words = inst_word_count; + recordNumberType(inst_offset, &inst); + // We must wait until here to set this pointer, because the vector might // have been be resized while we accumulated its elements. inst.operands = _.operands.data(); @@ -496,22 +551,33 @@ case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { assert(spvIsExtendedInstruction(opcode)); assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE); - spv_ext_inst_desc ext_inst; - if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) == + + const spvtools::ExtInstDesc* desc = nullptr; + if (spvtools::LookupExtInst(inst->ext_inst_type, word, &desc) == SPV_SUCCESS) { - // if we know about this ext inst, push the expected operands - spvPushOperandTypes(ext_inst->operandTypes, expected_operands); - } else { - // if we don't know this extended instruction and the set isn't - // non-semantic, we cannot process further - if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) { - return diagnostic() - << "Invalid extended instruction number: " << word; - } else { - // for non-semantic instruction sets, we know the form of all such - // extended instructions contains a series of IDs as parameters + // Push VARIABLE_ID so extra trailing operands from future NSDI + // versions are silently absorbed after the instruction-specific ones. + if (spvExtInstIsNonSemantic(inst->ext_inst_type)) { expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); } + + // if we know about this ext inst, push the expected operands + spvPushOperandTypes(desc->operands(), expected_operands); + } else { + // If we don't know this extended instruction and the set is semantic, + // fail unless handle_unknown_opcodes_ is set. For non-semantic sets, + // always continue regardless of the flag. In both non-error cases the + // remaining operands are exposed as variable IDs. For non-semantic + // sets the disassembler emits the instruction via its normal operand + // loop; for semantic sets with handle_unknown_opcodes_ set, the + // disassembler independently detects the unknown number via + // LookupExtInst and emits the entire instruction as OpUnknown. + if (!spvExtInstIsNonSemantic(inst->ext_inst_type) && + !handle_unknown_opcodes_) { + return diagnostic() + << "Invalid extended instruction number: " << word; + } + expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); } } break; @@ -522,8 +588,8 @@ return diagnostic() << "Invalid " << spvOperandTypeStr(type) << ": " << word; } - spv_opcode_desc opcode_entry = nullptr; - if (grammar_.lookupOpcode(spv::Op(word), &opcode_entry)) { + const spvtools::InstructionDesc* opcode_entry = nullptr; + if (spvtools::LookupOpcode(spv::Op(word), &opcode_entry)) { return diagnostic(SPV_ERROR_INTERNAL) << "OpSpecConstant opcode table out of sync"; } @@ -532,8 +598,9 @@ // operants for the opcode. assert(opcode_entry->hasType); assert(opcode_entry->hasResult); - assert(opcode_entry->numTypes >= 2); - spvPushOperandTypes(opcode_entry->operandTypes + 2, expected_operands); + assert(opcode_entry->operands().size() >= 2); + spvPushOperandTypes(opcode_entry->operands().subspan(2), + expected_operands); } break; case SPV_OPERAND_TYPE_LITERAL_INTEGER: @@ -633,6 +700,7 @@ } break; case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: case SPV_OPERAND_TYPE_EXECUTION_MODEL: case SPV_OPERAND_TYPE_ADDRESSING_MODEL: case SPV_OPERAND_TYPE_MEMORY_MODEL: @@ -686,20 +754,24 @@ parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT; if (type == SPV_OPERAND_TYPE_OPTIONAL_FPENCODING) parsed_operand.type = SPV_OPERAND_TYPE_FPENCODING; + if (type == SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY) + parsed_operand.type = SPV_OPERAND_TYPE_CAPABILITY; - spv_operand_desc entry; - if (grammar_.lookupOperand(type, word, &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(type, word, &entry)) { + if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true; return diagnostic() << "Invalid " << spvOperandTypeStr(parsed_operand.type) << " operand: " << word; } // Prepare to accept operands to this operand, if needed. - spvPushOperandTypes(entry->operandTypes, expected_operands); + spvPushOperandTypes(entry->operands(), expected_operands); } break; case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: { - spv_operand_desc entry; - if (grammar_.lookupOperand(type, word, &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(type, word, &entry)) { + if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true; return diagnostic() << "Invalid " << spvOperandTypeStr(parsed_operand.type) << " operand: " << word @@ -709,7 +781,7 @@ "SPIRV-Headers"; } // Prepare to accept operands to this operand, if needed. - spvPushOperandTypes(entry->operandTypes, expected_operands); + spvPushOperandTypes(entry->operands(), expected_operands); } break; case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: @@ -718,6 +790,8 @@ case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_TENSOR_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: @@ -726,7 +800,9 @@ case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: - case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: { + case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: + case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: { // This operand is a mask. // Map an optional operand type to its corresponding concrete type. @@ -738,6 +814,11 @@ parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS; if (type == SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS) parsed_operand.type = SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS; + if (type == SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS) + parsed_operand.type = + SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS; + if (type == SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS) + parsed_operand.type = SPV_OPERAND_TYPE_TENSOR_OPERANDS; // Check validity of set mask bits. Also prepare for operands for those // masks if they have any. To get operand order correct, scan from @@ -748,23 +829,24 @@ uint32_t remaining_word = word; for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) { if (remaining_word & mask) { - spv_operand_desc entry; - if (grammar_.lookupOperand(type, mask, &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(type, mask, &entry)) { + if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true; return diagnostic() << "Invalid " << spvOperandTypeStr(parsed_operand.type) << " operand: " << word << " has invalid mask component " << mask; } remaining_word ^= mask; - spvPushOperandTypes(entry->operandTypes, expected_operands); + spvPushOperandTypes(entry->operands(), expected_operands); } } if (word == 0) { // An all-zeroes mask *might* also be valid. - spv_operand_desc entry; - if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(type, 0, &entry)) { // Prepare for its operands, if any. - spvPushOperandTypes(entry->operandTypes, expected_operands); + spvPushOperandTypes(entry->operands(), expected_operands); } } } break; @@ -822,6 +904,7 @@ parsed_operand->number_kind = info.type; parsed_operand->number_bit_width = info.bit_width; + parsed_operand->fp_encoding = info.encoding; // Round up the word count. parsed_operand->num_words = static_cast<uint16_t>((info.bit_width + 31) / 32); return SPV_SUCCESS; @@ -839,6 +922,17 @@ } else if (spv::Op::OpTypeFloat == opcode) { info.type = SPV_NUMBER_FLOATING; info.bit_width = peekAt(inst_offset + 2); + if (inst->num_words >= 4) { + const spvtools::OperandDesc* desc = nullptr; + spv_result_t status = spvtools::LookupOperand( + SPV_OPERAND_TYPE_FPENCODING, peekAt(inst_offset + 3), &desc); + if (status == SPV_SUCCESS) { + info.encoding = spvFPEncodingFromOperandFPEncoding( + static_cast<spv::FPEncoding>(desc->value)); + } else { + info.encoding = SPV_FP_ENCODING_UNKNOWN; + } + } } // The *result* Id of a type generating instruction is the type Id. _.type_id_to_number_type_info[inst->result_id] = info; @@ -852,12 +946,25 @@ spv_parsed_header_fn_t parsed_header, spv_parsed_instruction_fn_t parsed_instruction, spv_diagnostic* diagnostic) { + return spvBinaryParseWithOptions(context, user_data, code, num_words, + parsed_header, parsed_instruction, + diagnostic, 0); +} + +spv_result_t spvBinaryParseWithOptions( + const spv_const_context context, void* user_data, const uint32_t* code, + const size_t num_words, spv_parsed_header_fn_t parsed_header, + spv_parsed_instruction_fn_t parsed_instruction, spv_diagnostic* diagnostic, + uint32_t options) { spv_context_t hijack_context = *context; if (diagnostic) { *diagnostic = nullptr; spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic); } Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction); + if (options & SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES) { + parser.SetHandleUnknownOpcodes(true); + } return parser.parse(code, num_words, diagnostic); }
diff --git a/source/binary.h b/source/binary.h index eb3beac..7dbb9a8 100644 --- a/source/binary.h +++ b/source/binary.h
@@ -35,6 +35,14 @@ // replacement for C11's strnlen_s which might not exist in all environments. size_t spv_strnlen_s(const char* str, size_t strsz); +// Like spvBinaryParse, but accepts a bitmask of spv_binary_to_text_options_t +// flags to control parser behavior during disassembly. +spv_result_t spvBinaryParseWithOptions( + const spv_const_context context, void* user_data, const uint32_t* code, + const size_t num_words, spv_parsed_header_fn_t parsed_header, + spv_parsed_instruction_fn_t parsed_instruction, spv_diagnostic* diagnostic, + uint32_t options); + // Decode the string literal operand with index operand_index from instruction // inst. std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst,
diff --git a/source/cfa.h b/source/cfa.h index 9ae3e39..dc8b9ba 100644 --- a/source/cfa.h +++ b/source/cfa.h
@@ -15,6 +15,8 @@ #ifndef SOURCE_CFA_H_ #define SOURCE_CFA_H_ +#include <stddef.h> + #include <algorithm> #include <cassert> #include <cstdint>
diff --git a/source/common_debug_info.h b/source/common_debug_info.h index ffa5d34..66862d2 100644 --- a/source/common_debug_info.h +++ b/source/common_debug_info.h
@@ -18,7 +18,7 @@ #define SOURCE_COMMON_DEBUG_INFO_HEADER_H_ // This enum defines the known common set of instructions that are the same -// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100. +// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo. // Note that NonSemantic.Shader.* instructions can still have slightly // different encoding, as it does not use literals anywhere and only constants. enum CommonDebugInfoInstructions {
diff --git a/source/diff/CMakeLists.txt b/source/diff/CMakeLists.txt index 52f18f2..2bd6106 100644 --- a/source/diff/CMakeLists.txt +++ b/source/diff/CMakeLists.txt
@@ -18,8 +18,16 @@ diff.cpp ) +if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV_TOOLS_DIFF_SOURCES ${spirv-tools_SOURCE_DIR}/source/mimalloc.cpp) +endif() + add_library(SPIRV-Tools-diff ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_DIFF_SOURCES}) +if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + target_link_libraries(SPIRV-Tools-diff PRIVATE mimalloc-static) +endif() + spvtools_default_compile_options(SPIRV-Tools-diff) target_include_directories(SPIRV-Tools-diff PUBLIC @@ -39,7 +47,13 @@ spvtools_check_symbol_exports(SPIRV-Tools-diff) if(ENABLE_SPIRV_TOOLS_INSTALL) - install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets) + set(SPIRV-Tools-diff-InstallTargets SPIRV-Tools-diff) + + if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV-Tools-diff-InstallTargets mimalloc-static) + endif() + + install(TARGETS ${SPIRV-Tools-diff-InstallTargets} EXPORT SPIRV-Tools-diffTargets) export(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake) spvtools_config_package_dir(SPIRV-Tools-diff PACKAGE_DIR)
diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp index 6269af5..d548aea 100644 --- a/source/diff/diff.cpp +++ b/source/diff/diff.cpp
@@ -67,7 +67,9 @@ ids.resize(write_index); } -// A mapping between src and dst ids. +// A mapping from ids in one module to ids in the other. +// +// Differ contains two of these, for src->dst and dst->src. class IdMap { public: IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); } @@ -190,6 +192,7 @@ IdMap dst_to_src_; }; +// Mappings from ids to instructions and metadata, for a single module's ids. struct IdInstructions { IdInstructions(const opt::Module* module) : inst_map_(module->IdBound(), nullptr), @@ -198,6 +201,10 @@ forward_pointer_map_(module->IdBound()) { // Map ids from all sections to instructions that define them. MapIdsToInstruction(module->ext_inst_imports()); + MapIdsToInstruction(module->debugs1()); + MapIdsToInstruction(module->debugs2()); + MapIdsToInstruction(module->debugs3()); + MapIdsToInstruction(module->ext_inst_debuginfo()); MapIdsToInstruction(module->types_values()); for (const opt::Function& function : *module) { function.ForEachInst( @@ -321,6 +328,8 @@ // Get various properties from an id. These Helper functions are passed to // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument). uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id); + uint32_t GroupIdsHelperGetFunctionTypeId(const IdInstructions& id_to, + uint32_t id); spv::StorageClass GroupIdsHelperGetTypePointerStorageClass( const IdInstructions& id_to, uint32_t id); spv::Op GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, @@ -883,6 +892,17 @@ return GetInst(id_to, id)->type_id(); } +// Return an `OpFunction` instruction's full `OpTypeFunction` type, +// which includes parameter types. +// +// `GroupIdsHelperGetTypeId` applied to an `OpFunction` only gets the +// function's return type, so this is a slightly more precise way to +// match up functions by signature. +uint32_t Differ::GroupIdsHelperGetFunctionTypeId(const IdInstructions& id_to, + uint32_t id) { + return GetInst(id_to, id)->GetSingleWordOperand(3); +} + spv::StorageClass Differ::GroupIdsHelperGetTypePointerStorageClass( const IdInstructions& id_to, uint32_t id) { const opt::Instruction* inst = GetInst(id_to, id); @@ -902,6 +922,24 @@ return type_inst->opcode(); } +// Group unmatched ids in `ids` according to some characteristic, +// determined by `get_group`. +// +// Using `get_group` to compute some sort of key for each id, set +// `groups` to map each key to all the ids that have that key. +// +// For example, to group ids by name, pass `Differ::GetName` as +// `get_group`. This will fill `groups` with a map from each name to +// all the ids with that name. +// +// Under the assumption that we're trying to establish new pairings, +// ids that are already paired are omitted from `groups`. +// +// The `is_src` parameter indicates whether `ids` are drawn from the +// source module or the destination module. +// +// The template parameter `T` is the key type, like `std::string` or +// `uint32_t`. template <typename T> void Differ::GroupIds(const IdGroup& ids, bool is_src, std::map<T, IdGroup>* groups, @@ -924,6 +962,10 @@ } } +// Group `src_ids` and `dst_ids` according to `get_group`, and then use +// `match_group` to pair up ids in corresponding groups. +// +// Don't try to pair ids in groups whose key is `invalid_group_key`. template <typename T> void Differ::GroupIdsAndMatch( const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, @@ -1177,6 +1219,7 @@ case spv::Op::OpMemberDecorate: return DoOperandsMatch(src_inst, dst_inst, 0, 3); case spv::Op::OpExtInst: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); case spv::Op::OpDecorationGroup: case spv::Op::OpGroupDecorate: case spv::Op::OpGroupMemberDecorate: @@ -2483,7 +2526,7 @@ // If there are multiple functions with the same name, group them by // type, and match only if the types match (and are unique). - GroupIdsAndMatch<uint32_t>(src_group, dst_group, 0, + GroupIdsAndMatchByMappedId(src_group, dst_group, &Differ::GroupIdsHelperGetTypeId, [this](const IdGroup& src_group_by_type_id, const IdGroup& dst_group_by_type_id) { @@ -2526,9 +2569,19 @@ dst_match_result, 0); } - // Best effort match functions with matching type. - GroupIdsAndMatch<uint32_t>( - src_func_ids, dst_func_ids, 0, &Differ::GroupIdsHelperGetTypeId, + // Best effort match functions with matching return and argument types. + GroupIdsAndMatchByMappedId( + src_func_ids, dst_func_ids, &Differ::GroupIdsHelperGetFunctionTypeId, + [this](const IdGroup& src_group_by_func_type_id, + const IdGroup& dst_group_by_func_type_id) { + BestEffortMatchFunctions(src_group_by_func_type_id, + dst_group_by_func_type_id, src_func_insts_, + dst_func_insts_); + }); + + // Best effort match functions with matching return types. + GroupIdsAndMatchByMappedId( + src_func_ids, dst_func_ids, &Differ::GroupIdsHelperGetTypeId, [this](const IdGroup& src_group_by_type_id, const IdGroup& dst_group_by_type_id) { BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id, @@ -2560,6 +2613,9 @@ // This section includes OpExtInst for DebugInfo extension MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(), dst_->ext_inst_debuginfo()); + // OpExtInst can exist in other sections too, such as with non-semantic info. + MatchDebugAndAnnotationInstructions(src_->types_values(), + dst_->types_values()); } void Differ::MatchAnnotations() { @@ -2746,30 +2802,7 @@ src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr); dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr); - const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; - spv_opcode_table opcode_table; - spv_operand_table operand_table; - spv_ext_inst_table ext_inst_table; - spv_result_t result; - - result = spvOpcodeTableGet(&opcode_table, target_env); - if (result != SPV_SUCCESS) return result; - - result = spvOperandTableGet(&operand_table, target_env); - if (result != SPV_SUCCESS) return result; - - result = spvExtInstTableGet(&ext_inst_table, target_env); - if (result != SPV_SUCCESS) return result; - - spv_context_t context{ - target_env, - opcode_table, - operand_table, - ext_inst_table, - }; - - const AssemblyGrammar grammar(&context); - if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + spv_context_t context{SPV_ENV_UNIVERSAL_1_6, nullptr}; uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT; if (options_.indent) { @@ -2777,7 +2810,7 @@ } NameMapper name_mapper = GetTrivialNameMapper(); - disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options, + disassemble::InstructionDisassembler dis(out_, disassembly_options, name_mapper); if (!options_.no_header) {
diff --git a/source/disassemble.cpp b/source/disassemble.cpp index 93791a0..b85958e 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp
@@ -31,7 +31,6 @@ #include <unordered_map> #include <utility> -#include "source/assembly_grammar.h" #include "source/binary.h" #include "source/diagnostic.h" #include "source/ext_inst.h" @@ -40,6 +39,7 @@ #include "source/print.h" #include "source/spirv_constant.h" #include "source/spirv_endian.h" +#include "source/table2.h" #include "source/util/hex_float.h" #include "source/util/make_unique.h" #include "spirv-tools/libspirv.h" @@ -115,8 +115,7 @@ // representation. class Disassembler { public: - Disassembler(const AssemblyGrammar& grammar, uint32_t options, - NameMapper name_mapper) + Disassembler(uint32_t options, NameMapper name_mapper) : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), nested_indent_( spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NESTED_INDENT, options)), @@ -124,7 +123,7 @@ spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_REORDER_BLOCKS, options)), text_(), out_(print_ ? out_stream() : out_stream(text_)), - instruction_disassembler_(grammar, out_.get(), options, name_mapper), + instruction_disassembler_(out_.get(), options, name_mapper), header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)), byte_offset_(0) {} @@ -624,12 +623,10 @@ } // namespace namespace disassemble { -InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar, - std::ostream& stream, +InstructionDisassembler::InstructionDisassembler(std::ostream& stream, uint32_t options, NameMapper name_mapper) - : grammar_(grammar), - stream_(stream), + : stream_(stream), print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) @@ -640,6 +637,8 @@ comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), show_byte_offset_( spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), + handle_unknown_opcodes_(spvIsInBitfield( + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, options)), name_mapper_(std::move(name_mapper)), last_instruction_comment_alignment_(0) {} @@ -691,18 +690,59 @@ // first so its length can be readily available. std::ostringstream line; + if (handle_unknown_opcodes_) { + const InstructionDesc* opcode_desc = nullptr; + bool needs_raw_emit = LookupOpcode(opcode, &opcode_desc) != SPV_SUCCESS; + + // Also check for an unknown extended instruction number in a semantic + // extended instruction set. The ext_inst_type is set by the parser even + // when the instruction number itself is unknown. + if (!needs_raw_emit && opcode == spv::Op::OpExtInst && + inst.ext_inst_type != SPV_EXT_INST_TYPE_NONE && + !spvExtInstIsNonSemantic(inst.ext_inst_type) && inst.num_words >= 5) { + const ExtInstDesc* ext_desc = nullptr; + needs_raw_emit = LookupExtInst(inst.ext_inst_type, inst.words[4], + &ext_desc) != SPV_SUCCESS; + } + + // Also check for a known opcode whose operands were not decoded because + // one of its enum operands had an unknown value. The parser signals this + // by setting num_operands = 0 while leaving num_words intact. + // Legitimately zero-operand instructions (OpNop, OpReturn, etc.) have an + // empty grammar operand list, so this check does not trigger for them. + if (!needs_raw_emit && opcode_desc != nullptr && + !opcode_desc->operands().empty() && inst.num_operands == 0) { + needs_raw_emit = true; + } + + if (needs_raw_emit) { + line << std::string(indent_, ' '); + line << "OpUnknown(" << inst.opcode << ", " << inst.num_words << ")"; + for (uint16_t i = 1; i < inst.num_words; i++) { + line << " " << inst.words[i]; + } + // Warn that the ID bound in the reassembled module may be incorrect + // if this instruction defines a result ID, because the assembler does + // not track integers inside OpUnknown as ID assignments. + line << " ; note: ID bound may be incorrect after reassembly"; + stream_ << line.str() << "\n"; + last_instruction_comment_alignment_ = 0; + return; + } + } + if (nested_indent_ && opcode == spv::Op::OpLabel) { // Separate the blocks by an empty line to make them easier to separate stream_ << std::endl; } if (inst.result_id) { - SetBlue(); + SetBlue(line); const std::string id_name = name_mapper_(inst.result_id); if (indent_) line << std::setw(std::max(0, indent_ - 3 - int(id_name.size()))); line << "%" << id_name; - ResetColor(); + ResetColor(line); line << " = "; } else { line << std::string(indent_, ' '); @@ -771,7 +811,7 @@ {line_length + 2, last_instruction_comment_alignment_, kCommentColumn}); // Round up the alignment to a multiple of 4 for more niceness. align = (align + 3) & ~0x3u; - last_instruction_comment_alignment_ = align; + last_instruction_comment_alignment_ = std::min({align, 256u}); stream_ << std::string(align - line_length, ' ') << "; " << comments.str(); } else { @@ -870,11 +910,10 @@ stream << "%" << name_mapper_(word); break; case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { - spv_ext_inst_desc ext_inst; SetRed(stream); - if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) == - SPV_SUCCESS) { - stream << ext_inst->name; + const ExtInstDesc* desc = nullptr; + if (LookupExtInst(inst.ext_inst_type, word, &desc) == SPV_SUCCESS) { + stream << desc->name().data(); } else { if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) { assert(false && "should have caught this earlier"); @@ -885,11 +924,11 @@ } } break; case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { - spv_opcode_desc opcode_desc; - if (grammar_.lookupOpcode(spv::Op(word), &opcode_desc)) + const spvtools::InstructionDesc* opcodeEntry = nullptr; + if (LookupOpcode(spv::Op(word), &opcodeEntry)) assert(false && "should have caught this earlier"); SetRed(stream); - stream << opcode_desc->name; + stream << opcodeEntry->name().data(); } break; case SPV_OPERAND_TYPE_LITERAL_INTEGER: case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: @@ -911,6 +950,7 @@ stream << '"'; } break; case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: case SPV_OPERAND_TYPE_EXECUTION_MODEL: case SPV_OPERAND_TYPE_ADDRESSING_MODEL: @@ -948,10 +988,10 @@ case SPV_OPERAND_TYPE_QUANTIZATION_MODES: case SPV_OPERAND_TYPE_FPENCODING: case SPV_OPERAND_TYPE_OVERFLOW_MODES: { - spv_operand_desc entry; - if (grammar_.lookupOperand(operand.type, word, &entry)) + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(operand.type, word, &entry)) assert(false && "should have caught this earlier"); - stream << entry->name; + stream << entry->name().data(); } break; case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: case SPV_OPERAND_TYPE_FUNCTION_CONTROL: @@ -968,10 +1008,10 @@ if (spvOperandIsConcreteMask(operand.type)) { EmitMaskOperand(stream, operand.type, word); } else if (spvOperandIsConcrete(operand.type)) { - spv_operand_desc entry; - if (grammar_.lookupOperand(operand.type, word, &entry)) + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(operand.type, word, &entry)) assert(false && "should have caught this earlier"); - stream << entry->name; + stream << entry->name().data(); } else { assert(false && "unhandled or invalid case"); } @@ -991,20 +1031,20 @@ for (mask = 1; remaining_word; mask <<= 1) { if (remaining_word & mask) { remaining_word ^= mask; - spv_operand_desc entry; - if (grammar_.lookupOperand(type, mask, &entry)) + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(type, mask, &entry)) assert(false && "should have caught this earlier"); if (num_emitted) stream << "|"; - stream << entry->name; + stream << entry->name().data(); num_emitted++; } } if (!num_emitted) { // An operand value of 0 was provided, so represent it by the name // of the 0 value. In many cases, that's "None". - spv_operand_desc entry; - if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) - stream << entry->name; + const spvtools::OperandDesc* entry = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(type, 0, &entry)) + stream << entry->name().data(); } } @@ -1042,25 +1082,22 @@ const size_t wordCount, const uint32_t options) { spv_context context = spvContextCreate(env); - const AssemblyGrammar grammar(context); - if (!grammar.isValid()) { - spvContextDestroy(context); - return ""; - } // Generate friendly names for Ids if requested. std::unique_ptr<FriendlyNameMapper> friendly_mapper; NameMapper name_mapper = GetTrivialNameMapper(); if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { - friendly_mapper = MakeUnique<FriendlyNameMapper>(context, code, wordCount); + friendly_mapper = + MakeUnique<FriendlyNameMapper>(context, code, wordCount, options); name_mapper = friendly_mapper->GetNameMapper(); } // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); + Disassembler disassembler(options, name_mapper); WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); - spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, - DisassembleTargetInstruction, nullptr); + spvBinaryParseWithOptions(context, &wrapped, code, wordCount, + DisassembleTargetHeader, + DisassembleTargetInstruction, nullptr, options); spv_text text = nullptr; std::string output; @@ -1086,24 +1123,21 @@ spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); } - const spvtools::AssemblyGrammar grammar(&hijack_context); - if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; - // Generate friendly names for Ids if requested. std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper; spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { friendly_mapper = spvtools::MakeUnique<spvtools::FriendlyNameMapper>( - &hijack_context, code, wordCount); + &hijack_context, code, wordCount, options); name_mapper = friendly_mapper->GetNameMapper(); } // Now disassemble! - spvtools::Disassembler disassembler(grammar, options, name_mapper); - if (auto error = - spvBinaryParse(&hijack_context, &disassembler, code, wordCount, - spvtools::DisassembleHeader, - spvtools::DisassembleInstruction, pDiagnostic)) { + spvtools::Disassembler disassembler(options, name_mapper); + if (auto error = spvBinaryParseWithOptions( + &hijack_context, &disassembler, code, wordCount, + spvtools::DisassembleHeader, spvtools::DisassembleInstruction, + pDiagnostic, options)) { return error; }
diff --git a/source/disassemble.h b/source/disassemble.h index b6d13c6..7165a04 100644 --- a/source/disassemble.h +++ b/source/disassemble.h
@@ -37,7 +37,6 @@ const size_t word_count, const uint32_t options); -class AssemblyGrammar; namespace disassemble { // Shared code with other tools (than the disassembler) that might need to @@ -45,8 +44,8 @@ // binary for an instruction to its assembly representation. class InstructionDisassembler { public: - InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream, - uint32_t options, NameMapper name_mapper); + InstructionDisassembler(std::ostream& stream, uint32_t options, + NameMapper name_mapper); // Emits the assembly header for the module. void EmitHeaderSpirv(); @@ -104,7 +103,6 @@ // |id_comments_|. void GenerateCommentForDecoratedId(const spv_parsed_instruction_t& inst); - const spvtools::AssemblyGrammar& grammar_; std::ostream& stream_; const bool print_; // Should we also print to the standard output stream? const bool color_; // Should we print in colour? @@ -112,6 +110,9 @@ const bool nested_indent_; // Whether indentation should indicate nesting const int comment_; // Should we comment the source const bool show_byte_offset_; // Should we print byte offset, in hex? + // Whether to emit unknown opcodes and unknown extended instructions as + // OpUnknown with raw integer operands rather than failing. + const bool handle_unknown_opcodes_; spvtools::NameMapper name_mapper_; // Some comments are generated as instructions (such as OpDecorate) are
diff --git a/source/enum_set.h b/source/enum_set.h index a375138..340f5b9 100644 --- a/source/enum_set.h +++ b/source/enum_set.h
@@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <stddef.h> + #include <algorithm> #include <cassert> #include <cstdint> #include <functional> #include <initializer_list> +#include <iterator> #include <limits> #include <type_traits> #include <vector>
diff --git a/source/enum_string_mapping.h b/source/enum_string_mapping.h deleted file mode 100644 index b136584..0000000 --- a/source/enum_string_mapping.h +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright (c) 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef SOURCE_ENUM_STRING_MAPPING_H_ -#define SOURCE_ENUM_STRING_MAPPING_H_ - -#include <string> - -#include "source/extensions.h" -#include "source/latest_version_spirv_header.h" - -namespace spvtools { - -// Finds Extension enum corresponding to |str|. Returns false if not found. -bool GetExtensionFromString(const char* str, Extension* extension); - -// Returns text string corresponding to |extension|. -const char* ExtensionToString(Extension extension); - -// Returns text string corresponding to |capability|. -const char* CapabilityToString(spv::Capability capability); - -} // namespace spvtools - -#endif // SOURCE_ENUM_STRING_MAPPING_H_
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index f2ff63f..34daf7a 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp
@@ -17,98 +17,7 @@ #include <cstring> // DebugInfo extended instruction set. -// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html -// TODO(dneto): DebugInfo.h should probably move to SPIRV-Headers. -#include "DebugInfo.h" - -#include "source/latest_version_glsl_std_450_header.h" -#include "source/latest_version_opencl_std_header.h" -#include "source/macro.h" -#include "source/spirv_definition.h" - -#include "debuginfo.insts.inc" -#include "glsl.std.450.insts.inc" -#include "nonsemantic.clspvreflection.insts.inc" -#include "nonsemantic.shader.debuginfo.100.insts.inc" -#include "nonsemantic.vkspreflection.insts.inc" -#include "opencl.debuginfo.100.insts.inc" -#include "opencl.std.insts.inc" - -#include "spirv-tools/libspirv.h" -#include "spv-amd-gcn-shader.insts.inc" -#include "spv-amd-shader-ballot.insts.inc" -#include "spv-amd-shader-explicit-vertex-parameter.insts.inc" -#include "spv-amd-shader-trinary-minmax.insts.inc" - -static const spv_ext_inst_group_t kGroups_1_0[] = { - {SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glsl_entries), glsl_entries}, - {SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(opencl_entries), opencl_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, - ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries), - spv_amd_shader_explicit_vertex_parameter_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, - ARRAY_SIZE(spv_amd_shader_trinary_minmax_entries), - spv_amd_shader_trinary_minmax_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, - ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries}, - {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, - ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries}, - {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries), - debuginfo_entries}, - {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, - ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, - {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}, - {SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, - ARRAY_SIZE(nonsemantic_vkspreflection_entries), - nonsemantic_vkspreflection_entries}, -}; - -static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0), - kGroups_1_0}; - -spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, - spv_target_env env) { - if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER; - - switch (env) { - // The extended instruction sets are all version 1.0 so far. - 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_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_2_1: - case SPV_ENV_OPENCL_EMBEDDED_2_1: - case SPV_ENV_OPENCL_2_2: - case SPV_ENV_OPENCL_EMBEDDED_2_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: - case SPV_ENV_UNIVERSAL_1_3: - case SPV_ENV_VULKAN_1_1: - case SPV_ENV_VULKAN_1_1_SPIRV_1_4: - case SPV_ENV_UNIVERSAL_1_4: - case SPV_ENV_UNIVERSAL_1_5: - case SPV_ENV_VULKAN_1_2: - case SPV_ENV_UNIVERSAL_1_6: - case SPV_ENV_VULKAN_1_3: - case SPV_ENV_VULKAN_1_4: - *pExtInstTable = &kTable_1_0; - return SPV_SUCCESS; - default: - return SPV_ERROR_INVALID_TABLE; - } -} +// #include "DebugInfo.h" spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { // The names are specified by the respective extension instruction @@ -137,7 +46,9 @@ if (!strcmp("OpenCL.DebugInfo.100", name)) { return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; } - if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) { + // Match any version of NonSemantic.Shader.DebugInfo. + // Later versions are supersets that share the same instruction numbering. + if (!strncmp("NonSemantic.Shader.DebugInfo.", name, 29)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; } if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { @@ -146,6 +57,12 @@ if (!strncmp("NonSemantic.VkspReflection.", name, 27)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION; } + if (!strcmp("TOSA.001000.1", name)) { + return SPV_EXT_INST_TYPE_TOSA_001000_1; + } + if (!strcmp("Arm.MotionEngine.100", name)) { + return SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100; + } // ensure to add any known non-semantic extended instruction sets // above this point, and update spvExtInstIsNonSemantic() if (!strncmp("NonSemantic.", name, 12)) { @@ -172,47 +89,3 @@ } return false; } - -spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, - const spv_ext_inst_type_t type, - const char* name, - spv_ext_inst_desc* pEntry) { - if (!table) return SPV_ERROR_INVALID_TABLE; - if (!pEntry) return SPV_ERROR_INVALID_POINTER; - - for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) { - const auto& group = table->groups[groupIndex]; - if (type != group.type) continue; - for (uint32_t index = 0; index < group.count; index++) { - const auto& entry = group.entries[index]; - if (!strcmp(name, entry.name)) { - *pEntry = &entry; - return SPV_SUCCESS; - } - } - } - - return SPV_ERROR_INVALID_LOOKUP; -} - -spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, - const spv_ext_inst_type_t type, - const uint32_t value, - spv_ext_inst_desc* pEntry) { - if (!table) return SPV_ERROR_INVALID_TABLE; - if (!pEntry) return SPV_ERROR_INVALID_POINTER; - - for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) { - const auto& group = table->groups[groupIndex]; - if (type != group.type) continue; - for (uint32_t index = 0; index < group.count; index++) { - const auto& entry = group.entries[index]; - if (value == entry.ext_inst) { - *pEntry = &entry; - return SPV_SUCCESS; - } - } - } - - return SPV_ERROR_INVALID_LOOKUP; -}
diff --git a/source/ext_inst.h b/source/ext_inst.h index 4027f4c..3c3150b 100644 --- a/source/ext_inst.h +++ b/source/ext_inst.h
@@ -27,20 +27,4 @@ // Returns true if the extended instruction set is debug info bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type); -// Finds the named extended instruction of the given type in the given extended -// instruction table. On success, returns SPV_SUCCESS and writes a handle of -// the instruction entry into *entry. -spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, - const spv_ext_inst_type_t type, - const char* name, - spv_ext_inst_desc* entry); - -// Finds the extended instruction of the given type in the given extended -// instruction table by value. On success, returns SPV_SUCCESS and writes a -// handle of the instruction entry into *entry. -spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, - const spv_ext_inst_type_t type, - const uint32_t value, - spv_ext_inst_desc* pEntry); - #endif // SOURCE_EXT_INST_H_
diff --git a/source/extensions.cpp b/source/extensions.cpp index ac987fc..ab4caa0 100644 --- a/source/extensions.cpp +++ b/source/extensions.cpp
@@ -19,23 +19,29 @@ #include <string> #include "source/binary.h" -#include "source/enum_string_mapping.h" +#include "source/table2.h" namespace spvtools { std::string GetExtensionString(const spv_parsed_instruction_t* inst) { - if (inst->opcode != static_cast<uint16_t>(spv::Op::OpExtension)) { + if ((inst->opcode != static_cast<uint16_t>(spv::Op::OpExtension)) && + (inst->opcode != + static_cast<uint16_t>(spv::Op::OpConditionalExtensionINTEL))) { return "ERROR_not_op_extension"; } - assert(inst->num_operands == 1); + const bool is_conditional = + inst->opcode == + static_cast<uint16_t>(spv::Op::OpConditionalExtensionINTEL); + assert(inst->num_operands == (is_conditional ? 2 : 1)); + const uint16_t op_i = is_conditional ? 1 : 0; - const auto& operand = inst->operands[0]; + const auto& operand = inst->operands[op_i]; assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING); assert(inst->num_words > operand.offset); (void)operand; /* No unused variables in release builds. */ - return spvDecodeLiteralStringOperand(*inst, 0); + return spvDecodeLiteralStringOperand(*inst, op_i); } std::string ExtensionSetToString(const ExtensionSet& extensions) {
diff --git a/source/extensions.h b/source/extensions.h index cda4924..ffca5f8 100644 --- a/source/extensions.h +++ b/source/extensions.h
@@ -15,25 +15,19 @@ #ifndef SOURCE_EXTENSIONS_H_ #define SOURCE_EXTENSIONS_H_ -#include <cstdint> #include <string> #include "source/enum_set.h" -#include "spirv-tools/libspirv.h" +#include "source/table2.h" namespace spvtools { -// The known SPIR-V extensions. -enum Extension : uint32_t { -#include "extension_enum.inc" -}; - using ExtensionSet = EnumSet<Extension>; -// Returns literal string operand of OpExtension instruction. +// Returns the literal string operand of OpExtension instruction. std::string GetExtensionString(const spv_parsed_instruction_t* inst); -// Returns text string listing |extensions| separated by whitespace. +// Returns a text string listing |extensions| separated by whitespace. std::string ExtensionSetToString(const ExtensionSet& extensions); } // namespace spvtools
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 86ee657..a3e0395 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt
@@ -431,15 +431,18 @@ ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc ) - if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) - # Enable parallel builds across four cores for this lib - add_definitions(/MP4) - endif() - spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz) + if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV_TOOLS_DIFF_SOURCES ${spirv-tools_SOURCE_DIR}/source/mimalloc.cpp) + endif() + add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES}) + if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + target_link_libraries(SPIRV-Tools-fuzz PRIVATE mimalloc-static) + endif() + spvtools_default_compile_options(SPIRV-Tools-fuzz) # Compilation of the auto-generated protobuf source file will yield warnings, @@ -470,7 +473,13 @@ spvtools_check_symbol_exports(SPIRV-Tools-fuzz) if(ENABLE_SPIRV_TOOLS_INSTALL) - install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets) + set(SPIRV-Tools-fuzz-InstallTargets SPIRV-Tools-fuzz) + + if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV-Tools-fuzz-InstallTargets mimalloc-static) + endif() + + install(TARGETS ${SPIRV-Tools-fuzz-InstallTargets} EXPORT SPIRV-Tools-fuzzTargets) export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake) spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR)
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp index 191fd71..2425ec6 100644 --- a/source/fuzz/force_render_red.cpp +++ b/source/fuzz/force_render_red.cpp
@@ -36,8 +36,11 @@ // Check that this is a fragment shader bool found_capability_shader = false; for (auto& capability : ir_context->capabilities()) { - assert(capability.opcode() == spv::Op::OpCapability); - if (spv::Capability(capability.GetSingleWordInOperand(0)) == + assert(capability.opcode() == spv::Op::OpCapability || + capability.opcode() == spv::Op::OpConditionalCapabilityINTEL); + const uint32_t i_cap = + capability.opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0; + if (spv::Capability(capability.GetSingleWordInOperand(i_cap)) == spv::Capability::Shader) { found_capability_shader = true; break;
diff --git a/source/fuzz/transformation_inline_function.cpp b/source/fuzz/transformation_inline_function.cpp index 69e88fd..f4f373f 100644 --- a/source/fuzz/transformation_inline_function.cpp +++ b/source/fuzz/transformation_inline_function.cpp
@@ -182,6 +182,8 @@ } auto* cloned_block = block.Clone(ir_context); + // TODO: Handle the nullptr. + assert(cloned_block); cloned_block = caller_function->InsertBasicBlockBefore( std::unique_ptr<opt::BasicBlock>(cloned_block), successor_block); cloned_block->GetLabel()->SetResultId(result_id_map.at(cloned_block->id()));
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 4ab68d0..0271166 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp
@@ -819,6 +819,8 @@ // Clone the block so that it can be added to the new function. auto cloned_block = std::unique_ptr<opt::BasicBlock>(block_it->Clone(ir_context)); + // TODO: Handle a nullptr. + assert(cloned_block); // If this is the region's exit block, then the cloned block is the outlined // region's exit block.
diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt index a35b9a5..5fd89c3 100644 --- a/source/link/CMakeLists.txt +++ b/source/link/CMakeLists.txt
@@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. add_library(SPIRV-Tools-link ${SPIRV_TOOLS_LIBRARY_TYPE} + fnvar.cpp linker.cpp )
diff --git a/source/link/fnvar.cpp b/source/link/fnvar.cpp new file mode 100644 index 0000000..352e7ea --- /dev/null +++ b/source/link/fnvar.cpp
@@ -0,0 +1,1011 @@ +// Copyright 2025 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 "fnvar.h" + +#include <initializer_list> +#include <memory> +#include <sstream> + +#include "source/opt/instruction.h" + +namespace spvtools { + +using opt::Function; +using opt::Instruction; +using opt::analysis::Type; + +namespace { +// Helper functions + +// Parses a CSV source string for the purpose of this extension. +// +// Required columns must be known in advance and supplied as the required_cols +// argument -- this is used for error checking. Values are assumed to be +// separated by CSV_SEP. The input source string is assumed to be the output of +// io::ReadTextFile and no other validation, apart from the CSV parsing, is +// performed. +// +// Returns true on success, false on error (with error message stored in +// err_msg). +bool ParseCsv(const std::string& source, + const std::vector<std::string>& required_cols, + std::stringstream& err_msg, + std::vector<std::vector<std::string>>& result) { + std::stringstream fn_variants_csv_stream(source); + std::string line; + std::vector<std::string> columns; + constexpr char CSV_SEP = ','; + bool first_line = true; + + while (std::getline(fn_variants_csv_stream, line, '\n')) { + if (line.empty()) { + continue; + } + + std::vector<std::string> vals; + std::string val; + std::stringstream line_stream(line); + auto* vec = first_line ? &columns : &vals; + + while (std::getline(line_stream, val, CSV_SEP)) { + vec->push_back(val); + } + + if (!line_stream && val.empty()) { + vec->push_back(""); + } + + if (!first_line) { + if (vals.size() != columns.size()) { + err_msg << "Number of values does not match the number of columns. " + "Offending line:\n" + << line; + return false; + } + result.push_back(vals); + } + + first_line = false; + } + + // check if required columns match actual columns (ordering matters) + + if (columns.size() != required_cols.size()) { + err_msg << "Invalid number of CSV columns: " << columns.size() + << ", expected " << required_cols.size() << "."; + return false; + } + + for (size_t i = 0; i < columns.size(); ++i) { + if (columns[i] != required_cols[i]) { + err_msg << "Invalid name of column " << i + 1 << ". Expected '" + << required_cols[i] << "', got '" << columns[i] << "'."; + return false; + } + } + + return true; +} + +// Annotate ID with ConditionalINTEL decoration +void DecorateConditional(IRContext* context, uint32_t id_to_decorate, + uint32_t spec_const_id) { + auto decor_instr = + std::make_unique<Instruction>(context, spv::Op::OpDecorate); + decor_instr->AddOperand({SPV_OPERAND_TYPE_ID, {id_to_decorate}}); + decor_instr->AddOperand({SPV_OPERAND_TYPE_DECORATION, + {uint32_t(spv::Decoration::ConditionalINTEL)}}); + decor_instr->AddOperand({SPV_OPERAND_TYPE_ID, {spec_const_id}}); + context->module()->AddAnnotationInst(std::move(decor_instr)); +} + +// Finds entry point corresponding to a function +// +// Returns null if not found, otherwise returns pointer to the EP Instruction. +Instruction* FindEntryPoint(const Instruction& fn_inst) { + auto* mod = fn_inst.context()->module(); + for (auto& entry_point : mod->entry_points()) { + const int ep_i = + entry_point.opcode() == spv::Op::OpConditionalEntryPointINTEL ? 2 : 1; + if (entry_point.GetOperand(ep_i).AsId() == fn_inst.result_id()) { + return &entry_point; + } + } + return nullptr; +} + +// If the function has an entry point, converts it to a conditional one +void ConvertEPToConditional(Module* module, const Function& fn, + uint32_t spec_const_id) { + for (const auto& ep_inst : module->entry_points()) { + if (ep_inst.opcode() == spv::Op::OpEntryPoint) { + auto* entry_point = FindEntryPoint(fn.DefInst()); + if (entry_point != nullptr) { + std::vector<opt::Operand> old_operands; + for (auto operand : *entry_point) { + old_operands.push_back(operand); + } + entry_point->ToNop(); + entry_point->SetOpcode(spv::Op::OpConditionalEntryPointINTEL); + entry_point->AddOperand({SPV_OPERAND_TYPE_ID, {spec_const_id}}); + for (auto old_operand : old_operands) { + entry_point->AddOperand(old_operand); + } + } + } + } +} + +// Finds ID of a bool type (returns 0 if not found) +uint32_t FindIdOfBoolType(const Module* const mod) { + return mod->context()->get_type_mgr()->GetBoolTypeId(); +} + +// Combines IDs using OpSpecConstantOp with the operation defined by cmp_op. +// +// Returns the ID of the final result. If there are no IDs, returns 0. If there +// is one ID, does not generate any instructions and returns the ID. +uint32_t CombineIds(IRContext* const context, const std::vector<uint32_t>& ids, + spv::Op cmp_op) { + if (ids.empty()) { + return 0; + } else if (ids.size() == 1) { + return ids[0]; + } else { + uint32_t bool_id = FindIdOfBoolType(context->module()); + assert(bool_id != 0); + + uint32_t prev_spec_const_id = ids[0]; + + for (size_t i = 1; i < ids.size(); ++i) { + const uint32_t id = ids[i]; + const uint32_t spec_const_op_id = context->TakeNextId(); + + auto inst = std::make_unique<Instruction>( + context, spv::Op::OpSpecConstantOp, bool_id, spec_const_op_id, + std::initializer_list<opt::Operand>{ + {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {(uint32_t)(cmp_op)}}, + {SPV_OPERAND_TYPE_ID, {prev_spec_const_id}}, + {SPV_OPERAND_TYPE_ID, {id}}}); + context->module()->AddType(std::move(inst)); + + prev_spec_const_id = spec_const_op_id; + } + + return prev_spec_const_id; + } +} + +// Returns whether instruction can be shared between variant modules and +// combined using spec constants (such as conditional capabilities). +bool CanBeFnVarCombined(const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + if ((opcode != spv::Op::OpExtInstImport) && + (opcode != spv::Op::OpCapability) && (opcode != spv::Op::OpExtension) && + !spvOpcodeGeneratesType(opcode)) { + return false; + } + + if ((opcode == spv::Op::OpCapability) && + ((inst->GetSingleWordOperand(0) == + static_cast<uint32_t>(spv::Capability::FunctionVariantsINTEL)) || + (inst->GetSingleWordOperand(0) == + static_cast<uint32_t>(spv::Capability::SpecConditionalINTEL)))) { + // Always enabled + return false; + } + + if ((opcode == spv::Op::OpExtension) && + (inst->GetOperand(0).AsString() == FNVAR_EXT_NAME)) { + // Always enabled + return false; + } + + return true; +} + +// Calculates hash of an instruction. +// +// Applicable only to instructions that can be combined (ie. with +// CanBeFnVarCombined being true) and from those, hash can be only computed for +// selected instructions. Computing hash from other instruction is unsupported. +size_t HashInst(const Instruction* inst) { + if (CanBeFnVarCombined(inst)) { + if (spvOpcodeGeneratesType(inst->opcode())) { + const Type* t = + inst->context()->get_type_mgr()->GetType(inst->result_id()); + assert(t != nullptr); + return t->HashValue(); + } + + if (inst->opcode() == spv::Op::OpExtension) { + const auto name = inst->GetOperand(0).AsString(); + return std::hash<std::string>()(name); + } + + if (inst->opcode() == spv::Op::OpCapability) { + const auto cap = inst->GetSingleWordOperand(0); + return std::hash<uint32_t>()(cap); + } + + if (inst->opcode() == spv::Op::OpExtInstImport) { + const auto name = inst->GetOperand(1).AsString(); + return std::hash<std::string>()(name); + } + } + + assert(false && "Unsupported instruction hash"); + return std::hash<const Instruction*>()(inst); +} + +std::string GetFnName(const Instruction& fn_inst) { + // Check entry point + const auto* ep_inst = FindEntryPoint(fn_inst); + if (ep_inst != nullptr) { + const int name_i = + ep_inst->opcode() == spv::Op::OpConditionalEntryPointINTEL ? 3 : 2; + return ep_inst->GetOperand(name_i).AsString(); + } + + // Check name of export linkage attribute decoration + const auto* decor_mgr = fn_inst.context()->get_decoration_mgr(); + for (const auto* inst : + decor_mgr->GetDecorationsFor(fn_inst.result_id(), true)) { + const auto decoration = inst->GetOperand(1); + if ((decoration.type == SPV_OPERAND_TYPE_DECORATION) && + (decoration.words.size() == 1) && + (decoration.words[0] == + static_cast<uint32_t>(spv::Decoration::LinkageAttributes))) { + const auto linkage = inst->GetOperand(3); + if ((linkage.type == SPV_OPERAND_TYPE_LINKAGE_TYPE) && + (linkage.words.size() == 1) && + (linkage.words[0] == + static_cast<uint32_t>(spv::LinkageType::Export))) { + // decorates fn with LinkageAttribute and Export linkage type -> get the + // name + return inst->GetOperand(2).AsString(); + } + } + } + + return ""; +} + +uint32_t FindSpecConstByName(const Module* mod, std::string name) { + for (const auto* const_inst : mod->context()->GetConstants()) { + if (opt::IsSpecConstantInst(const_inst->opcode())) { + const auto id = const_inst->result_id(); + for (const auto& name_inst : mod->debugs2()) { + if ((name_inst.opcode() == spv::Op::OpName) && + (name_inst.GetOperand(0).AsId() == id) && + (name_inst.GetOperand(1).AsString() == name)) { + return id; + } + } + } + } + return 0; +} + +uint32_t CombineVariantDefs(const std::vector<VariantDef>& variant_defs, + const std::vector<size_t> var_ids, + IRContext* context, + std::map<std::vector<size_t>, uint32_t>& cache) { + assert(var_ids.size() <= variant_defs.size()); + uint32_t spec_const_comb_id = 0; + if (var_ids.size() != variant_defs.size()) { + // if not used by all variants + if (cache.find(var_ids) == cache.end()) { + // cache variant combinations + std::vector<uint32_t> spec_const_ids; + for (const auto& var_id : var_ids) { + const auto var_name = variant_defs[var_id].GetName(); + const auto var_spec_id = + FindSpecConstByName(context->module(), var_name); + spec_const_ids.push_back(var_spec_id); + } + spec_const_comb_id = + CombineIds(context, spec_const_ids, spv::Op::OpLogicalOr); + assert(spec_const_comb_id != 0); + cache.insert({var_ids, spec_const_comb_id}); + } else { + spec_const_comb_id = cache[var_ids]; + } + } + return spec_const_comb_id; +} + +bool strToInt(std::string s, uint32_t* x) { + for (const char& c : s) { + if (c < '0' || c > '9') { + return false; + } + } + if (!(std::stringstream(s) >> *x)) { + return false; + } + return true; +} + +} // anonymous namespace + +bool VariantDefs::ProcessFnVar(const LinkerOptions& options, + const std::vector<Module*>& modules) { + assert(variant_defs_.empty()); + assert(modules.size() == options.GetInFiles().size()); + + for (size_t i = 0; i < modules.size(); ++i) { + const auto* feat_mgr = modules[i]->context()->get_feature_mgr(); + if ((feat_mgr->HasCapability(spv::Capability::FunctionVariantsINTEL)) || + (feat_mgr->HasCapability(spv::Capability::SpecConditionalINTEL)) || + (feat_mgr->HasExtension(kSPV_INTEL_function_variants))) { + // In principle, it can be done but it's complicated due to having to + // combine the existing conditionals with the new ones. For example, + // conditional capabilities would need to become "doubly-conditional". + err_ << "Creating multitarget modules from multitarget modules is not " + "supported. Offending file: " + << options.GetInFiles()[i]; + return false; + } + } + + std::vector<std::vector<std::string>> target_rows; + std::vector<std::vector<std::string>> architecture_rows; + + if (!options.GetFnVarTargetsCsv().empty()) { + const std::vector<std::string> tgt_cols = {"module", "target", "features"}; + if (!ParseCsv(options.GetFnVarTargetsCsv(), tgt_cols, err_, target_rows)) { + return false; + } + } + + if (!options.GetFnVarArchitecturesCsv().empty()) { + const std::vector<std::string> arch_cols = {"module", "category", "family", + "op", "architecture"}; + if (!ParseCsv(options.GetFnVarArchitecturesCsv(), arch_cols, err_, + architecture_rows)) { + return false; + } + } + + // check that all modules defined in the CSV exist + + for (const auto& tgt_vals : target_rows) { + bool found = false; + for (const auto& in_file : options.GetInFiles()) { + if (tgt_vals[0] == in_file) { + found = true; + } + } + if (!found) { + err_ << "Module '" << tgt_vals[0] + << "' found in targets CSV not passed to the CLI."; + return false; + } + } + + for (const auto& arch_vals : architecture_rows) { + bool found = false; + for (const auto& in_file : options.GetInFiles()) { + if (arch_vals[0] == in_file) { + found = true; + } + } + if (!found) { + err_ << "Module '" << arch_vals[0] + << "' found in architectures CSV not passed to the CLI."; + return false; + } + } + + // create per-module variant defs + + for (size_t i = 0; i < modules.size(); ++i) { + // first module passed to the CLI is considered the base module + bool is_base = i == 0; + const auto name = options.GetInFiles()[i]; + auto variant_def = VariantDef(is_base, name, modules[i]); + + for (const auto& arch_row : architecture_rows) { + const auto row_name = arch_row[0]; + if (row_name == name) { + uint32_t category, family, op, architecture; + + if (!strToInt(arch_row[1], &category)) { + err_ << "Error converting " << arch_row[1] + << " to architecture category."; + return false; + } + if (!strToInt(arch_row[2], &family)) { + err_ << "Error converting " << arch_row[2] + << " to architecture family."; + return false; + } + if (!strToInt(arch_row[3], &op)) { + err_ << "Error converting " << arch_row[3] << " to architecture op."; + return false; + } + if (!strToInt(arch_row[4], &architecture)) { + err_ << "Error converting " << arch_row[4] << " to architecture."; + return false; + } + + variant_def.AddArchDef(category, family, op, architecture); + } + } + + for (const auto& tgt_row : target_rows) { + const auto row_name = tgt_row[0]; + if (row_name == name) { + uint32_t target; + std::vector<uint32_t> features; + + if (!strToInt(tgt_row[1], &target)) { + err_ << "Error converting " << tgt_row[1] << " to target."; + return false; + } + + // get features as FEAT_SEP-delimited integers + + std::stringstream feat_stream(tgt_row[2]); + std::string feat; + while (std::getline(feat_stream, feat, FEAT_SEP)) { + uint32_t ufeat; + // if (!(std::stringstream(feat) >> ufeat)) { + if (!strToInt(feat, &ufeat)) { + err_ << "Error converting " << feat << " in " << tgt_row[2] + << " to target feature."; + return false; + } + features.push_back(ufeat); + } + + variant_def.AddTgtDef(target, features); + } + } + + if (options.GetHasFnVarCapabilities()) { + variant_def.InferCapabilities(); + } + + variant_defs_.push_back(variant_def); + } + + return true; +} + +bool VariantDefs::ProcessVariantDefs() { + EnsureBoolType(); + CollectVarInsts(); + if (!GenerateFnVarConstants()) { + return false; + } + CollectBaseFnCalls(); + return true; +} + +void VariantDefs::GenerateHeader(IRContext* linked_context) { + linked_context->AddCapability(spv::Capability::SpecConditionalINTEL); + linked_context->AddCapability(spv::Capability::FunctionVariantsINTEL); + linked_context->AddExtension(std::string(FNVAR_EXT_NAME)); + + // Specifies used registry version + auto inst = + std::make_unique<Instruction>(linked_context, spv::Op::OpModuleProcessed); + std::stringstream line; + line << "SPV_INTEL_function_variants registry version " + << FNVAR_REGISTRY_VERSION; + inst->AddOperand( + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(line.str())}); + linked_context->AddDebug3Inst(std::move(inst)); +} + +void VariantDefs::CombineVariantInstructions(IRContext* linked_context) { + CombineBaseFnCalls(linked_context); + CombineInstructions(linked_context); +} + +void VariantDefs::EnsureBoolType() { + for (auto& variant_def : variant_defs_) { + Module* module = variant_def.GetModule(); + IRContext* context = module->context(); + + uint32_t bool_id = FindIdOfBoolType(module); + if (bool_id == 0) { + bool_id = context->TakeNextId(); + auto variant_bool = std::make_unique<Instruction>( + context, spv::Op::OpTypeBool, 0, bool_id, + std::initializer_list<opt::Operand>{}); + module->AddType(std::move(variant_bool)); + } + } +} + +void VariantDefs::CollectVarInsts() { + for (size_t i = 0; i < variant_defs_.size(); ++i) { + const auto variant_def = variant_defs_[i]; + const auto* var_mod = variant_def.GetModule(); + + var_mod->ForEachInst([this, &i](const Instruction* inst) { + if (CanBeFnVarCombined(inst)) { + const size_t inst_hash = HashInst(inst); + if (fnvar_usage_.find(inst_hash) == fnvar_usage_.end()) { + fnvar_usage_.insert({inst_hash, {i}}); + } else { + assert(fnvar_usage_[inst_hash].size() < variant_defs_.size()); + fnvar_usage_[inst_hash].push_back(i); + } + } + }); + } +} + +bool VariantDefs::GenerateFnVarConstants() { + assert(variant_defs_.size() > 0); + assert(variant_defs_[0].IsBase()); + + if (variant_defs_.size() == 1) { + return true; + } + + for (auto& variant_def : variant_defs_) { + Module* module = variant_def.GetModule(); + IRContext* context = module->context(); + + uint32_t bool_id = FindIdOfBoolType(module); + if (bool_id == 0) { + // add a bool type if not present already + bool_id = context->TakeNextId(); + auto variant_bool = std::make_unique<Instruction>( + context, spv::Op::OpTypeBool, 0, bool_id, + std::initializer_list<opt::Operand>{}); + module->AddType(std::move(variant_bool)); + } + + // Spec constant architecture and target + + std::vector<uint32_t> spec_const_arch_ids; + for (const auto& arch_def : variant_def.GetArchDefs()) { + const uint32_t spec_const_arch_id = context->TakeNextId(); + spec_const_arch_ids.push_back(spec_const_arch_id); + + auto inst = std::make_unique<Instruction>( + context, spv::Op::OpSpecConstantArchitectureINTEL, bool_id, + spec_const_arch_id, + std::initializer_list<opt::Operand>{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.category}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.family}}, + // Using spec op opcode here expects then next operand to be + // a type: + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.op}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.architecture}}, + }); + module->AddType(std::move(inst)); + } + + std::vector<uint32_t> spec_const_tgt_ids; + for (const auto& tgt_def : variant_def.GetTgtDefs()) { + const uint32_t spec_const_tgt_id = context->TakeNextId(); + spec_const_tgt_ids.push_back(spec_const_tgt_id); + + auto inst = std::make_unique<Instruction>( + context, spv::Op::OpSpecConstantTargetINTEL, bool_id, + spec_const_tgt_id, + std::initializer_list<opt::Operand>{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {tgt_def.target}}, + }); + for (const auto& feat : tgt_def.features) { + inst->AddOperand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {feat}}); + } + module->AddType(std::move(inst)); + } + + std::vector<uint32_t> spec_const_ids; + + // Spec constant capabilities + + const auto variant_capabilities = variant_def.GetCapabilities(); + if (!variant_capabilities.empty()) { + const uint32_t spec_const_cap_id = context->TakeNextId(); + auto inst = std::make_unique<Instruction>( + context, spv::Op::OpSpecConstantCapabilitiesINTEL, bool_id, + spec_const_cap_id, std::initializer_list<opt::Operand>{}); + for (const auto& cap : variant_capabilities) { + inst->AddOperand({SPV_OPERAND_TYPE_CAPABILITY, {uint32_t(cap)}}); + } + module->AddType(std::move(inst)); + spec_const_ids.push_back(spec_const_cap_id); + } + + // Combine architectures such that, for the same module, those with the same + // category and family are combined with AND and different cat/fam are + // combined with OR. + // This lets you create combinations like "architecture between X and Y". + + // map (category, family) -> IDs + std::map<std::pair<uint32_t, uint32_t>, std::vector<uint32_t>> arch_map_and; + + for (size_t i = 0; i < spec_const_arch_ids.size(); ++i) { + const auto& arch_def = variant_def.GetArchDefs()[i]; + const auto id = spec_const_arch_ids[i]; + const auto key = std::make_pair(arch_def.category, arch_def.family); + if (arch_map_and.find(key) == arch_map_and.end()) { + arch_map_and[key] = {id}; + } else { + arch_map_and[key].push_back(id); + } + } + + std::vector<uint32_t> arch_ids_or; + for (const auto& it : arch_map_and) { + const auto id = CombineIds(context, it.second, spv::Op::OpLogicalAnd); + if (id > 0) { + arch_ids_or.push_back(id); + } + } + + const uint32_t spec_const_arch_id = + CombineIds(context, arch_ids_or, spv::Op::OpLogicalOr); + if (spec_const_arch_id > 0) { + spec_const_ids.push_back(spec_const_arch_id); + } + + const uint32_t spec_const_tgt_id = + CombineIds(context, spec_const_tgt_ids, spv::Op::OpLogicalOr); + if (spec_const_tgt_id > 0) { + spec_const_ids.push_back(spec_const_tgt_id); + } + + uint32_t combined_spec_const_id = + CombineIds(context, spec_const_ids, spv::Op::OpLogicalAnd); + if (combined_spec_const_id == 0) { + // If the variant module has no constraints, use SpecConstantTrue + combined_spec_const_id = context->TakeNextId(); + auto inst = std::make_unique<Instruction>( + context, spv::Op::OpSpecConstantTrue, bool_id, combined_spec_const_id, + std::initializer_list<opt::Operand>{}); + context->module()->AddType(std::move(inst)); + } + assert(combined_spec_const_id != 0); + + // Add a name the combined boolean ID so we can look it up after the IDs are + // shifted + auto inst = std::make_unique<Instruction>(context, spv::Op::OpName); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {combined_spec_const_id}}); + std::vector<uint32_t> str_words; + utils::AppendToVector(variant_def.GetName(), &str_words); + inst->AddOperand({SPV_OPERAND_TYPE_LITERAL_STRING, {str_words}}); + module->AddDebug2Inst(std::move(inst)); + + // Annotate all instructions in the types section (eg. constants) with + // ConditionalINTEL, unless they can be shared between variant_defs_ (eg. + // types). Spec constants are excluded because they might have been + // generated by this extension. + for (const auto& type_inst : module->types_values()) { + if (!CanBeFnVarCombined(&type_inst) && + !spvOpcodeIsSpecConstant(type_inst.opcode())) { + DecorateConditional(context, type_inst.result_id(), + combined_spec_const_id); + } + } + } + + // Annotate functions with ConditionalINTEL + + for (const auto& base_fn : *variant_defs_[0].GetModule()) { + // For each function of the base module, find matching variant functions in + // other modules + + auto base_fn_name = GetFnName(base_fn.DefInst()); + if (base_fn_name.empty()) { + err_ << "Could not find name of a function " << base_fn.result_id() + << " in a base module " << variant_defs_[0].GetName() + << ". To be usable by SPV_INTEL_function_variants, a function " + "must either have an entry point or an export " + "LinkAttribute decoration."; + return false; + } + + bool base_fn_needs_conditional = false; + for (size_t i = 1; i < variant_defs_.size(); ++i) { + const auto& variant_def = variant_defs_[i]; + auto* variant_module = variant_def.GetModule(); + auto* variant_context = variant_module->context(); + + for (const auto& var_fn : *variant_module) { + auto var_fn_name = GetFnName(var_fn.DefInst()); + if (var_fn_name.empty()) { + err_ << "Could not find name of a function " << var_fn.result_id() + << " in a base module " << variant_def.GetName() + << ". To be usable by SPV_INTEL_function_variants, a function " + "must either have an entry point or an export " + "LinkAttribute decoration."; + return false; + } + + if (base_fn_name == var_fn_name) { + base_fn_needs_conditional = true; + } + + // each function in a variant module gets a ConditionalINTEL decoration + + uint32_t spec_const_id = + FindSpecConstByName(variant_module, variant_def.GetName()); + assert(spec_const_id != 0); + DecorateConditional(variant_context, var_fn.result_id(), spec_const_id); + ConvertEPToConditional(variant_module, var_fn, spec_const_id); + } + } + + if (base_fn_needs_conditional) { + // only a base function that has a variant in another module gets a + // ConditionalINTEL decoration, the others are common for all + // variant_defs_ + auto* base_module = variant_defs_[0].GetModule(); + auto* base_context = base_module->context(); + uint32_t spec_const_id = + FindSpecConstByName(base_module, variant_defs_[0].GetName()); + assert(spec_const_id != 0); + DecorateConditional(base_context, base_fn.result_id(), spec_const_id); + ConvertEPToConditional(base_module, base_fn, spec_const_id); + } + } + + return true; +} + +void VariantDefs::CollectBaseFnCalls() { + auto* base_mod = variant_defs_[0].GetModule(); + assert(variant_defs_[0].IsBase()); + const auto* base_def_use_mgr = base_mod->context()->get_def_use_mgr(); + + base_mod->ForEachInst([this, &base_def_use_mgr](const Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionCall) { + // For each function call in base module, get the function name + const auto fn_id = inst->GetOperand(2).AsId(); + const auto* called_fn_inst = base_def_use_mgr->GetDef(fn_id); + assert(called_fn_inst != nullptr); + const auto called_fn_name = GetFnName(*called_fn_inst); + assert(!called_fn_name.empty()); + + std::vector<std::pair<std::string, const opt::Function*>> called_fns; + for (size_t i = 1; i < variant_defs_.size(); ++i) { + // ... then see in which variant the called function was defined + const auto& variant_def = variant_defs_[i]; + assert(!variant_def.IsBase()); + + for (const auto& fn : *variant_def.GetModule()) { + const auto fn_name = GetFnName(fn.DefInst()); + if (fn_name == called_fn_name) { + called_fns.push_back(std::make_pair(variant_def.GetName(), &fn)); + } + } + } + + if (!called_fns.empty()) { + base_fn_calls_[inst->result_id()] = called_fns; + } + } + }); +} + +void VariantDefs::CombineBaseFnCalls(IRContext* linked_context) { + for (auto kv : base_fn_calls_) { + const uint32_t call_id = kv.first; + const auto called_fns = kv.second; + + if (called_fns.empty()) { + return; + } + + opt::BasicBlock* fn_call_bb = linked_context->get_instr_block(call_id); + + Instruction* found_call_inst = nullptr; + auto bb_iter = fn_call_bb->begin(); + while (bb_iter != fn_call_bb->end() && found_call_inst == nullptr) { + if (bb_iter->HasResultId() && bb_iter->result_id() == call_id) { + found_call_inst = &*bb_iter; + } + ++bb_iter; + } + + if (found_call_inst == nullptr) { + return; + } + + const auto base_spec_const_id = FindSpecConstByName( + variant_defs_[0].GetModule(), variant_defs_[0].GetName()); + const auto base_type_op = found_call_inst->context() + ->get_def_use_mgr() + ->GetDef(found_call_inst->type_id()) + ->opcode(); + const auto base_call_id = found_call_inst->result_id(); + + // decorate the base call with ConditionalINTEL + DecorateConditional(linked_context, base_call_id, base_spec_const_id); + + // Add OpFunctionCall for each variant + Instruction* last_inst = found_call_inst; + std::vector<std::pair<uint32_t, uint32_t>> var_call_ids; + for (const auto& kv2 : called_fns) { + const std::string var_name = kv2.first; + const opt::Function* fn = kv2.second; + const uint32_t spec_const_id = + FindSpecConstByName(linked_context->module(), var_name); + assert(spec_const_id != 0); + const uint32_t var_call_id = linked_context->TakeNextId(); + var_call_ids.push_back(std::make_pair(spec_const_id, var_call_id)); + + auto* var_call_inst = found_call_inst->Clone(linked_context); + var_call_inst->SetResultId(var_call_id); + var_call_inst->SetOperand(2, {fn->result_id()}); + var_call_inst->InsertAfter(last_inst); + linked_context->set_instr_block(var_call_inst, fn_call_bb); + last_inst = var_call_inst; + + // decorate the variant call with ConditionalINTEL + DecorateConditional(linked_context, var_call_id, spec_const_id); + } + + if (base_type_op != spv::Op::OpTypeVoid) { + // Add OpConditionalCopyObjectINTEL combining the function calls + const uint32_t result_id = linked_context->TakeNextId(); + auto conditional_copy_inst = new Instruction( + linked_context, spv::Op::OpConditionalCopyObjectINTEL, + found_call_inst->type_id(), result_id, + {{SPV_OPERAND_TYPE_ID, {base_spec_const_id}}, + {SPV_OPERAND_TYPE_ID, {found_call_inst->result_id()}}}); + + for (const auto& kv3 : var_call_ids) { + const auto spec_const_id = kv3.first; + const auto var_call_id = kv3.second; + conditional_copy_inst->AddOperand( + {SPV_OPERAND_TYPE_ID, {spec_const_id}}); + conditional_copy_inst->AddOperand({SPV_OPERAND_TYPE_ID, {var_call_id}}); + } + conditional_copy_inst->InsertAfter(last_inst); + linked_context->set_instr_block(conditional_copy_inst, fn_call_bb); + last_inst = conditional_copy_inst; + + // In all remaining instructions within the basic block, replace all + // usages of the base call ID with the result of + // OpConditionalCopyObjectINTEL + do { + last_inst = last_inst->NextNode(); + last_inst->ForEachInId([base_call_id, result_id](uint32_t* id) { + if (*id == base_call_id) { + *id = result_id; + } + }); + } while (last_inst != nullptr && *last_inst != *fn_call_bb->tail()); + } + } + + // Combine spec consts for the base module (base module is activated if all + // variant defs are inactive AND the base module constraints are satisfied) + + std::vector<uint32_t> var_spec_const_ids; + for (const auto& variant_def : variant_defs_) { + if (variant_def.IsBase()) { + continue; + } + + const auto id = + FindSpecConstByName(linked_context->module(), variant_def.GetName()); + assert(id != 0); + var_spec_const_ids.push_back(id); + } + const uint32_t base_or_id = + CombineIds(linked_context, var_spec_const_ids, spv::Op::OpLogicalOr); + + if (base_or_id != 0) { + const uint32_t bool_id = FindIdOfBoolType(linked_context->module()); + assert(bool_id != 0); + + const uint32_t base_not_id = linked_context->TakeNextId(); + auto spec_const_op_inst = std::make_unique<Instruction>( + linked_context, spv::Op::OpSpecConstantOp, bool_id, base_not_id, + std::initializer_list<opt::Operand>{ + {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, + {(uint32_t)(spv::Op::OpLogicalNot)}}, + {SPV_OPERAND_TYPE_ID, {base_or_id}}}); + linked_context->module()->AddType(std::move(spec_const_op_inst)); + + // Update any ConditionalINTEL annotations, names and entry points + // referencing the old spec const ID to use the new one + + const uint32_t old_base_spec_const_id = FindSpecConstByName( + linked_context->module(), variant_defs_[0].GetName()); + assert(old_base_spec_const_id != 0); + const uint32_t base_spec_const_id = + CombineIds(linked_context, {old_base_spec_const_id, base_not_id}, + spv::Op::OpLogicalAnd); + + for (auto& annot_inst : linked_context->module()->annotations()) { + if ((annot_inst.GetSingleWordOperand(1) == + uint32_t(spv::Decoration::ConditionalINTEL)) && + (annot_inst.GetOperand(2).AsId() == old_base_spec_const_id)) { + annot_inst.SetOperand(2, {base_spec_const_id}); + } + } + + for (auto& name_inst : linked_context->module()->debugs2()) { + if ((name_inst.opcode() == spv::Op::OpName) && + (name_inst.GetOperand(0).AsId() == old_base_spec_const_id)) { + name_inst.SetOperand(0, {base_spec_const_id}); + } + } + + for (auto& ep_inst : linked_context->module()->entry_points()) { + if ((ep_inst.opcode() == spv::Op::OpConditionalEntryPointINTEL) && + (ep_inst.GetOperand(0).AsId() == old_base_spec_const_id)) { + ep_inst.SetOperand(0, {base_spec_const_id}); + } + } + + linked_context->module()->ForEachInst( + [old_base_spec_const_id, base_spec_const_id](Instruction* inst) { + if (inst->opcode() == spv::Op::OpConditionalCopyObjectINTEL) { + inst->ForEachInId( + [old_base_spec_const_id, base_spec_const_id](uint32_t* id) { + if (*id == old_base_spec_const_id) { + *id = base_spec_const_id; + } + }); + } + }); + } +} + +void VariantDefs::CombineInstructions(IRContext* linked_context) { + // cache for existing variant ID combinations + std::map<std::vector<size_t>, uint32_t> spec_const_comb_ids; + + linked_context->module()->ForEachInst( + [this, &linked_context, &spec_const_comb_ids](Instruction* inst) { + if (!CanBeFnVarCombined(inst)) { + return; + } + + const size_t inst_hash = HashInst(inst); + if (fnvar_usage_.find(inst_hash) != fnvar_usage_.end()) { + const std::vector<size_t> var_ids = fnvar_usage_[inst_hash]; + const uint32_t spec_const_comb_id = CombineVariantDefs( + variant_defs_, var_ids, linked_context, spec_const_comb_ids); + if (spec_const_comb_id != 0) { + if (inst->HasResultId()) { + DecorateConditional(linked_context, inst->result_id(), + spec_const_comb_id); + } else if (inst->opcode() == spv::Op::OpCapability) { + const uint32_t cap = inst->GetSingleWordOperand(0); + inst->SetOpcode(spv::Op::OpConditionalCapabilityINTEL); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {spec_const_comb_id}}, + {SPV_OPERAND_TYPE_CAPABILITY, {cap}}}); + } else if (inst->opcode() == spv::Op::OpExtension) { + const std::string ext_name = inst->GetOperand(0).AsString(); + inst->SetOpcode(spv::Op::OpConditionalExtensionINTEL); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {spec_const_comb_id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, + {utils::MakeVector(ext_name)}}}); + } else { + assert(false && "Unsupported"); + } + } + } + }); +} + +} // namespace spvtools
diff --git a/source/link/fnvar.h b/source/link/fnvar.h new file mode 100644 index 0000000..49db030 --- /dev/null +++ b/source/link/fnvar.h
@@ -0,0 +1,244 @@ +// Copyright 2025 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. + +// Implementation of generating multitarget modules according to the +// *SPV_INTEL_function_variants* extension +// +// Multitarget module is generated by linking separate modules: a base module +// and variant modules containing device-specific variants of the functions in +// the base module. The behavior is controlled by Comma-Separated Values (CSV) +// files passed to the following flags: +// --fnvar-targets: Required columns: +// module - module file name +// target - device target ISA value +// features - feature values for the target separated by '/' (FEAT_SEP) +// --fnvar-architectures: Required columns: +// module - module file name +// category - device category value +// family - device family value +// op - opcode of the comparison instruction +// architecture - device architecture +// The values (except module) are decimal strings with their meaning defined in +// the 'targets registry' as described in the extension spec. The decimal +// strings may only encode unsigned 32-bit integers (characters 0-9), possibly +// with leading zeros. +// +// In addition, --fnvar-capabilities generates OpSpecConstantCapabilitiesINTEL +// for each module with operands corresponding to the module's capabilities. +// +// Each line in the targets/architectures CSV file defines one +// OpSpecConstant<Target/Architecture>INTEL instruction, the columns correspond +// to the operands of these instructions. One module can have multiple lines, in +// which case they are combined into a single boolean spec constant using +// OpSpecConstantOp and OpLogicalOr (except when category and family in the +// architectures CSV are the same, then the lines are combined with +// OpLogicalAnd). For example, the following architectures CSV +// +// module,category,family,op,architecture +// foo.spv,1,7,174,1 +// foo.spv,1,7,178,3 +// foo.spv,1,8,170,1 +// +// is combined as follows: +// +// %53 = OpSpecConstantArchitectureINTEL %bool 1 7 174 1 +// %54 = OpSpecConstantArchitectureINTEL %bool 1 7 178 3 +// %55 = OpSpecConstantArchitectureINTEL %bool 1 8 170 1 +// %56 = OpSpecConstantOp %bool LogicalAnd %53 %54 +// %foo_spv = OpSpecConstantOp %bool LogicalOr %55 %56 +// +// The %foo_spv is annotated with OpName "foo.spv" (the module's name) which +// serves as an identifier to find the constant later. We cannot use IDs for it +// because the IDs get shifted during linking. +// +// The first module passed to `spirv-link` is considered the 'base' module. For +// example, if base module defines functions 'foo' and 'bar' and the other +// modules define only 'foo', only the 'foo' is treated as a function variant +// guarded by spec constants. The 'bar' function will be untouched and therefore +// present for all variants. The function variants are matched by name, and +// therefore they must either have an entry point, or an Export linkage +// attribute. + +#ifndef FNVAR_H +#define FNVAR_H + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/linker.hpp" + +namespace spvtools { + +using opt::IRContext; +using opt::Module; + +// Map of instruction hash -> which variants are using the instruction (denoted +// by the index to the variants vector) +using FnVarUsage = std::unordered_map<size_t, std::vector<size_t>>; + +// Map of base function call ID -> variant functions corresponding to the +// called function (along with the variant name) +using BaseFnCalls = + std::map<uint32_t, + std::vector<std::pair<std::string, const opt::Function*>>>; + +constexpr char FNVAR_EXT_NAME[] = "SPV_INTEL_function_variants"; +constexpr uint32_t FNVAR_REGISTRY_VERSION = 0; +constexpr char FEAT_SEP = '/'; + +struct FnVarArchDef { + uint32_t category; + uint32_t family; + uint32_t op; + uint32_t architecture; +}; + +struct FnVarTargetDef { + uint32_t target; + std::vector<uint32_t> features; +}; + +// Definition of a variant +// +// Stores architecture and target definitions inferred from lines in the CSV +// files for a single module (as well as a pointer to the Module). +class VariantDef { + public: + VariantDef(bool isbase, std::string nm, Module* mod) + : is_base(isbase), name(nm), module(mod) {} + + bool IsBase() const { return this->is_base; } + std::string GetName() const { return this->name; } + Module* GetModule() const { return this->module; } + + void AddArchDef(uint32_t category, uint32_t family, uint32_t op, + uint32_t architecture) { + FnVarArchDef arch_def; + arch_def.category = category; + arch_def.family = family; + arch_def.op = op; + arch_def.architecture = architecture; + this->arch_defs.push_back(arch_def); + } + const std::vector<FnVarArchDef>& GetArchDefs() const { + return this->arch_defs; + } + + void AddTgtDef(uint32_t target, std::vector<uint32_t> features) { + FnVarTargetDef tgt_def; + tgt_def.target = target; + tgt_def.features = features; + this->tgt_defs.push_back(tgt_def); + } + const std::vector<FnVarTargetDef>& GetTgtDefs() const { + return this->tgt_defs; + } + + void InferCapabilities() { + for (const auto& cap_inst : module->capabilities()) { + capabilities.insert(spv::Capability(cap_inst.GetOperand(0).words[0])); + } + } + const std::set<spv::Capability>& GetCapabilities() const { + return this->capabilities; + } + + private: + bool is_base; + std::string name; + Module* module; + std::vector<FnVarTargetDef> tgt_defs; + std::vector<FnVarArchDef> arch_defs; + std::set<spv::Capability> capabilities; +}; + +// Collection of VariantDef instances +// +// Apart from being a wrapper around a vector of VariantDef instances, it +// defines the main API for generating SPV_INTEL_function_variants instructions +// based on the CSV files. +class VariantDefs { + public: + // Returns last error message. + std::string GetErr() { return err_.str(); } + + // Processes CSV files passed to the CLI and populate _variants. + // + // Returns true on success, false on error. + bool ProcessFnVar(const LinkerOptions& options, + const std::vector<Module*>& modules); + + // Analyses each variant def module and generates those instructions that are + // module-specific, ie., not requiring knowledge from other modules. + // + // Returns true on success, false on error. + bool ProcessVariantDefs(); + + // Generates basic instructions required for this extension to work. + void GenerateHeader(IRContext* linked_context); + + // Generates instructions from this extension that result from combining + // several variant def modules. + void CombineVariantInstructions(IRContext* linked_context); + + private: + // Adds a boolean type to every module if there is none. + // + // These are necessary for spec constants. + void EnsureBoolType(); + + // Collects which combinable instructions are defined in which modules + void CollectVarInsts(); + + // Generates OpSpecConstant<Target/Architecture/Capabilities>INTEL and + // combines them as necessary. Also converts entry points to conditional ones + // and decorates module-specific instructions with ConditionalINTEL. + // + // Returns true on success, false on error. + bool GenerateFnVarConstants(); + + // Determines which functions in the base module are called by which function + // variants. + void CollectBaseFnCalls(); + + // Combines OpFunctionCall instructions collected with CollectBaseFnCalls() + // using conditional copy. + void CombineBaseFnCalls(IRContext* linked_context); + + // Decorates instructions shared between modules with ConditionalINTEL or + // generates conditional capabilities and extensions, depending on which + // variants are used by each. + void CombineInstructions(IRContext* linked_context); + + // Accumulates all errors encountered during processing. + std::stringstream err_; + + // Collection of VariantDef instances + std::vector<VariantDef> variant_defs_; + + // Used for combining OpFunctionCall instructions + BaseFnCalls base_fn_calls_; + + // Used for determining which function variant uses which (applicable) + // instruction + FnVarUsage fnvar_usage_; +}; + +} // namespace spvtools + +#endif // FNVAR_H
diff --git a/source/link/linker.cpp b/source/link/linker.cpp index e6aa72e..002cc41 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp
@@ -15,9 +15,10 @@ #include "spirv-tools/linker.hpp" #include <algorithm> +#include <cstdint> #include <cstdio> #include <cstring> -#include <iostream> +#include <functional> #include <memory> #include <numeric> #include <string> @@ -26,19 +27,18 @@ #include <utility> #include <vector> -#include "source/assembly_grammar.h" +#include "fnvar.h" #include "source/diagnostic.h" #include "source/opt/build_module.h" #include "source/opt/compact_ids_pass.h" #include "source/opt/decoration_manager.h" #include "source/opt/ir_builder.h" -#include "source/opt/ir_loader.h" #include "source/opt/pass_manager.h" #include "source/opt/remove_duplicates_pass.h" #include "source/opt/remove_unused_interface_variables_pass.h" #include "source/opt/type_manager.h" #include "source/spirv_constant.h" -#include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/util/make_unique.h" #include "source/util/string_utils.h" #include "spirv-tools/libspirv.hpp" @@ -103,7 +103,6 @@ // |linked_context| should not be null. spv_result_t MergeModules(const MessageConsumer& consumer, const std::vector<Module*>& in_modules, - const AssemblyGrammar& grammar, IRContext* linked_context); // Compute all pairs of import and export and return it in |linkings_to_do|. @@ -246,7 +245,6 @@ spv_result_t MergeModules(const MessageConsumer& consumer, const std::vector<Module*>& input_modules, - const AssemblyGrammar& grammar, IRContext* linked_context) { spv_position_t position = {}; @@ -294,29 +292,33 @@ const uint32_t module_addressing_model = memory_model_inst->GetSingleWordOperand(0u); if (module_addressing_model != linked_addressing_model) { - spv_operand_desc linked_desc = nullptr, module_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - linked_addressing_model, &linked_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - module_addressing_model, &module_desc); + const spvtools::OperandDesc* linked_desc = nullptr; + const spvtools::OperandDesc* module_desc = nullptr; + spvtools::LookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + linked_addressing_model, &linked_desc); + spvtools::LookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + module_addressing_model, &module_desc); return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting addressing models: " << linked_desc->name + << "Conflicting addressing models: " << linked_desc->name().data() << " (input modules 1 through " << i << ") vs " - << module_desc->name << " (input module " << (i + 1) << ")."; + << module_desc->name().data() << " (input module " << (i + 1) + << ")."; } const uint32_t module_memory_model = memory_model_inst->GetSingleWordOperand(1u); if (module_memory_model != linked_memory_model) { - spv_operand_desc linked_desc = nullptr, module_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model, - &linked_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model, - &module_desc); + const spvtools::OperandDesc* linked_desc = nullptr; + const spvtools::OperandDesc* module_desc = nullptr; + spvtools::LookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, + linked_memory_model, &linked_desc); + spvtools::LookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, + module_memory_model, &module_desc); return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting memory models: " << linked_desc->name + << "Conflicting memory models: " << linked_desc->name().data() << " (input modules 1 through " << i << ") vs " - << module_desc->name << " (input module " << (i + 1) << ")."; + << module_desc->name().data() << " (input module " << (i + 1) + << ")."; } } linked_module->SetMemoryModel(std::unique_ptr<Instruction>( @@ -326,18 +328,21 @@ for (const auto& module : input_modules) for (const auto& inst : module->entry_points()) { const uint32_t model = inst.GetSingleWordInOperand(0); - const std::string name = inst.GetInOperand(2).AsString(); + const std::string name = + inst.opcode() == spv::Op::OpConditionalEntryPointINTEL + ? inst.GetOperand(3).AsString() + : inst.GetOperand(2).AsString(); const auto i = std::find_if( entry_points.begin(), entry_points.end(), [model, name](const std::pair<uint32_t, std::string>& v) { return v.first == model && v.second == name; }); if (i != entry_points.end()) { - spv_operand_desc desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc); + const spvtools::OperandDesc* desc = nullptr; + spvtools::LookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc); return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) << "The entry point \"" << name << "\", with execution model " - << desc->name << ", was already defined."; + << desc->name().data() << ", was already defined."; } linked_module->AddEntryPoint( std::unique_ptr<Instruction>(inst.Clone(linked_context))); @@ -420,6 +425,7 @@ std::vector<LinkageSymbolInfo> imports; std::unordered_map<std::string, std::vector<LinkageSymbolInfo>> exports; + std::unordered_map<std::string, LinkageSymbolInfo> linkonce; // Figure out the imports and exports for (const auto& decoration : linked_context.annotations()) { @@ -478,10 +484,24 @@ << " LinkageAttributes; " << id << " is neither of them.\n"; } - if (spv::LinkageType(type) == spv::LinkageType::Import) + if (spv::LinkageType(type) == spv::LinkageType::Import) { imports.push_back(symbol_info); - else if (spv::LinkageType(type) == spv::LinkageType::Export) + } else if (spv::LinkageType(type) == spv::LinkageType::Export) { exports[symbol_info.name].push_back(symbol_info); + } else if (spv::LinkageType(type) == spv::LinkageType::LinkOnceODR) { + if (linkonce.find(symbol_info.name) == linkonce.end()) + linkonce[symbol_info.name] = symbol_info; + } + } + + for (const auto& possible_export : linkonce) { + if (exports.find(possible_export.first) == exports.end()) + exports[possible_export.first].push_back(possible_export.second); + else + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Combination of Export and LinkOnceODR is not allowed, found " + "for \"" + << possible_export.second.name << "\"."; } // Find the import/export pairs @@ -661,8 +681,10 @@ if (inst->opcode() == spv::Op::OpDecorate && spv::Decoration(inst->GetSingleWordOperand(1u)) == spv::Decoration::LinkageAttributes && - spv::LinkageType(inst->GetSingleWordOperand(3u)) == - spv::LinkageType::Export) { + (spv::LinkageType(inst->GetSingleWordOperand(3u)) == + spv::LinkageType::Export || + spv::LinkageType(inst->GetSingleWordOperand(3u)) == + spv::LinkageType::LinkOnceODR)) { linked_context->KillInst(&*inst); } } @@ -709,8 +731,7 @@ if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND) DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING) << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1) - << ", was exceeded:" - << " " << max_id_bound << " is the current ID bound.\n" + << ", was exceeded: " << max_id_bound << " is the current ID bound.\n" << "The resulting module might not be supported by all " "implementations."; @@ -721,8 +742,8 @@ if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX) DiagnosticStream(position, consumer, "", SPV_WARNING) << "The minimum limit of global values, " - << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;" - << " " << num_global_values << " global values were found.\n" + << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded; " + << num_global_values << " global values were found.\n" << "The resulting module might not be supported by all " "implementations."; @@ -834,6 +855,22 @@ ir_contexts.push_back(std::move(ir_context)); } + const bool make_multitarget = !options.GetFnVarArchitecturesCsv().empty() || + !options.GetFnVarTargetsCsv().empty(); + + VariantDefs variant_defs; + + if (make_multitarget) { + if (!variant_defs.ProcessFnVar(options, modules)) { + return DiagnosticStream(position, consumer, "", SPV_ERROR_FNVAR) + << variant_defs.GetErr(); + } + if (!variant_defs.ProcessVariantDefs()) { + return DiagnosticStream(position, consumer, "", SPV_ERROR_FNVAR) + << variant_defs.GetErr(); + } + } + // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint // range from the other binaries, and compute the new ID bound. uint32_t max_id_bound = 0u; @@ -847,9 +884,12 @@ IRContext linked_context(c_context->target_env, consumer); linked_context.module()->SetHeader(header); + if (make_multitarget) { + variant_defs.GenerateHeader(&linked_context); + } + // Phase 3: Merge all the binaries into a single one. - AssemblyGrammar grammar(c_context); - res = MergeModules(consumer, modules, grammar, &linked_context); + res = MergeModules(consumer, modules, &linked_context); if (res != SPV_SUCCESS) return res; if (options.GetVerifyIds()) { @@ -864,6 +904,10 @@ opt::Pass::Status pass_res = manager.Run(&linked_context); if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + if (make_multitarget) { + variant_defs.CombineVariantInstructions(&linked_context); + } + // Phase 5: Find the import/export pairs LinkageTable linkings_to_do; res = GetImportExportPairs(consumer, linked_context,
diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt index 4704beb..c2636ae 100644 --- a/source/lint/CMakeLists.txt +++ b/source/lint/CMakeLists.txt
@@ -20,11 +20,6 @@ lint_divergent_derivatives.cpp ) -if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) - # Enable parallel builds across four cores for this lib. - add_definitions(/MP4) -endif() - add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES}) spvtools_default_compile_options(SPIRV-Tools-lint)
diff --git a/source/enum_string_mapping.cpp b/source/mimalloc.cpp similarity index 65% copy from source/enum_string_mapping.cpp copy to source/mimalloc.cpp index 32361a0..777cd6f 100644 --- a/source/enum_string_mapping.cpp +++ b/source/mimalloc.cpp
@@ -1,29 +1,15 @@ -// Copyright (c) 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "source/enum_string_mapping.h" - -#include <algorithm> -#include <cassert> -#include <cstring> -#include <string> -#include <unordered_map> - -#include "source/extensions.h" - -namespace spvtools { - -#include "enum_string_mapping.inc" - -} // namespace spvtools +// Copyright (c) 2025 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 "mimalloc-new-delete.h"
diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp index 7e5f091..798c609 100644 --- a/source/name_mapper.cpp +++ b/source/name_mapper.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,6 +27,7 @@ #include "source/binary.h" #include "source/latest_version_spirv_header.h" #include "source/parsed_operand.h" +#include "source/table2.h" #include "source/to_string.h" #include "spirv-tools/libspirv.h" @@ -36,12 +39,12 @@ FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context, const uint32_t* code, - const size_t wordCount) + const size_t wordCount, uint32_t options) : grammar_(AssemblyGrammar(context)) { spv_diagnostic diag = nullptr; // We don't care if the parse fails. - spvBinaryParse(context, this, code, wordCount, nullptr, - ParseInstructionForwarder, &diag); + spvBinaryParseWithOptions(context, this, code, wordCount, nullptr, + ParseInstructionForwarder, &diag, options); spvDiagnosticDestroy(diag); } @@ -209,7 +212,20 @@ } break; case spv::Op::OpTypeFloat: { const auto bit_width = inst.words[2]; - // TODO: Handle optional fpencoding enum once actually used. + if (inst.num_words > 3) { + if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::BFloat16KHR) { + SaveName(result_id, "bfloat16"); + break; + } + if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float8E4M3EXT) { + SaveName(result_id, "fp8e4m3"); + break; + } + if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float8E5M2EXT) { + SaveName(result_id, "fp8e5m2"); + break; + } + } switch (bit_width) { case 16: SaveName(result_id, "half"); @@ -241,6 +257,10 @@ SaveName(result_id, std::string("_runtimearr_") + NameForId(inst.words[2])); break; + case spv::Op::OpTypeNodePayloadArrayAMDX: + SaveName(result_id, + std::string("_payloadarr_") + NameForId(inst.words[2])); + break; case spv::Op::OpTypePointer: SaveName(result_id, std::string("_ptr_") + NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS, @@ -316,9 +336,9 @@ std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type, uint32_t word) { - spv_operand_desc desc = nullptr; - if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) { - return desc->name; + const spvtools::OperandDesc* desc = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(type, word, &desc)) { + return desc->name().data(); } else { // Invalid input. Just give something. return std::string("StorageClass") + to_string(word);
diff --git a/source/name_mapper.h b/source/name_mapper.h index 6902141..106a012 100644 --- a/source/name_mapper.h +++ b/source/name_mapper.h
@@ -63,9 +63,12 @@ public: // Construct a friendly name mapper, and determine friendly names for each // defined Id in the specified module. The module is specified by the code - // wordCount, and should be parseable in the specified context. + // wordCount, and should be parseable in the specified context. The options + // bitmask is passed to the binary parser; pass + // SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES to tolerate unknown + // opcodes in the module. FriendlyNameMapper(const spv_const_context context, const uint32_t* code, - const size_t wordCount); + const size_t wordCount, uint32_t options = 0); // Returns a NameMapper which maps ids to the friendly names parsed from the // module provided to the constructor.
diff --git a/source/opcode.cpp b/source/opcode.cpp index 2b25fc3..43fcd5e 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp
@@ -1,6 +1,6 @@ // Copyright (c) 2015-2022 The Khronos Group Inc. -// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights -// reserved. +// Modifications Copyright (C) 2020-2024 Advanced Micro Devices, Inc. All +// rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,18 +27,10 @@ #include "source/spirv_constant.h" #include "source/spirv_endian.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "spirv-tools/libspirv.h" namespace { -struct OpcodeDescPtrLen { - const spv_opcode_desc_t* ptr; - uint32_t len; -}; - -#include "core.insts-unified1.inc" - -static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), - kOpcodeTableEntries}; // Represents a vendor tool entry in the SPIR-V XML Registry. struct VendorTool { @@ -78,115 +70,6 @@ } } -spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) { - if (!pInstTable) return SPV_ERROR_INVALID_POINTER; - - // Descriptions of each opcode. Each entry describes the format of the - // instruction that follows a particular opcode. - - *pInstTable = &kOpcodeTable; - return SPV_SUCCESS; -} - -spv_result_t spvOpcodeTableNameLookup(spv_target_env env, - const spv_opcode_table table, - const char* name, - spv_opcode_desc* pEntry) { - if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; - if (!table) return SPV_ERROR_INVALID_TABLE; - - // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be - // preferable but the table requires sorting on the Opcode name, but it's - // static const initialized and matches the order of the spec. - const size_t nameLength = strlen(name); - const auto version = spvVersionForTargetEnv(env); - for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) { - const spv_opcode_desc_t& entry = table->entries[opcodeIndex]; - // We consider the current opcode as available as long as - // 1. The target environment satisfies the minimal requirement of the - // opcode; or - // 2. There is at least one extension enabling this opcode. - // - // Note that the second rule assumes the extension enabling this instruction - // is indeed requested in the SPIR-V code; checking that should be - // validator's work. - if ((version >= entry.minVersion && version <= entry.lastVersion) || - entry.numExtensions > 0u || entry.numCapabilities > 0u) { - // Exact match case. - if (nameLength == strlen(entry.name) && - !strncmp(name, entry.name, nameLength)) { - *pEntry = &entry; - return SPV_SUCCESS; - } - // Lack of binary search really hurts here. There isn't an easy filter to - // apply before checking aliases since we need to handle promotion from - // vendor to KHR/EXT and KHR/EXT to core. It would require a sure-fire way - // of dropping suffices. Fortunately, most lookup are based on token - // value. - // - // If this was a binary search we could iterate between the lower and - // upper bounds. - if (entry.numAliases > 0) { - for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases; - aliasIndex++) { - // Skip Op prefix. Should this be encoded in the table instead? - const auto alias = entry.aliases[aliasIndex] + 2; - const size_t aliasLength = strlen(alias); - if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) { - *pEntry = &entry; - return SPV_SUCCESS; - } - } - } - } - } - - return SPV_ERROR_INVALID_LOOKUP; -} - -spv_result_t spvOpcodeTableValueLookup(spv_target_env env, - const spv_opcode_table table, - const spv::Op opcode, - spv_opcode_desc* pEntry) { - if (!table) return SPV_ERROR_INVALID_TABLE; - if (!pEntry) return SPV_ERROR_INVALID_POINTER; - - const auto beg = table->entries; - const auto end = table->entries + table->count; - - spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, 0, - {}, false, false, 0, nullptr, ~0u, ~0u}; - - auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { - return lhs.opcode < rhs.opcode; - }; - - // We need to loop here because there can exist multiple symbols for the same - // opcode value, and they can be introduced in different target environments, - // which means they can have different minimal version requirements. - // Assumes the underlying table is already sorted ascendingly according to - // opcode value. - const auto version = spvVersionForTargetEnv(env); - for (auto it = std::lower_bound(beg, end, needle, comp); - it != end && it->opcode == opcode; ++it) { - // We considers the current opcode as available as long as - // 1. The target environment satisfies the minimal requirement of the - // opcode; or - // 2. There is at least one extension enabling this opcode. - // - // Note that the second rule assumes the extension enabling this instruction - // is indeed requested in the SPIR-V code; checking that should be - // validator's work. - if ((version >= it->minVersion && version <= it->lastVersion) || - it->numExtensions > 0u || it->numCapabilities > 0u) { - *pEntry = it; - return SPV_SUCCESS; - } - } - - return SPV_ERROR_INVALID_LOOKUP; -} - void spvInstructionCopy(const uint32_t* words, const spv::Op opcode, const uint16_t wordCount, const spv_endianness_t endian, spv_instruction_t* pInst) { @@ -205,25 +88,13 @@ } const char* spvOpcodeString(const uint32_t opcode) { - const auto beg = kOpcodeTableEntries; - const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries); - spv_opcode_desc_t needle = {"", static_cast<spv::Op>(opcode), - 0, nullptr, - 0, {}, - 0, {}, - false, false, - 0, nullptr, - ~0u, ~0u}; - auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { - return lhs.opcode < rhs.opcode; - }; - auto it = std::lower_bound(beg, end, needle, comp); - if (it != end && it->opcode == spv::Op(opcode)) { - return it->name; + const spvtools::InstructionDesc* desc = nullptr; + if (SPV_SUCCESS != + spvtools::LookupOpcode(static_cast<spv::Op>(opcode), &desc)) { + assert(0 && "Unreachable!"); + return "unknown"; } - - assert(0 && "Unreachable!"); - return "unknown"; + return desc->name().data(); } const char* spvOpcodeString(const spv::Op opcode) { @@ -249,6 +120,10 @@ case spv::Op::OpSpecConstantComposite: case spv::Op::OpSpecConstantCompositeReplicateEXT: case spv::Op::OpSpecConstantOp: + case spv::Op::OpSpecConstantArchitectureINTEL: + case spv::Op::OpSpecConstantTargetINTEL: + case spv::Op::OpSpecConstantCapabilitiesINTEL: + case spv::Op::OpSpecConstantDataKHR: return true; default: return false; @@ -265,12 +140,23 @@ case spv::Op::OpConstantSampler: case spv::Op::OpConstantNull: case spv::Op::OpConstantFunctionPointerINTEL: + case spv::Op::OpConstantStringAMDX: case spv::Op::OpSpecConstantTrue: case spv::Op::OpSpecConstantFalse: case spv::Op::OpSpecConstant: case spv::Op::OpSpecConstantComposite: case spv::Op::OpSpecConstantCompositeReplicateEXT: case spv::Op::OpSpecConstantOp: + case spv::Op::OpSpecConstantStringAMDX: + case spv::Op::OpGraphConstantARM: + case spv::Op::OpAsmTargetINTEL: + case spv::Op::OpAsmINTEL: + case spv::Op::OpSpecConstantArchitectureINTEL: + case spv::Op::OpSpecConstantTargetINTEL: + case spv::Op::OpSpecConstantCapabilitiesINTEL: + case spv::Op::OpConstantSizeOfEXT: + case spv::Op::OpConstantDataKHR: + case spv::Op::OpSpecConstantDataKHR: return true; default: return false; @@ -281,17 +167,6 @@ return opcode == spv::Op::OpUndef || spvOpcodeIsConstant(opcode); } -bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode) { - switch (opcode) { - case spv::Op::OpSpecConstantTrue: - case spv::Op::OpSpecConstantFalse: - case spv::Op::OpSpecConstant: - return true; - default: - return false; - } -} - int32_t spvOpcodeIsComposite(const spv::Op opcode) { switch (opcode) { case spv::Op::OpTypeVector: @@ -301,6 +176,7 @@ case spv::Op::OpTypeRuntimeArray: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: return true; default: return false; @@ -315,9 +191,12 @@ case spv::Op::OpInBoundsAccessChain: case spv::Op::OpUntypedAccessChainKHR: case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpBufferPointerEXT: case spv::Op::OpFunctionParameter: case spv::Op::OpImageTexelPointer: + case spv::Op::OpUntypedImageTexelPointerEXT: case spv::Op::OpCopyObject: + case spv::Op::OpAllocateNodePayloadsAMDX: case spv::Op::OpSelect: case spv::Op::OpPhi: case spv::Op::OpFunctionCall: @@ -340,10 +219,13 @@ case spv::Op::OpInBoundsAccessChain: case spv::Op::OpUntypedAccessChainKHR: case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpBufferPointerEXT: case spv::Op::OpFunctionParameter: case spv::Op::OpImageTexelPointer: + case spv::Op::OpUntypedImageTexelPointerEXT: case spv::Op::OpCopyObject: case spv::Op::OpRawAccessChainNV: + case spv::Op::OpAllocateNodePayloadsAMDX: return true; default: return false; @@ -377,13 +259,20 @@ case spv::Op::OpTypeAccelerationStructureNV: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: // case spv::Op::OpTypeAccelerationStructureKHR: covered by // spv::Op::OpTypeAccelerationStructureNV case spv::Op::OpTypeRayQueryKHR: case spv::Op::OpTypeHitObjectNV: + case spv::Op::OpTypeHitObjectEXT: case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypeNodePayloadArrayAMDX: case spv::Op::OpTypeTensorLayoutNV: case spv::Op::OpTypeTensorViewNV: + case spv::Op::OpTypeTensorARM: + case spv::Op::OpTypeTaskSequenceINTEL: + case spv::Op::OpTypeGraphARM: + case spv::Op::OpTypeBufferEXT: return true; default: // In particular, OpTypeForwardPointer does not generate a type, @@ -399,6 +288,7 @@ case spv::Op::OpDecorate: case spv::Op::OpDecorateId: case spv::Op::OpMemberDecorate: + case spv::Op::OpMemberDecorateIdEXT: case spv::Op::OpGroupDecorate: case spv::Op::OpGroupMemberDecorate: case spv::Op::OpDecorateStringGOOGLE: @@ -421,6 +311,7 @@ case spv::Op::OpImageSampleProjExplicitLod: case spv::Op::OpImageSampleProjDrefImplicitLod: case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSampleFootprintNV: case spv::Op::OpImageFetch: case spv::Op::OpImageGather: case spv::Op::OpImageDrefGather: @@ -501,6 +392,7 @@ case spv::Op::OpTerminateRayKHR: case spv::Op::OpIgnoreIntersectionKHR: case spv::Op::OpEmitMeshTasksEXT: + case spv::Op::OpAbortKHR: return true; default: return false; @@ -747,6 +639,7 @@ case spv::Op::OpImageSparseSampleExplicitLod: case spv::Op::OpImageSparseSampleDrefImplicitLod: case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSampleFootprintNV: return true; default: return false;
diff --git a/source/opcode.h b/source/opcode.h index 08fc56d..3469c3b 100644 --- a/source/opcode.h +++ b/source/opcode.h
@@ -35,19 +35,6 @@ void spvOpcodeSplit(const uint32_t word, uint16_t* word_count, uint16_t* opcode); -// Finds the named opcode in the given opcode table. On success, returns -// SPV_SUCCESS and writes a handle of the table entry into *entry. -spv_result_t spvOpcodeTableNameLookup(spv_target_env, - const spv_opcode_table table, - const char* name, spv_opcode_desc* entry); - -// Finds the opcode by enumerant in the given opcode table. On success, returns -// SPV_SUCCESS and writes a handle of the table entry into *entry. -spv_result_t spvOpcodeTableValueLookup(spv_target_env, - const spv_opcode_table table, - const spv::Op opcode, - spv_opcode_desc* entry); - // Copies an instruction's word and fixes the endianness to host native. The // source instruction's stream/opcode/endianness is in the words/opcode/endian // parameter. The word_count parameter specifies the number of words to copy. @@ -71,9 +58,6 @@ // Returns true if the given opcode is a constant or undef. bool spvOpcodeIsConstantOrUndef(const spv::Op opcode); -// Returns true if the given opcode is a scalar specialization constant. -bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode); - // Determines if the given opcode is a composite type. Returns zero if false, // non-zero otherwise. int32_t spvOpcodeIsComposite(const spv::Op opcode);
diff --git a/source/operand.cpp b/source/operand.cpp index 5485646..3bb800f 100644 --- a/source/operand.cpp +++ b/source/operand.cpp
@@ -26,110 +26,9 @@ #include "source/macro.h" #include "source/opcode.h" #include "source/spirv_constant.h" - -// For now, assume unified1 contains up to SPIR-V 1.3 and no later -// SPIR-V version. -// TODO(dneto): Make one set of tables, but with version tags on a -// per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195 - -#include "operand.kinds-unified1.inc" +#include "source/table2.h" #include "spirv-tools/libspirv.h" -static const spv_operand_table_t kOperandTable = { - ARRAY_SIZE(pygen_variable_OperandInfoTable), - pygen_variable_OperandInfoTable}; - -spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable, - spv_target_env) { - if (!pOperandTable) return SPV_ERROR_INVALID_POINTER; - - *pOperandTable = &kOperandTable; - return SPV_SUCCESS; -} - -spv_result_t spvOperandTableNameLookup(spv_target_env, - const spv_operand_table table, - const spv_operand_type_t type, - const char* name, - const size_t nameLength, - spv_operand_desc* pEntry) { - if (!table) return SPV_ERROR_INVALID_TABLE; - if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; - - for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { - const auto& group = table->types[typeIndex]; - if (type != group.type) continue; - for (uint64_t index = 0; index < group.count; ++index) { - const auto& entry = group.entries[index]; - // We consider the current operand as available as long as - // it is in the grammar. It might not be *valid* to use, - // but that should be checked by the validator, not by parsing. - // - // Exact match case - if (nameLength == strlen(entry.name) && - !strncmp(entry.name, name, nameLength)) { - *pEntry = &entry; - return SPV_SUCCESS; - } - - // Check the aliases. Ideally we would have a version of the table sorted - // by name and then we could iterate between the lower and upper bounds to - // restrict the amount comparisons. Fortunately, name-based lookups are - // mostly restricted to the assembler. - if (entry.numAliases > 0) { - for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases; - aliasIndex++) { - const auto alias = entry.aliases[aliasIndex]; - const size_t aliasLength = strlen(alias); - if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) { - *pEntry = &entry; - return SPV_SUCCESS; - } - } - } - } - } - - return SPV_ERROR_INVALID_LOOKUP; -} - -spv_result_t spvOperandTableValueLookup(spv_target_env, - const spv_operand_table table, - const spv_operand_type_t type, - const uint32_t value, - spv_operand_desc* pEntry) { - if (!table) return SPV_ERROR_INVALID_TABLE; - if (!pEntry) return SPV_ERROR_INVALID_POINTER; - - spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr, - 0, nullptr, {}, ~0u, ~0u}; - - auto comp = [](const spv_operand_desc_t& lhs, const spv_operand_desc_t& rhs) { - return lhs.value < rhs.value; - }; - - for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { - const auto& group = table->types[typeIndex]; - if (type != group.type) continue; - - const auto beg = group.entries; - const auto end = group.entries + group.count; - - // Assumes the underlying table is already sorted ascendingly according to - // opcode value. - auto it = std::lower_bound(beg, end, needle, comp); - if (it != end && it->value == value) { - // The current operand is considered available as long as - // it is in the grammar. It might not be *valid* to use, - // but that should be checked by the validator, not by parsing. - *pEntry = it; - return SPV_SUCCESS; - } - } - - return SPV_ERROR_INVALID_LOOKUP; -} - const char* spvOperandTypeStr(spv_operand_type_t type) { switch (type) { case SPV_OPERAND_TYPE_ID: @@ -212,6 +111,7 @@ case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: return "kernel profiling info"; case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: return "capability"; case SPV_OPERAND_TYPE_RAY_FLAGS: return "ray flags"; @@ -237,6 +137,12 @@ return "cooperative matrix reduce"; case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: return "tensor addressing operands"; + case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + return "matrix multiply accumulate operands"; + case SPV_OPERAND_TYPE_TENSOR_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS: + return "tensor operands"; case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER: return "initialization mode qualifier"; case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER: @@ -297,6 +203,28 @@ return "quantization mode"; case SPV_OPERAND_TYPE_OVERFLOW_MODES: return "overflow mode"; + case SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT: + return "cooperative vector matrix layout"; + case SPV_OPERAND_TYPE_COMPONENT_TYPE: + return "component type"; + + case SPV_OPERAND_TYPE_KERNEL_PROPERTY_FLAGS: + return "kernel property flags"; + case SPV_OPERAND_TYPE_SHDEBUG100_BUILD_IDENTIFIER_FLAGS: + return "NonSemantic.Shader.DebugInfo.100 debug build identifier flags"; + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + return "NonSemantic.Shader.DebugInfo.100 debug base type attribute " + "encoding"; + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_COMPOSITE_TYPE: + return "NonSemantic.Shader.DebugInfo.100 debug composite type"; + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_IMPORTED_ENTITY: + return "NonSemantic.Shader.DebugInfo.100 debug imported entity"; + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS: + return "NonSemantic.Shader.DebugInfo.100 debug info flags"; + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION: + return "NonSemantic.Shader.DebugInfo.100 debug operation"; + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER: + return "NonSemantic.Shader.DebugInfo.100 debug type qualifier"; case SPV_OPERAND_TYPE_NONE: return "NONE"; @@ -308,6 +236,7 @@ void spvPushOperandTypes(const spv_operand_type_t* types, spv_operand_pattern_t* pattern) { + // Push them on in backward order. const spv_operand_type_t* endTypes; for (endTypes = types; *endTypes != SPV_OPERAND_TYPE_NONE; ++endTypes) { } @@ -317,9 +246,22 @@ } } -void spvPushOperandTypesForMask(spv_target_env env, - const spv_operand_table operandTable, - const spv_operand_type_t type, +void spvPushOperandTypes( + const spvtools::utils::Span<const spv_operand_type_t>& types, + spv_operand_pattern_t* pattern) { + // Push them on in backward order. + auto n = types.size(); + for (auto i = 0u; i < n; i++) { + auto type = types[n - 1 - i]; + // Check against the NONE type, in case the tables have them. + // This might be cleaned up. + if (type != SPV_OPERAND_TYPE_NONE) { + pattern->push_back(type); + } + } +} + +void spvPushOperandTypesForMask(const spv_operand_type_t type, const uint32_t mask, spv_operand_pattern_t* pattern) { // Scan from highest bits to lowest bits because we will append in LIFO @@ -327,10 +269,9 @@ for (uint32_t candidate_bit = (1u << 31u); candidate_bit; candidate_bit >>= 1) { if (candidate_bit & mask) { - spv_operand_desc entry = nullptr; - if (SPV_SUCCESS == spvOperandTableValueLookup(env, operandTable, type, - candidate_bit, &entry)) { - spvPushOperandTypes(entry->operandTypes, pattern); + const spvtools::OperandDesc* entry = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(type, candidate_bit, &entry)) { + spvPushOperandTypes(entry->operands(), pattern); } } } @@ -396,6 +337,16 @@ case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: case SPV_OPERAND_TYPE_FPENCODING: case SPV_OPERAND_TYPE_TENSOR_CLAMP_MODE: + case SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT: + case SPV_OPERAND_TYPE_COMPONENT_TYPE: + case SPV_OPERAND_TYPE_KERNEL_PROPERTY_FLAGS: + case SPV_OPERAND_TYPE_SHDEBUG100_BUILD_IDENTIFIER_FLAGS: + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER: return true; default: break; @@ -415,9 +366,11 @@ case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: + case SPV_OPERAND_TYPE_TENSOR_OPERANDS: return true; default: break; @@ -437,9 +390,12 @@ case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_CIV: case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING: + case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY: return true; default: break; @@ -454,6 +410,7 @@ case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER: case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID: case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_VARIABLE_CAPABILITY: return true; default: break; @@ -485,6 +442,10 @@ pattern->push_back(SPV_OPERAND_TYPE_LITERAL_INTEGER); pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID); return true; + case SPV_OPERAND_TYPE_VARIABLE_CAPABILITY: + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY); + return true; default: break; } @@ -562,11 +523,15 @@ case spv::Op::OpSelectionMerge: case spv::Op::OpDecorate: case spv::Op::OpMemberDecorate: + case spv::Op::OpMemberDecorateIdEXT: case spv::Op::OpDecorateId: case spv::Op::OpDecorateStringGOOGLE: case spv::Op::OpMemberDecorateStringGOOGLE: case spv::Op::OpBranch: case spv::Op::OpLoopMerge: + case spv::Op::OpConditionalEntryPointINTEL: + case spv::Op::OpConditionalCapabilityINTEL: + case spv::Op::OpConditionalExtensionINTEL: out = [](unsigned) { return true; }; break; case spv::Op::OpGroupDecorate: @@ -581,6 +546,11 @@ out = [](unsigned index) { return index == 2; }; break; + case spv::Op::OpConstantFunctionPointerINTEL: + // The Function parameter. + out = [](unsigned index) { return index == 2; }; + break; + case spv::Op::OpPhi: out = [](unsigned index) { return index > 1; }; break; @@ -617,6 +587,9 @@ // approximate, due to variable operands out = [](unsigned index) { return index > 6; }; break; + case spv::Op::OpGraphEntryPointARM: + out = [](unsigned index) { return index == 0; }; + break; default: out = [](unsigned) { return false; }; break; @@ -665,3 +638,17 @@ } return out; } + +spv_fp_encoding_t spvFPEncodingFromOperandFPEncoding(spv::FPEncoding encoding) { + switch (encoding) { + case spv::FPEncoding::BFloat16KHR: + return SPV_FP_ENCODING_BFLOAT16; + case spv::FPEncoding::Float8E4M3EXT: + return SPV_FP_ENCODING_FLOAT8_E4M3; + case spv::FPEncoding::Float8E5M2EXT: + return SPV_FP_ENCODING_FLOAT8_E5M2; + case spv::FPEncoding::Max: + break; + } + return SPV_FP_ENCODING_UNKNOWN; +}
diff --git a/source/operand.h b/source/operand.h index 3d42a05..9fc9b74 100644 --- a/source/operand.h +++ b/source/operand.h
@@ -19,6 +19,7 @@ #include <vector> #include "source/table.h" +#include "source/util/span.h" #include "spirv-tools/libspirv.h" // A sequence of operand types. @@ -35,25 +36,6 @@ // performance. using spv_operand_pattern_t = std::vector<spv_operand_type_t>; -// Finds the named operand in the table. The type parameter specifies the -// operand's group. A handle of the operand table entry for this operand will -// be written into *entry. -spv_result_t spvOperandTableNameLookup(spv_target_env, - const spv_operand_table table, - const spv_operand_type_t type, - const char* name, - const size_t name_length, - spv_operand_desc* entry); - -// Finds the operand with value in the table. The type parameter specifies the -// operand's group. A handle of the operand table entry for this operand will -// be written into *entry. -spv_result_t spvOperandTableValueLookup(spv_target_env, - const spv_operand_table table, - const spv_operand_type_t type, - const uint32_t value, - spv_operand_desc* entry); - // Gets the name string of the non-variable operand type. const char* spvOperandTypeStr(spv_operand_type_t type); @@ -68,10 +50,10 @@ bool spvOperandIsVariable(spv_operand_type_t type); // Append a list of operand types to the end of the pattern vector. -// The types parameter specifies the source array of types, ending with -// SPV_OPERAND_TYPE_NONE. -void spvPushOperandTypes(const spv_operand_type_t* types, - spv_operand_pattern_t* pattern); +// The types parameter specifies the source span of types. +void spvPushOperandTypes( + const spvtools::utils::Span<const spv_operand_type_t>& types, + spv_operand_pattern_t* pattern); // Appends the operands expected after the given typed mask onto the // end of the given pattern. @@ -81,9 +63,7 @@ // appear after operands for a more significant bit. // // If a set bit is unknown, then we assume it has no operands. -void spvPushOperandTypesForMask(spv_target_env, - const spv_operand_table operand_table, - const spv_operand_type_t mask_type, +void spvPushOperandTypesForMask(const spv_operand_type_t mask_type, const uint32_t mask, spv_operand_pattern_t* pattern); @@ -142,4 +122,7 @@ std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction( spv::Op opcode, spv_ext_inst_type_t ext_type, uint32_t key); +// Converts an spv::FPEncoding to spv_fp_encoding_t +spv_fp_encoding_t spvFPEncodingFromOperandFPEncoding(spv::FPEncoding encoding); + #endif // SOURCE_OPERAND_H_
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 4a12e6e..a0ca5b8 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt
@@ -59,6 +59,7 @@ fold_spec_constant_op_and_composite_pass.h freeze_spec_constant_value_pass.h function.h + graph.h graphics_robust_access_pass.h if_conversion.h inline_exhaustive_pass.h @@ -74,6 +75,7 @@ ir_context.h ir_loader.h licm_pass.h + legalize_multidim_array_pass.h liveness.h local_access_chain_convert_pass.h local_redundancy_elimination.h @@ -104,16 +106,19 @@ reflect.h register_pressure.h relax_float_ops_pass.h + canonicalize_ids_pass.h remove_dontinline_pass.h remove_duplicates_pass.h remove_unused_interface_variables_pass.h replace_desc_array_access_using_var_index.h replace_invalid_opc.h + resolve_binding_conflicts_pass.h scalar_analysis.h scalar_analysis_nodes.h scalar_replacement_pass.h set_spec_constant_default_value_pass.h simplification_pass.h + split_combined_image_sampler_pass.h spread_volatile_semantics.h ssa_rewrite_pass.h strength_reduction_pass.h @@ -178,6 +183,7 @@ fold_spec_constant_op_and_composite_pass.cpp freeze_spec_constant_value_pass.cpp function.cpp + graph.cpp graphics_robust_access_pass.cpp if_conversion.cpp inline_exhaustive_pass.cpp @@ -192,6 +198,7 @@ ir_context.cpp ir_loader.cpp licm_pass.cpp + legalize_multidim_array_pass.cpp liveness.cpp local_access_chain_convert_pass.cpp local_redundancy_elimination.cpp @@ -220,16 +227,19 @@ redundancy_elimination.cpp register_pressure.cpp relax_float_ops_pass.cpp + canonicalize_ids_pass.cpp remove_dontinline_pass.cpp remove_duplicates_pass.cpp remove_unused_interface_variables_pass.cpp replace_desc_array_access_using_var_index.cpp replace_invalid_opc.cpp + resolve_binding_conflicts_pass.cpp scalar_analysis.cpp scalar_analysis_simplification.cpp scalar_replacement_pass.cpp set_spec_constant_default_value_pass.cpp simplification_pass.cpp + split_combined_image_sampler_pass.cpp spread_volatile_semantics.cpp ssa_rewrite_pass.cpp strength_reduction_pass.cpp @@ -249,15 +259,18 @@ wrap_opkill.cpp ) -if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) - # Enable parallel builds across four cores for this lib - add_definitions(/MP4) -endif() - spvtools_pch(SPIRV_TOOLS_OPT_SOURCES pch_source_opt) +if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV_TOOLS_OPT_SOURCES ${spirv-tools_SOURCE_DIR}/source/mimalloc.cpp) +endif() + add_library(SPIRV-Tools-opt ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_OPT_SOURCES}) +if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + target_link_libraries(SPIRV-Tools-opt PRIVATE mimalloc-static) +endif() + spvtools_default_compile_options(SPIRV-Tools-opt) target_include_directories(SPIRV-Tools-opt PUBLIC @@ -274,7 +287,13 @@ spvtools_check_symbol_exports(SPIRV-Tools-opt) if(ENABLE_SPIRV_TOOLS_INSTALL) - install(TARGETS SPIRV-Tools-opt EXPORT SPIRV-Tools-optTargets) + set(SPIRV-Tools-opt-InstallTargets SPIRV-Tools-opt) + + if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)) + list(APPEND SPIRV-Tools-opt-InstallTargets mimalloc-static) + endif() + + install(TARGETS ${SPIRV-Tools-opt-InstallTargets} EXPORT SPIRV-Tools-optTargets) export(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake) spvtools_config_package_dir(SPIRV-Tools-opt PACKAGE_DIR)
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index d78d63c..8ca2518 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -44,6 +44,8 @@ constexpr uint32_t kExtInstOpInIdx = 1; constexpr uint32_t kInterpolantInIdx = 2; constexpr uint32_t kCooperativeMatrixLoadSourceAddrInIdx = 0; +constexpr uint32_t kDebugDeclareVariableInIdx = 3; +constexpr uint32_t kDebugValueValueInIdx = 3; // Sorting functor to present annotation instructions in an easy-to-process // order. The functor orders by opcode first and falls back on unique id @@ -126,6 +128,7 @@ switch (user->opcode()) { case spv::Op::OpAccessChain: case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpUntypedAccessChainKHR: case spv::Op::OpCopyObject: this->AddStores(func, user->result_id()); break; @@ -158,14 +161,16 @@ 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 + // Only allow NonSemantic.Shader.DebugInfo (any version) and + // NonSemantic.DebugPrintf; 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() == spv::Op::OpExtInstImport && "Expecting an import of an extension's instruction set."); const std::string extension_name = inst.GetInOperand(0).AsString(); if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && - (extension_name != "NonSemantic.Shader.DebugInfo.100") && + !spvtools::utils::starts_with(extension_name, + "NonSemantic.Shader.DebugInfo.") && (extension_name != "NonSemantic.DebugPrintf")) { return false; } @@ -270,17 +275,93 @@ }); } -bool AggressiveDCEPass::AggressiveDCE(Function* func) { - if (func->IsDeclaration()) return false; +Pass::Status AggressiveDCEPass::AggressiveDCE(Function* func) { + if (func->IsDeclaration()) return Pass::Status::SuccessWithoutChange; std::list<BasicBlock*> structured_order; cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order); live_local_vars_.clear(); InitializeWorkList(func, structured_order); ProcessWorkList(func); + if (ProcessDebugInformation(structured_order) == Pass::Status::Failure) + return Pass::Status::Failure; + ProcessWorkList(func); return KillDeadInstructions(func, structured_order); } -bool AggressiveDCEPass::KillDeadInstructions( +Pass::Status AggressiveDCEPass::ProcessDebugInformation( + std::list<BasicBlock*>& structured_order) { + for (auto bi = structured_order.begin(); bi != structured_order.end(); bi++) { + bool succeeded = (*bi)->WhileEachInst([this](Instruction* inst) { + if (!inst->IsNonSemanticInstruction()) return true; + + if (inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugDeclare) { + if (IsLive(inst)) return true; + + uint32_t var_id = + inst->GetSingleWordInOperand(kDebugDeclareVariableInIdx); + auto var_def = get_def_use_mgr()->GetDef(var_id); + + if (IsLive(var_def)) { + AddToWorklist(inst); + return true; + } + + // DebugDeclare Variable is not live. Find the value that was being + // stored to this variable. If it's live then create a new DebugValue + // with this value. Otherwise let it die in peace. + get_def_use_mgr()->ForEachUser(var_id, [this, + var_id](Instruction* user) { + if (user->opcode() == spv::Op::OpStore) { + uint32_t stored_value_id = 0; + const uint32_t kStoreValueInIdx = 1; + stored_value_id = user->GetSingleWordInOperand(kStoreValueInIdx); + if (!IsLive(get_def_use_mgr()->GetDef(stored_value_id))) { + return true; + } + + // value being stored is still live + Instruction* next_inst = user->NextNode(); + bool added = + context()->get_debug_info_mgr()->AddDebugValueForVariable( + user, var_id, stored_value_id, user); + if (added && next_inst) { + auto new_debug_value = next_inst->PreviousNode(); + AddToWorklist(new_debug_value); + } + } + return true; + }); + } else if (inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugValue) { + uint32_t var_operand_idx = kDebugValueValueInIdx; + uint32_t id = inst->GetSingleWordInOperand(var_operand_idx); + auto def = get_def_use_mgr()->GetDef(id); + + if (IsLive(def)) { + AddToWorklist(inst); + return true; + } + + // Value operand of DebugValue is not live + // Set Value to Undef of appropriate type + uint32_t type_id = def->type_id(); + uint32_t undef_id = Type2Undef(type_id); + if (undef_id == 0) return false; + + inst->SetInOperand(var_operand_idx, {undef_id}); + context()->get_def_use_mgr()->AnalyzeInstUse(inst); + AddToWorklist(inst); + } + return true; + }); + + if (!succeeded) return Pass::Status::Failure; + } + return Pass::Status::SuccessWithoutChange; +} + +Pass::Status AggressiveDCEPass::KillDeadInstructions( const Function* func, std::list<BasicBlock*>& structured_order) { bool modified = false; for (auto bi = structured_order.begin(); bi != structured_order.end();) { @@ -315,6 +396,9 @@ // Find an undef for the return value and make sure it gets kept by // the pass. auto undef_id = Type2Undef(func->type_id()); + if (undef_id == 0) { + return Pass::Status::Failure; + } auto undef = get_def_use_mgr()->GetDef(undef_id); live_insts_.Set(undef->unique_id()); merge_terminator->SetOpcode(spv::Op::OpReturnValue); @@ -333,7 +417,8 @@ ++bi; } } - return modified; + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; } void AggressiveDCEPass::ProcessWorkList(Function* func) { @@ -593,7 +678,7 @@ } } -void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { +Pass::Status AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { // Keep all execution modes. for (auto& exec : get_module()->execution_modes()) { AddToWorklist(&exec); @@ -668,18 +753,27 @@ } if (debug_global_seen) { auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone(); + if (dbg_none == nullptr) { + return Pass::Status::Failure; + } AddToWorklist(dbg_none); } - // Add top level DebugInfo to worklist + // Add DebugInfo which should never be eliminated to worklist for (auto& dbg : get_module()->ext_inst_debuginfo()) { - auto op = dbg.GetShader100DebugOpcode(); - if (op == NonSemanticShaderDebugInfo100DebugCompilationUnit || - op == NonSemanticShaderDebugInfo100DebugEntryPoint || - op == NonSemanticShaderDebugInfo100DebugSourceContinued) { + auto op = dbg.GetShaderDebugOpcode(); + if (op == NonSemanticShaderDebugInfoDebugCompilationUnit || + op == NonSemanticShaderDebugInfoDebugEntryPoint || + op == NonSemanticShaderDebugInfoDebugSource || + op == NonSemanticShaderDebugInfoDebugSourceContinued || + op == NonSemanticShaderDebugInfoDebugLocalVariable || + op == NonSemanticShaderDebugInfoDebugExpression || + op == NonSemanticShaderDebugInfoDebugOperation || + op == NonSemanticShaderDebugInfoDebugBuildIdentifier) { AddToWorklist(&dbg); } } + return Pass::Status::SuccessWithoutChange; } Pass::Status AggressiveDCEPass::ProcessImpl() { @@ -707,7 +801,9 @@ // Eliminate Dead functions. bool modified = EliminateDeadFunctions(); - InitializeModuleScopeLiveInstructions(); + if (InitializeModuleScopeLiveInstructions() == Pass::Status::Failure) { + return Pass::Status::Failure; + } // Run |AggressiveDCE| on the remaining functions. The order does not matter, // since |AggressiveDCE| is intra-procedural. This can mean that function @@ -715,7 +811,13 @@ // function will still be in the module after this pass. We expect this to be // rare. for (Function& fp : *context()->module()) { - modified |= AggressiveDCE(&fp); + Pass::Status function_status = AggressiveDCE(&fp); + if (function_status == Pass::Status::Failure) { + return Pass::Status::Failure; + } + if (function_status == Pass::Status::SuccessWithChange) { + modified = true; + } } // If the decoration manager is kept live then the context will try to keep it @@ -743,7 +845,9 @@ // Cleanup all CFG including all unreachable blocks. for (Function& fp : *context()->module()) { - modified |= CFGCleanup(&fp); + auto status = CFGCleanup(&fp); + if (status == Status::Failure) return Status::Failure; + if (status == Status::SuccessWithChange) modified = true; } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; @@ -913,6 +1017,19 @@ context()->AnalyzeUses(&dbg); continue; } + // Save debug build identifier even if no other instructions refer to it. + if (dbg.GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugBuildIdentifier) { + // The debug build identifier refers to other instructions that + // can potentially be removed, they also need to be kept alive. + dbg.ForEachInId([this](const uint32_t* id) { + Instruction* ref_inst = get_def_use_mgr()->GetDef(*id); + if (ref_inst) { + live_insts_.Set(ref_inst->unique_id()); + } + }); + continue; + } to_kill_.push_back(&dbg); modified = true; } @@ -970,7 +1087,6 @@ void AggressiveDCEPass::InitExtensions() { extensions_allowlist_.clear(); - // clang-format off extensions_allowlist_.insert({ "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_trinary_minmax", @@ -1008,6 +1124,8 @@ "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", + "SPV_EXT_descriptor_heap", + "SPV_KHR_untyped_pointers", "SPV_NV_fragment_shader_barycentric", "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint", @@ -1022,13 +1140,13 @@ "SPV_KHR_physical_storage_buffer", "SPV_KHR_terminate_invocation", "SPV_KHR_shader_clock", - "SPV_KHR_vulkan_memory_model", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", "SPV_KHR_fragment_shader_barycentric", + "SPV_KHR_vulkan_memory_model", "SPV_NV_bindless_texture", "SPV_EXT_shader_atomic_float_add", "SPV_EXT_fragment_shader_interlock", @@ -1036,9 +1154,19 @@ "SPV_NV_cooperative_matrix", "SPV_KHR_cooperative_matrix", "SPV_KHR_ray_tracing_position_fetch", - "SPV_KHR_fragment_shading_rate" + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control", + "SPV_NV_shader_invocation_reorder", + "SPV_NV_cluster_acceleration_structure", + "SPV_NV_linear_swept_spheres", + "SPV_KHR_maximal_reconvergence", + "SPV_NV_push_constant_bank", + "SPV_EXT_opacity_micromap", + "SPV_EXT_shader_invocation_reorder", + "SPV_EXT_shader_atomic_float16_add", + "SPV_KHR_abort", + "SPV_KHR_constant_data", }); - // clang-format on } Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) {
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index fbe08ad..12a5ff7 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -85,8 +85,10 @@ } // Adds entry points, execution modes and workgroup size decorations to the - // worklist for processing with the first function. - void InitializeModuleScopeLiveInstructions(); + // worklist for processing with the first function. Returns + // Pass::Status::Failure if it could not create a required debug instruction. + // Returns Pass::Status::SuccessWithoutChange otherwise. + Pass::Status InitializeModuleScopeLiveInstructions(); // Add |inst| to worklist_ and live_insts_. void AddToWorklist(Instruction* inst) { @@ -136,7 +138,7 @@ // existing control structures will remain. This can leave not-insignificant // sequences of ultimately useless code. // TODO(): Remove useless control constructs. - bool AggressiveDCE(Function* func); + Pass::Status AggressiveDCE(Function* func); Pass::Status ProcessImpl(); @@ -150,9 +152,21 @@ // will be empty at the end. void ProcessWorkList(Function* func); + // Process each DebugDeclare and DebugValue in |func| that has not been + // marked as live in the work list. DebugDeclare's are marked live now, and + // DebugValue Value operands are set to OpUndef. The work list will be empty + // at the end. + // Returns Pass::Status::Failure if it could not create an OpUndef. + // Otherwise, returns Pass::Status::SuccessWithChange if it made changes, + Pass::Status ProcessDebugInformation( + std::list<BasicBlock*>& structured_order); + // Kills any instructions in |func| that have not been marked as live. - bool KillDeadInstructions(const Function* func, - std::list<BasicBlock*>& structured_order); + // Returns Pass::Status::Failure if it could not create an OpUndef. + // Otherwise, returns Pass::Status::SuccessWithChange if it made changes, + // and Pass::Status::SuccessWithoutChange otherwise. + Pass::Status 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);
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp index a314567..7ad2f98 100644 --- a/source/opt/amd_ext_to_khr.cpp +++ b/source/opt/amd_ext_to_khr.cpp
@@ -19,7 +19,6 @@ #include "ir_builder.h" #include "source/opt/ir_context.h" -#include "spv-amd-shader-ballot.insts.inc" #include "type_manager.h" namespace spvtools { @@ -77,6 +76,7 @@ Instruction* temp = ir_builder.AddNaryExtendedInstruction( inst->type_id(), glsl405_ext_inst_id, opcode, {op1, op2}); + if (temp == nullptr) return false; Instruction::OperandList new_operands; new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}}); @@ -115,9 +115,12 @@ Instruction* min = ir_builder.AddNaryExtendedInstruction( inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(min_opcode), {op2, op3}); + if (min == nullptr) return false; + Instruction* max = ir_builder.AddNaryExtendedInstruction( inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(max_opcode), {op2, op3}); + if (max == nullptr) return false; Instruction::OperandList new_operands; new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}}); @@ -227,55 +230,81 @@ // Get the subgroup invocation id. uint32_t var_id = ctx->GetBuiltinInputVarId( uint32_t(spv::BuiltIn::SubgroupLocalInvocationId)); - assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + if (var_id == 0) return false; Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + if (var_inst == nullptr) return false; Instruction* var_ptr_type = ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + if (var_ptr_type == nullptr) return false; uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1); + if (uint_type_id == 0) return false; Instruction* id = ir_builder.AddLoad(uint_type_id, var_id); + if (id == nullptr) return false; uint32_t quad_mask = ir_builder.GetUintConstantId(3); + if (quad_mask == 0) return false; // This gives the offset in the group of 4 of this invocation. Instruction* quad_idx = ir_builder.AddBinaryOp( uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), quad_mask); + if (quad_idx == nullptr) return false; // Get the invocation id of the first invocation in the group of 4. Instruction* quad_ldr = ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseXor, id->result_id(), quad_idx->result_id()); + if (quad_ldr == nullptr) return false; // Get the offset of the target invocation from the offset vector. Instruction* my_offset = ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpVectorExtractDynamic, offset_id, quad_idx->result_id()); + if (my_offset == nullptr) return false; // Determine the index of the invocation to read from. Instruction* target_inv = ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpIAdd, quad_ldr->result_id(), my_offset->result_id()); + if (target_inv == nullptr) return false; // Do the group operations uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF); + if (uint_max_id == 0) return false; uint32_t subgroup_scope = ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup)); + if (subgroup_scope == 0) return false; + const auto* vec_type = type_mgr->GetUIntVectorType(4); + if (vec_type == nullptr) return false; const auto* ballot_value_const = const_mgr->GetConstant( - type_mgr->GetUIntVectorType(4), - {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + vec_type, {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + if (ballot_value_const == nullptr) return false; Instruction* ballot_value = const_mgr->GetDefiningInstruction(ballot_value_const); + if (ballot_value == nullptr) return false; + uint32_t bool_type_id = type_mgr->GetBoolTypeId(); + if (bool_type_id == 0) return false; Instruction* is_active = ir_builder.AddNaryOp( - type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract, + bool_type_id, spv::Op::OpGroupNonUniformBallotBitExtract, {subgroup_scope, ballot_value->result_id(), target_inv->result_id()}); + if (is_active == nullptr) return false; Instruction* shuffle = ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle, {subgroup_scope, data_id, target_inv->result_id()}); + if (shuffle == nullptr) return false; // Create the null constant to use in the select. - const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), - std::vector<uint32_t>()); + const auto* result_type = type_mgr->GetType(inst->type_id()); + if (result_type == nullptr) return false; + const auto* null = + const_mgr->GetConstant(result_type, std::vector<uint32_t>()); + if (null == nullptr) { + return false; + } Instruction* null_inst = const_mgr->GetDefiningInstruction(null); + if (null_inst == nullptr) { + return false; + } // Build the select. inst->SetOpcode(spv::Op::OpSelect); @@ -340,59 +369,85 @@ uint32_t data_id = inst->GetSingleWordInOperand(2); Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3)); + if (mask_inst == nullptr) return false; assert(mask_inst->opcode() == spv::Op::OpConstantComposite && "The mask is suppose to be a vector constant."); assert(mask_inst->NumInOperands() == 3 && "The mask is suppose to have 3 components."); uint32_t uint_x = mask_inst->GetSingleWordInOperand(0); + if (uint_x == 0) return false; uint32_t uint_y = mask_inst->GetSingleWordInOperand(1); + if (uint_y == 0) return false; uint32_t uint_z = mask_inst->GetSingleWordInOperand(2); + if (uint_z == 0) return false; // Get the subgroup invocation id. uint32_t var_id = ctx->GetBuiltinInputVarId( uint32_t(spv::BuiltIn::SubgroupLocalInvocationId)); + if (var_id == 0) return false; ctx->AddExtension("SPV_KHR_shader_ballot"); - assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + if (var_inst == nullptr) return false; Instruction* var_ptr_type = ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + if (var_ptr_type == nullptr) return false; uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1); + if (uint_type_id == 0) return false; Instruction* id = ir_builder.AddLoad(uint_type_id, var_id); + if (id == nullptr) return false; // Do the bitwise operations. uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0); + if (mask_extended == 0) return false; Instruction* and_mask = ir_builder.AddBinaryOp( uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended); + if (and_mask == nullptr) return false; Instruction* and_result = ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), and_mask->result_id()); + if (and_result == nullptr) return false; Instruction* or_result = ir_builder.AddBinaryOp( uint_type_id, spv::Op::OpBitwiseOr, and_result->result_id(), uint_y); + if (or_result == nullptr) return false; Instruction* target_inv = ir_builder.AddBinaryOp( uint_type_id, spv::Op::OpBitwiseXor, or_result->result_id(), uint_z); + if (target_inv == nullptr) return false; // Do the group operations uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF); + if (uint_max_id == 0) return false; uint32_t subgroup_scope = ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup)); + if (subgroup_scope == 0) return false; + const auto* vec_type = type_mgr->GetUIntVectorType(4); + if (vec_type == nullptr) return false; const auto* ballot_value_const = const_mgr->GetConstant( - type_mgr->GetUIntVectorType(4), - {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + vec_type, {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + if (ballot_value_const == nullptr) return false; Instruction* ballot_value = const_mgr->GetDefiningInstruction(ballot_value_const); + if (ballot_value == nullptr) return false; + uint32_t bool_type_id = type_mgr->GetBoolTypeId(); + if (bool_type_id == 0) return false; Instruction* is_active = ir_builder.AddNaryOp( - type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract, + bool_type_id, spv::Op::OpGroupNonUniformBallotBitExtract, {subgroup_scope, ballot_value->result_id(), target_inv->result_id()}); + if (is_active == nullptr) return false; Instruction* shuffle = ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle, {subgroup_scope, data_id, target_inv->result_id()}); + if (shuffle == nullptr) return false; // Create the null constant to use in the select. - const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), - std::vector<uint32_t>()); + const auto* result_type = type_mgr->GetType(inst->type_id()); + if (result_type == nullptr) return false; + const auto* null = + const_mgr->GetConstant(result_type, std::vector<uint32_t>()); + if (null == nullptr) return false; Instruction* null_inst = const_mgr->GetDefiningInstruction(null); + if (null_inst == nullptr) return false; // Build the select. inst->SetOpcode(spv::Op::OpSelect); @@ -426,23 +481,28 @@ const std::vector<const analysis::Constant*>&) { uint32_t var_id = ctx->GetBuiltinInputVarId( uint32_t(spv::BuiltIn::SubgroupLocalInvocationId)); - ctx->AddCapability(spv::Capability::SubgroupBallotKHR); - ctx->AddExtension("SPV_KHR_shader_ballot"); - assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + if (var_id == 0) return false; Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + if (var_inst == nullptr) return false; Instruction* var_ptr_type = ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + if (var_ptr_type == nullptr) return false; + ctx->AddCapability(spv::Capability::SubgroupBallotKHR); + ctx->AddExtension("SPV_KHR_shader_ballot"); InstructionBuilder ir_builder( ctx, inst, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* t = ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id); + if (t == nullptr) return false; analysis::Bool bool_type; uint32_t bool_type_id = ctx->get_type_mgr()->GetTypeInstruction(&bool_type); + if (bool_type_id == 0) return false; Instruction* cmp = ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(), inst->GetSingleWordInOperand(4)); + if (cmp == nullptr) return false; // Build a select. inst->SetOpcode(spv::Op::OpSelect); @@ -485,7 +545,8 @@ uint32_t var_id = context->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::SubgroupLtMask)); - assert(var_id != 0 && "Could not get SubgroupLtMask variable."); + if (var_id == 0) return false; + context->AddCapability(spv::Capability::GroupNonUniformBallot); Instruction* var_inst = def_use_mgr->GetDef(var_id); Instruction* var_ptr_type = def_use_mgr->GetDef(var_inst->type_id()); @@ -498,6 +559,7 @@ analysis::Vector temp_type(GetUIntType(context), 2); const analysis::Type* shuffle_type = context->get_type_mgr()->GetRegisteredType(&temp_type); + if (shuffle_type == nullptr) return false; uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type); uint32_t mask_id = inst->GetSingleWordInOperand(2); @@ -511,13 +573,17 @@ context, inst, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id); + if (load == nullptr) return false; Instruction* shuffle = ir_builder.AddVectorShuffle( shuffle_type_id, load->result_id(), load->result_id(), {0, 1}); + if (shuffle == nullptr) return false; Instruction* bitcast = ir_builder.AddUnaryOp( mask_inst->type_id(), spv::Op::OpBitcast, shuffle->result_id()); + if (bitcast == nullptr) return false; Instruction* t = ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd, bitcast->result_id(), mask_id); + if (t == nullptr) return false; inst->SetOpcode(spv::Op::OpBitCount); inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}}); @@ -572,9 +638,13 @@ analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); uint32_t float_type_id = type_mgr->GetFloatTypeId(); + if (float_type_id == 0) return false; const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2); + if (v2_float_type == nullptr) return false; uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type); + if (v2_float_type_id == 0) return false; uint32_t bool_id = type_mgr->GetBoolTypeId(); + if (bool_id == 0) return false; InstructionBuilder ir_builder( ctx, inst, @@ -588,20 +658,29 @@ glsl405_ext_inst_id = ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); } + if (glsl405_ext_inst_id == 0) return false; // Get the constants that will be used. uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + if (f0_const_id == 0) return false; uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + if (f2_const_id == 0) return false; uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5); + if (f0_5_const_id == 0) return false; const analysis::Constant* vec_const = const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id}); - uint32_t vec_const_id = - const_mgr->GetDefiningInstruction(vec_const)->result_id(); + if (vec_const == nullptr) return false; + Instruction* vec_const_inst = const_mgr->GetDefiningInstruction(vec_const); + if (vec_const_inst == nullptr) return false; + uint32_t vec_const_id = vec_const_inst->result_id(); // Extract the input values. Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); + if (x == nullptr) return false; Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1}); + if (y == nullptr) return false; Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2}); + if (z == nullptr) return false; // Negate the input values. Instruction* nx = @@ -610,72 +689,97 @@ ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, y->result_id()); Instruction* nz = ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, z->result_id()); + if (nx == nullptr) return false; + if (ny == nullptr) return false; + if (nz == nullptr) return false; // Get the abolsute values of the inputs. Instruction* ax = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()}); + if (ax == nullptr) return false; Instruction* ay = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()}); + if (ay == nullptr) return false; Instruction* az = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()}); + if (az == nullptr) return false; // Find which values are negative. Used in later computations. Instruction* is_z_neg = ir_builder.AddBinaryOp( bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id); + if (is_z_neg == nullptr) return false; Instruction* is_y_neg = ir_builder.AddBinaryOp( bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id); + if (is_y_neg == nullptr) return false; Instruction* is_x_neg = ir_builder.AddBinaryOp( bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id); + if (is_x_neg == nullptr) return false; // Compute cubema Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, {ax->result_id(), ay->result_id()}); + if (amax_x_y == nullptr) return false; Instruction* amax = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, {az->result_id(), amax_x_y->result_id()}); + if (amax == nullptr) return false; Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul, f2_const_id, amax->result_id()); + if (cubema == nullptr) return false; // Do the comparisons needed for computing cubesc and cubetc. Instruction* is_z_max = ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, az->result_id(), amax_x_y->result_id()); + if (is_z_max == nullptr) return false; Instruction* not_is_z_max = ir_builder.AddUnaryOp( bool_id, spv::Op::OpLogicalNot, is_z_max->result_id()); + if (not_is_z_max == nullptr) return false; Instruction* y_gr_x = ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, ay->result_id(), ax->result_id()); + if (y_gr_x == nullptr) return false; Instruction* is_y_max = ir_builder.AddBinaryOp(bool_id, spv::Op::OpLogicalAnd, not_is_z_max->result_id(), y_gr_x->result_id()); + if (is_y_max == nullptr) return false; // Select the correct value for cubesc. Instruction* cubesc_case_1 = ir_builder.AddSelect( float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id()); + if (cubesc_case_1 == nullptr) return false; Instruction* cubesc_case_2 = ir_builder.AddSelect( float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id()); + if (cubesc_case_2 == nullptr) return false; Instruction* sel = ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(), cubesc_case_2->result_id()); + if (sel == nullptr) return false; Instruction* cubesc = ir_builder.AddSelect(float_type_id, is_z_max->result_id(), cubesc_case_1->result_id(), sel->result_id()); + if (cubesc == nullptr) return false; // Select the correct value for cubetc. Instruction* cubetc_case_1 = ir_builder.AddSelect( float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id()); + if (cubetc_case_1 == nullptr) return false; Instruction* cubetc = ir_builder.AddSelect(float_type_id, is_y_max->result_id(), cubetc_case_1->result_id(), ny->result_id()); + if (cubetc == nullptr) return false; // Do the division Instruction* cube = ir_builder.AddCompositeConstruct( v2_float_type_id, {cubesc->result_id(), cubetc->result_id()}); + if (cube == nullptr) return false; Instruction* denom = ir_builder.AddCompositeConstruct( v2_float_type_id, {cubema->result_id(), cubema->result_id()}); + if (denom == nullptr) return false; Instruction* div = ir_builder.AddBinaryOp( v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id()); + if (div == nullptr) return false; // Get the final result by adding 0.5 to |div|. inst->SetOpcode(spv::Op::OpFAdd); @@ -723,7 +827,9 @@ analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); uint32_t float_type_id = type_mgr->GetFloatTypeId(); + if (float_type_id == 0) return false; uint32_t bool_id = type_mgr->GetBoolTypeId(); + if (bool_id == 0) return false; InstructionBuilder ir_builder( ctx, inst, @@ -740,56 +846,79 @@ // Get the constants that will be used. uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + if (f0_const_id == 0) return false; uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0); + if (f1_const_id == 0) return false; uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + if (f2_const_id == 0) return false; uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0); + if (f3_const_id == 0) return false; uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0); + if (f4_const_id == 0) return false; uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0); + if (f5_const_id == 0) return false; // Extract the input values. Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); + if (x == nullptr) return false; Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1}); + if (y == nullptr) return false; + // TODO(1-841): Handle id overflow. Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2}); + if (z == nullptr) return false; // Get the absolute values of the inputs. Instruction* ax = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()}); + if (ax == nullptr) return false; Instruction* ay = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()}); + if (ay == nullptr) return false; Instruction* az = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()}); + if (az == nullptr) return false; // Find which values are negative. Used in later computations. Instruction* is_z_neg = ir_builder.AddBinaryOp( bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id); + if (is_z_neg == nullptr) return false; Instruction* is_y_neg = ir_builder.AddBinaryOp( bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id); + if (is_y_neg == nullptr) return false; Instruction* is_x_neg = ir_builder.AddBinaryOp( bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id); + if (is_x_neg == nullptr) return false; // Find the max value. Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction( float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, {ax->result_id(), ay->result_id()}); + if (amax_x_y == nullptr) return false; Instruction* is_z_max = ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, az->result_id(), amax_x_y->result_id()); + if (is_z_max == nullptr) return false; Instruction* y_gr_x = ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, ay->result_id(), ax->result_id()); + if (y_gr_x == nullptr) return false; // Get the value for each case. Instruction* case_z = ir_builder.AddSelect( float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id); + if (case_z == nullptr) return false; Instruction* case_y = ir_builder.AddSelect( float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id); + if (case_y == nullptr) return false; Instruction* case_x = ir_builder.AddSelect( float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id); + if (case_x == nullptr) return false; // Select the correct case. Instruction* sel = ir_builder.AddSelect(float_type_id, y_gr_x->result_id(), case_y->result_id(), case_x->result_id()); + if (sel == nullptr) return false; // Get the final result by adding 0.5 to |div|. inst->SetOpcode(spv::Op::OpSelect); @@ -929,11 +1058,18 @@ std::unique_ptr<AmdExtFoldingRules>(new AmdExtFoldingRules(context())), MakeUnique<AmdExtConstFoldingRules>(context())); for (Function& func : *get_module()) { - func.ForEachInst([&changed, &folder](Instruction* inst) { - if (folder.FoldInstruction(inst)) { - changed = true; - } - }); + bool failed = + !func.WhileEachInst([&changed, &folder, this](Instruction* inst) { + if (folder.FoldInstruction(inst)) { + changed = true; + return true; + } else if (context()->id_overflow()) { + return false; + } + return true; + }); + + if (failed) return Status::Failure; } // Now that instruction that require the extensions have been removed, we can
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp index a9fc8e2..84cf488 100644 --- a/source/opt/basic_block.cpp +++ b/source/opt/basic_block.cpp
@@ -29,11 +29,19 @@ } // namespace BasicBlock* BasicBlock::Clone(IRContext* context) const { - BasicBlock* clone = new BasicBlock( - std::unique_ptr<Instruction>(GetLabelInst()->Clone(context))); + Instruction* label_clone = GetLabelInst()->Clone(context); + if (!label_clone) { + return nullptr; + } + BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone)); for (const auto& inst : insts_) { // Use the incoming context - clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context))); + Instruction* inst_clone = inst.Clone(context); + if (!inst_clone) { + delete clone; + return nullptr; + } + clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone)); } if (context->AreAnalysesValid(
diff --git a/source/opt/canonicalize_ids_pass.cpp b/source/opt/canonicalize_ids_pass.cpp new file mode 100644 index 0000000..8b07c38 --- /dev/null +++ b/source/opt/canonicalize_ids_pass.cpp
@@ -0,0 +1,532 @@ +// Copyright (c) 2025 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/canonicalize_ids_pass.h" + +#include <algorithm> +#include <limits> + +namespace spvtools { +namespace opt { + +Pass::Status CanonicalizeIdsPass::Process() { + // Initialize the new ID map. + new_id_.resize(GetBound(), unused_); + + // Scan the IDs and set to unmapped. + ScanIds(); + + // Create new IDs for types and consts. + CanonicalizeTypeAndConst(); + + // Create new IDs for names. + CanonicalizeNames(); + + // Create new IDs for functions. + CanonicalizeFunctions(); + + // Create new IDs for everything else. + CanonicalizeRemainders(); + + // Apply the new IDs to the module. + auto const modified = ApplyMap(); + + // Update bound in the header. + if (modified) { + UpdateBound(); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +void CanonicalizeIdsPass::ScanIds() { + get_module()->ForEachInst( + [this](Instruction* inst) { + // Look for types and constants. + if (spvOpcodeGeneratesType(inst->opcode()) || + spvOpcodeIsConstant(inst->opcode())) { + type_and_const_ids_.push_back(inst->result_id()); + SetNewId(inst->result_id(), unmapped_); + } + // Look for names. + else if (inst->opcode() == spv::Op::OpName) { + // store name string in map so that we can compute the hash later + auto const name = inst->GetOperand(1).AsString(); + auto const target = inst->GetSingleWordInOperand(0); + name_ids_[name] = target; + SetNewId(target, unmapped_); + } + // Look for function IDs. + else if (inst->opcode() == spv::Op::OpFunction) { + auto const res_id = inst->result_id(); + function_ids_.push_back(res_id); + SetNewId(res_id, unmapped_); + } + // Look for remaining result IDs. + else if (inst->HasResultId()) { + auto const res_id = inst->result_id(); + SetNewId(res_id, unmapped_); + } + }, + true); +} + +void CanonicalizeIdsPass::CanonicalizeTypeAndConst() { + // Remap type IDs. + static constexpr std::uint32_t soft_type_id_limit = 3011; // small prime. + static constexpr std::uint32_t first_mapped_id = 8; // offset into ID space + for (auto const id : type_and_const_ids_) { + if (!IsOldIdUnmapped(id)) { + continue; + } + + // Compute the hash value. + auto const hash_value = HashTypeAndConst(id); + if (hash_value != unmapped_) { + SetNewId(id, hash_value % soft_type_id_limit + first_mapped_id); + } + } +} + +// Hash types to canonical values. This can return ID collisions (it's a bit +// inevitable): it's up to the caller to handle that gracefully. +spv::Id CanonicalizeIdsPass::HashTypeAndConst(spv::Id const id) const { + spv::Id value = 0; + + auto const inst = get_def_use_mgr()->GetDef(id); + auto const op_code = inst->opcode(); + switch (op_code) { + case spv::Op::OpTypeVoid: + value = 0; + break; + case spv::Op::OpTypeBool: + value = 1; + break; + case spv::Op::OpTypeInt: { + auto const signedness = inst->GetSingleWordOperand(2); + value = 3 + signedness; + break; + } + case spv::Op::OpTypeFloat: + value = 5; + break; + case spv::Op::OpTypeVector: { + auto const component_type = inst->GetSingleWordOperand(1); + auto const component_count = inst->GetSingleWordOperand(2); + value = 6 + HashTypeAndConst(component_type) * (component_count - 1); + break; + } + case spv::Op::OpTypeMatrix: { + auto const column_type = inst->GetSingleWordOperand(1); + auto const column_count = inst->GetSingleWordOperand(2); + value = 30 + HashTypeAndConst(column_type) * (column_count - 1); + break; + } + case spv::Op::OpTypeImage: { + // TODO: Why isn't the format used to compute the hash value? + auto const sampled_type = inst->GetSingleWordOperand(1); + auto const dim = inst->GetSingleWordOperand(2); + auto const depth = inst->GetSingleWordOperand(3); + auto const arrayed = inst->GetSingleWordOperand(4); + auto const ms = inst->GetSingleWordOperand(5); + auto const sampled = inst->GetSingleWordOperand(6); + value = 120 + HashTypeAndConst(sampled_type) + dim + depth * 8 * 16 + + arrayed * 4 * 16 + ms * 2 * 16 + sampled * 1 * 16; + break; + } + case spv::Op::OpTypeSampler: + value = 500; + break; + case spv::Op::OpTypeSampledImage: + value = 502; + break; + case spv::Op::OpTypeArray: { + auto const element_type = inst->GetSingleWordOperand(1); + auto const length = inst->GetSingleWordOperand(2); + value = 501 + HashTypeAndConst(element_type) * length; + break; + } + case spv::Op::OpTypeRuntimeArray: { + auto const element_type = inst->GetSingleWordOperand(1); + value = 5000 + HashTypeAndConst(element_type); + break; + } + case spv::Op::OpTypeStruct: + value = 10000; + for (uint32_t w = 1; w < inst->NumOperandWords(); ++w) { + value += (w + 1) * HashTypeAndConst(inst->GetSingleWordOperand(w)); + } + break; + case spv::Op::OpTypeOpaque: { + // TODO: Name is a literal that may have more than one word. + auto const name = inst->GetSingleWordOperand(1); + value = 6000 + name; + break; + } + case spv::Op::OpTypePointer: { + auto const type = inst->GetSingleWordOperand(2); + value = 100000 + HashTypeAndConst(type); + break; + } + case spv::Op::OpTypeFunction: + value = 200000; + for (uint32_t w = 1; w < inst->NumOperandWords(); ++w) { + value += (w + 1) * HashTypeAndConst(inst->GetSingleWordOperand(w)); + } + break; + case spv::Op::OpTypeEvent: + value = 300000; + break; + case spv::Op::OpTypeDeviceEvent: + value = 300001; + break; + case spv::Op::OpTypeReserveId: + value = 300002; + break; + case spv::Op::OpTypeQueue: + value = 300003; + break; + case spv::Op::OpTypePipe: + value = 300004; + break; + case spv::Op::OpTypePipeStorage: + value = 300005; + break; + case spv::Op::OpTypeNamedBarrier: + value = 300006; + break; + case spv::Op::OpConstantTrue: + value = 300007; + break; + case spv::Op::OpConstantFalse: + value = 300008; + break; + case spv::Op::OpTypeRayQueryKHR: + value = 300009; + break; + case spv::Op::OpTypeAccelerationStructureKHR: + value = 300010; + break; + // Don't map the following types. + // TODO: These types were not remapped in the glslang version of the + // remapper. Support should be added as necessary. + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: + case spv::Op::OpTypeHitObjectNV: + case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypeNodePayloadArrayAMDX: + case spv::Op::OpTypeTensorLayoutNV: + case spv::Op::OpTypeTensorViewNV: + case spv::Op::OpTypeTensorARM: + case spv::Op::OpTypeTaskSequenceINTEL: + value = unmapped_; + break; + case spv::Op::OpConstant: { + auto const result_type = inst->GetSingleWordOperand(0); + value = 400011 + HashTypeAndConst(result_type); + auto const literal = inst->GetOperand(2); + for (uint32_t w = 0; w < literal.words.size(); ++w) { + value += (w + 3) * literal.words[w]; + } + break; + } + case spv::Op::OpConstantComposite: { + auto const result_type = inst->GetSingleWordOperand(0); + value = 300011 + HashTypeAndConst(result_type); + for (uint32_t w = 2; w < inst->NumOperandWords(); ++w) { + value += (w + 1) * HashTypeAndConst(inst->GetSingleWordOperand(w)); + } + break; + } + case spv::Op::OpConstantNull: { + auto const result_type = inst->GetSingleWordOperand(0); + value = 500009 + HashTypeAndConst(result_type); + break; + } + case spv::Op::OpConstantSampler: { + auto const result_type = inst->GetSingleWordOperand(0); + value = 600011 + HashTypeAndConst(result_type); + for (uint32_t w = 2; w < inst->NumOperandWords(); ++w) { + value += (w + 1) * inst->GetSingleWordOperand(w); + } + break; + } + // Don't map the following constants. + // TODO: These constants were not remapped in the glslang version of the + // remapper. Support should be added as necessary. + case spv::Op::OpConstantCompositeReplicateEXT: + case spv::Op::OpConstantFunctionPointerINTEL: + case spv::Op::OpConstantStringAMDX: + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpSpecConstant: + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpSpecConstantCompositeReplicateEXT: + case spv::Op::OpSpecConstantOp: + case spv::Op::OpSpecConstantStringAMDX: + case spv::Op::OpSpecConstantDataKHR: + value = unmapped_; + break; + // TODO: Add additional types/constants as needed. See + // spvOpcodeGeneratesType and spvOpcodeIsConstant. + default: + context()->consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, + "unhandled opcode will not be canonicalized"); + break; + } + + return value; +} + +void CanonicalizeIdsPass::CanonicalizeNames() { + static constexpr std::uint32_t soft_type_id_limit = 3011; // Small prime. + static constexpr std::uint32_t first_mapped_id = + 3019; // Offset into ID space. + + for (auto const& [name, target] : name_ids_) { + if (!IsOldIdUnmapped(target)) { + continue; + } + + spv::Id hash_value = 1911; + for (const char c : name) { + hash_value = hash_value * 1009 + c; + } + + if (IsOldIdUnmapped(target)) { + SetNewId(target, hash_value % soft_type_id_limit + first_mapped_id); + } + } +} + +void CanonicalizeIdsPass::CanonicalizeFunctions() { + static constexpr std::uint32_t soft_type_id_limit = 19071; // Small prime. + static constexpr std::uint32_t first_mapped_id = + 6203; // Offset into ID space. + // Window size for context-sensitive canonicalization values + // Empirical best size from a single data set. TODO: Would be a good tunable. + // We essentially perform a little convolution around each instruction, + // to capture the flavor of nearby code, to hopefully match to similar + // code in other modules. + static const int32_t window_size = 2; + + for (auto const func_id : function_ids_) { + // Store the instructions and opcode hash values in vectors so that the + // window of instructions can be easily accessed and avoid having to + // recompute the hash value repeatedly in overlapping windows. + std::vector<Instruction*> insts; + std::vector<uint32_t> opcode_hashvals; + auto const func = context()->GetFunction(func_id); + func->WhileEachInst([&](Instruction* inst) { + insts.emplace_back(inst); + opcode_hashvals.emplace_back(HashOpCode(inst)); + return true; + }); + + // For every instruction in the function, compute the hash value using the + // instruction and a small window of surrounding instructions. + assert(insts.size() < (size_t)std::numeric_limits<int32_t>::max()); + for (int32_t i = 0; i < (int32_t)insts.size(); ++i) { + auto const inst = insts[i]; + if (!inst->HasResultId()) { + continue; + } + + auto const old_id = inst->result_id(); + if (!IsOldIdUnmapped(old_id)) { + continue; + } + + int32_t const lower_bound = std::max(0, i - window_size); + int32_t const upper_bound = + std::min((int32_t)insts.size() - 1, i + window_size); + spv::Id hash_value = func_id * 17; // Small prime. + // Include the hash value of the preceding instructions in the hash but + // don't include instructions before the OpFunction. + for (int32_t j = i - 1; j >= lower_bound; --j) { + auto const local_inst = insts[j]; + if (local_inst->opcode() == spv::Op::OpFunction) { + break; + } + + hash_value = hash_value * 30103 + + opcode_hashvals[j]; // 30103 is a semi-arbitrary prime. + } + + // Include the hash value of the subsequent instructions in the hash but + // don't include instructions past OpFunctionEnd. + for (int32_t j = i; j <= upper_bound; ++j) { + auto const local_inst = insts[j]; + if (local_inst->opcode() == spv::Op::OpFunctionEnd) { + break; + } + + hash_value = hash_value * 30103 + + opcode_hashvals[j]; // 30103 is a semiarbitrary prime. + } + + SetNewId(old_id, hash_value % soft_type_id_limit + first_mapped_id); + } + } +} + +spv::Id CanonicalizeIdsPass::HashOpCode(Instruction const* const inst) const { + auto const op_code = inst->opcode(); + std::uint32_t offset = 0; + if (op_code == spv::Op::OpExtInst) { + // offset is literal instruction + offset = inst->GetSingleWordOperand(3); + } + + return (std::uint32_t)op_code * 19 + offset; // 19 is a small prime. +} + +// Assign remaining IDs sequentially from remaining holes in the new ID space. +void CanonicalizeIdsPass::CanonicalizeRemainders() { + spv::Id next_id = 1; + for (uint32_t old_id = 0; old_id < new_id_.size(); ++old_id) { + if (IsOldIdUnmapped(old_id)) { + next_id = SetNewId(old_id, next_id); + } + } +} + +bool CanonicalizeIdsPass::ApplyMap() { + bool modified = false; + context()->module()->ForEachInst( + [this, &modified](Instruction* inst) { + for (auto operand = inst->begin(); operand != inst->end(); ++operand) { + const auto type = operand->type; + if (spvIsIdType(type)) { + uint32_t& id = operand->words[0]; + uint32_t const new_id = GetNewId(id); + if (new_id == unused_) { + continue; + } + + assert(new_id != unmapped_ && "new_id should not be unmapped_"); + + if (id != new_id) { + modified = true; + id = new_id; + if (type == SPV_OPERAND_TYPE_RESULT_ID) { + inst->SetResultId(new_id); + } else if (type == SPV_OPERAND_TYPE_TYPE_ID) { + inst->SetResultType(new_id); + } + } + } + } + const auto& debug_scope = inst->GetDebugScope(); + if (debug_scope.GetLexicalScope() != kNoDebugScope) { + uint32_t old_scope = debug_scope.GetLexicalScope(); + uint32_t new_scope = GetNewId(old_scope); + uint32_t old_inlined_at = debug_scope.GetInlinedAt(); + uint32_t new_inlined_at = old_inlined_at != kNoInlinedAt + ? GetNewId(old_inlined_at) + : old_inlined_at; + if ((new_scope != unused_ && new_scope != old_scope) || + (new_inlined_at != unused_ && new_inlined_at != old_inlined_at)) { + DebugScope new_debug_scope(new_scope, new_inlined_at); + inst->SetDebugScope(new_debug_scope); + modified = true; + } + } + }, + true); + + return modified; +} + +spv::Id CanonicalizeIdsPass::GetBound() const { + return context()->module()->id_bound(); +} + +void CanonicalizeIdsPass::UpdateBound() { + context()->module()->SetIdBound(context()->module()->ComputeIdBound()); + + context()->ResetFeatureManager(); +} + +// Set a new ID. If the new ID is alreadly claimed, the next consecutive ID +// will be claimed, mapped, and returned to the caller. +spv::Id CanonicalizeIdsPass::SetNewId(spv::Id const old_id, spv::Id new_id) { + assert(old_id < GetBound() && "don't remap an ID that is out of bounds"); + + if (old_id >= new_id_.size()) { + new_id_.resize(old_id + 1, unused_); + } + + if (new_id != unmapped_ && new_id != unused_) { + assert(!IsOldIdUnused(old_id) && "don't remap unused IDs"); + assert(IsOldIdUnmapped(old_id) && "don't remap already mapped IDs"); + + new_id = ClaimNewId(new_id); + } + + new_id_[old_id] = new_id; + + return new_id; +} + +// Helper function for SetNewID. Claim a new ID. If the new ID is already +// claimed, the next consecutive ID will be claimed and returned to the caller. +spv::Id CanonicalizeIdsPass::ClaimNewId(spv::Id new_id) { + // Return the ID if it's not taken. + auto iter = claimed_new_ids_.find(new_id); + if (iter != claimed_new_ids_.end()) { + // Otherwise, search for the next unused ID using our current iterator. + // Technically, it's a linear search across the set starting at the + // iterator, but it's not as bad as it would appear in practice assuming the + // hash values are well distributed. + iter = std::adjacent_find(iter, claimed_new_ids_.end(), [](int a, int b) { + return a + 1 != b; // Stop at the first non-consecutive pair. + }); + if (iter != claimed_new_ids_.end()) { + new_id = + *iter + 1; // We need the next ID after where the search stopped. + } else { + new_id = *(--iter) + 1; // We reached the end so we use the next ID. + } + } + + assert(!IsNewIdClaimed(new_id) && + "don't remap to an ID that is already claimed"); + iter = claimed_new_ids_.insert(iter, new_id); + assert(*iter == new_id); + + return new_id; +} + +std::string CanonicalizeIdsPass::IdAsString(spv::Id const id) const { + if (id == unused_) { + return "unused"; + } else if (id == unmapped_) { + return "unmapped"; + } else { + return std::to_string(id); + } +} + +void CanonicalizeIdsPass::PrintNewIds() const { + for (spv::Id id = 0; id < new_id_.size(); ++id) { + auto const message = + "new id[" + IdAsString(id) + "]: " + IdAsString(new_id_[id]); + context()->consumer()(SPV_MSG_INFO, "", {0, 0, 0}, message.c_str()); + } +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/canonicalize_ids_pass.h b/source/opt/canonicalize_ids_pass.h new file mode 100644 index 0000000..6c38d34 --- /dev/null +++ b/source/opt/canonicalize_ids_pass.h
@@ -0,0 +1,115 @@ +// Copyright (c) 2025 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <algorithm> +#include <map> +#include <set> +#include <vector> + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// The canonicalize IDs pass is an optimization to improve compression of SPIR-V +// binary files via entropy reduction. It transforms SPIR-V to SPIR-V, remapping +// IDs. The resulting modules have an increased ID range (IDs are not as tightly +// packed around zero), but will compress better when multiple modules are +// compressed together, since the compressor's dictionary can find better cross +// module commonality. Remapping is accomplished via canonicalization. Thus, +// modules can be compressed one at a time with no loss of quality relative to +// operating on many modules at once. + +// This pass should be run after most optimization passes except for +// --strip-debug because this pass will use OpName to canonicalize IDs. i.e. Run +// --strip-debug after this pass. + +// This is a port of remap utility in glslang. There are great deal of magic +// numbers that are present throughout this code. The general goal is to replace +// the IDs with a hash value such that the distribution of IDs is deterministic +// and minimizes collisions. The magic numbers in the glslang version were +// chosen semi-arbitrarily and have been preserved in this port in order to +// maintain backward compatibility. + +class CanonicalizeIdsPass : public Pass { + public: + CanonicalizeIdsPass() = default; + virtual ~CanonicalizeIdsPass() = default; + + Pass::Status Process() override; + + const char* name() const override { return "canonicalize-ids"; } + + private: + // Special values for IDs. + static constexpr spv::Id unmapped_{spv::Id(-10000)}; + static constexpr spv::Id unused_{spv::Id(-10001)}; + + // Scans the module for IDs and sets them to unmapped_. + void ScanIds(); + + // Functions to compute new IDs. + void CanonicalizeTypeAndConst(); + spv::Id HashTypeAndConst( + spv::Id const id) const; // Helper for CanonicalizeTypeAndConst. + void CanonicalizeNames(); + void CanonicalizeFunctions(); + spv::Id HashOpCode(Instruction const* const inst) + const; // Helper for CanonicalizeFunctions. + void CanonicalizeRemainders(); + + // Applies the new IDs. + bool ApplyMap(); + + // Methods to manage the bound field in header. + spv::Id GetBound() const; // All IDs must satisfy 0 < ID < bound. + void UpdateBound(); + + // Methods to map from old IDs to new IDs. + spv::Id GetNewId(spv::Id const old_id) const { return new_id_[old_id]; } + spv::Id SetNewId(spv::Id const old_id, spv::Id new_id); + + // Methods to manage claimed IDs. + spv::Id ClaimNewId(spv::Id new_id); + bool IsNewIdClaimed(spv::Id const new_id) const { + return claimed_new_ids_.find(new_id) != claimed_new_ids_.end(); + } + + // Queries for old IDs. + bool IsOldIdUnmapped(spv::Id const old_id) const { + return GetNewId(old_id) == unmapped_; + } + bool IsOldIdUnused(spv::Id const old_id) const { + return GetNewId(old_id) == unused_; + } + + // Container to map old IDs to new IDs. e.g. new_id_[old_id] = new_id + std::vector<spv::Id> new_id_; + + // IDs from the new ID space that have been claimed (faster than searching + // through new_id_). + std::set<spv::Id> claimed_new_ids_; + + // Helper functions for printing IDs (useful for debugging). + std::string IdAsString(spv::Id const id) const; + void PrintNewIds() const; + + // Containers to track IDs we want to canonicalize. + std::vector<spv::Id> type_and_const_ids_; + std::map<std::string, spv::Id> name_ids_; + std::vector<spv::Id> function_ids_; +}; + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index 46bfc90..c53ee19 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp
@@ -165,6 +165,9 @@ context()->get_instruction_folder().FoldInstructionToConstant(instr, map_func); + if (folded_inst && context()->id_overflow()) { + return SSAPropagator::kFailed; + } if (folded_inst != nullptr) { // We do not want to change the body of the function by adding new // instructions. When folding we can only generate new constants. @@ -360,6 +363,13 @@ } } + // Mark the extended instruction imports as `kVarying`. We know they + // will not be constants, and will be used by `OpExtInst` instructions. + // This allows those instructions to be fully processed. + for (const auto& inst : get_module()->ext_inst_imports()) { + values_[inst.result_id()] = kVaryingSSAId; + } + original_id_bound_ = context()->module()->IdBound(); } @@ -369,6 +379,7 @@ // Process all entry point functions. ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); }; bool modified = context()->ProcessReachableCallTree(pfn); + if (context()->id_overflow()) return Pass::Status::Failure; return modified ? Pass::Status::SuccessWithChange : Pass::Status::SuccessWithoutChange; }
diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp index 4c4bb25..5f4a7d1 100644 --- a/source/opt/cfg.cpp +++ b/source/opt/cfg.cpp
@@ -254,12 +254,13 @@ } // Adjust the OpPhi instructions as needed. - bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) { + bool ok = bb->WhileEachPhiInst([latch_block, bb, new_header, + context](Instruction* phi) -> bool { std::vector<uint32_t> preheader_phi_ops; std::vector<Operand> header_phi_ops; - // Identify where the original inputs to original OpPhi belong: header or - // preheader. + // Identify where the original inputs to original OpPhi belong: header + // or preheader. for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { uint32_t def_id = phi->GetSingleWordInOperand(i); uint32_t branch_id = phi->GetSingleWordInOperand(i + 1); @@ -272,21 +273,24 @@ } } - // Create a phi instruction if and only if the preheader_phi_ops has more - // than one pair. + // Create a phi instruction if and only if the preheader_phi_ops has + // more than one pair. if (preheader_phi_ops.size() > 2) { InstructionBuilder builder( context, &*bb->begin(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops); + if (!new_phi) { + return false; + } // Add the OpPhi to the header bb. header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}}); header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}}); } else { - // An OpPhi with a single entry is just a copy. In this case use the same - // instruction in the new header. + // An OpPhi with a single entry is just a copy. In this case use the + // same instruction in the new header. header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}}); header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}}); } @@ -297,8 +301,13 @@ new_header->begin()->InsertBefore(std::move(phi_owner)); context->set_instr_block(phi, new_header); context->AnalyzeUses(phi); + return true; }); + if (!ok) { + return nullptr; + } + // Add a branch to the new header. InstructionBuilder branch_builder( context, bb,
diff --git a/source/opt/cfg_cleanup_pass.cpp b/source/opt/cfg_cleanup_pass.cpp index 26fed89..6cd0479 100644 --- a/source/opt/cfg_cleanup_pass.cpp +++ b/source/opt/cfg_cleanup_pass.cpp
@@ -25,8 +25,17 @@ Pass::Status CFGCleanupPass::Process() { // Process all entry point functions. - ProcessFunction pfn = [this](Function* fp) { return CFGCleanup(fp); }; + bool failure = false; + ProcessFunction pfn = [this, &failure](Function* fp) { + auto status = CFGCleanup(fp); + if (status == Status::Failure) { + failure = true; + return false; + } + return status == Status::SuccessWithChange; + }; bool modified = context()->ProcessReachableCallTree(pfn); + if (failure) return Pass::Status::Failure; return modified ? Pass::Status::SuccessWithChange : Pass::Status::SuccessWithoutChange; }
diff --git a/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp index 99ec796..ec90d97 100644 --- a/source/opt/combine_access_chains.cpp +++ b/source/opt/combine_access_chains.cpp
@@ -27,36 +27,48 @@ bool modified = false; for (auto& function : *get_module()) { - modified |= ProcessFunction(function); + auto status = ProcessFunction(function); + if (status == Status::Failure) return Status::Failure; + if (status == Status::SuccessWithChange) modified = true; } return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); } -bool CombineAccessChains::ProcessFunction(Function& function) { +Pass::Status CombineAccessChains::ProcessFunction(Function& function) { if (function.IsDeclaration()) { - return false; + return Status::SuccessWithoutChange; } bool modified = false; + bool failure = false; cfg()->ForEachBlockInReversePostOrder( - function.entry().get(), [&modified, this](BasicBlock* block) { - block->ForEachInst([&modified, this](Instruction* inst) { + function.entry().get(), [&modified, &failure, this](BasicBlock* block) { + if (failure) return; + block->ForEachInst([&modified, &failure, this](Instruction* inst) { + if (failure) return; switch (inst->opcode()) { case spv::Op::OpAccessChain: case spv::Op::OpInBoundsAccessChain: case spv::Op::OpPtrAccessChain: - case spv::Op::OpInBoundsPtrAccessChain: - modified |= CombineAccessChain(inst); + case spv::Op::OpInBoundsPtrAccessChain: { + auto status = CombineAccessChain(inst); + if (status == Status::Failure) { + failure = true; + } else if (status == Status::SuccessWithChange) { + modified = true; + } break; + } default: break; } }); }); - return modified; + if (failure) return Status::Failure; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } uint32_t CombineAccessChains::GetConstantValue( @@ -121,9 +133,9 @@ return type; } -bool CombineAccessChains::CombineIndices(Instruction* ptr_input, - Instruction* inst, - std::vector<Operand>* new_operands) { +Pass::Status CombineAccessChains::CombineIndices( + Instruction* ptr_input, Instruction* inst, + std::vector<Operand>* new_operands) { analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); analysis::ConstantManager* constant_mgr = context()->get_constant_mgr(); @@ -150,8 +162,10 @@ GetConstantValue(element_constant); const analysis::Constant* new_value_constant = constant_mgr->GetConstant(last_index_constant->type(), {new_value}); + if (!new_value_constant) return Status::Failure; Instruction* new_value_inst = constant_mgr->GetDefiningInstruction(new_value_constant); + if (!new_value_inst) return Status::Failure; new_value_id = new_value_inst->result_id(); } else if (!type->AsStruct() || combining_element_operands) { // Generate an addition of the two indices. @@ -161,16 +175,17 @@ Instruction* addition = builder.AddIAdd(last_index_inst->type_id(), last_index_inst->result_id(), element_inst->result_id()); + if (!addition) return Status::Failure; new_value_id = addition->result_id(); } else { // Indexing into structs must be constant, so bail out here. - return false; + return Status::SuccessWithoutChange; } new_operands->push_back({SPV_OPERAND_TYPE_ID, {new_value_id}}); - return true; + return Status::SuccessWithChange; } -bool CombineAccessChains::CreateNewInputOperands( +Pass::Status CombineAccessChains::CreateNewInputOperands( Instruction* ptr_input, Instruction* inst, std::vector<Operand>* new_operands) { // Start by copying all the input operands of the feeder access chain. @@ -182,7 +197,8 @@ if (IsPtrAccessChain(inst->opcode())) { // The last index of the feeder should be combined with the element operand // of |inst|. - if (!CombineIndices(ptr_input, inst, new_operands)) return false; + auto status = CombineIndices(ptr_input, inst, new_operands); + if (status != Status::SuccessWithChange) return status; } else { // The indices aren't being combined so now add the last index operand of // |ptr_input|. @@ -196,10 +212,10 @@ new_operands->push_back(inst->GetInOperand(i)); } - return true; + return Status::SuccessWithChange; } -bool CombineAccessChains::CombineAccessChain(Instruction* inst) { +Pass::Status CombineAccessChains::CombineAccessChain(Instruction* inst) { assert((inst->opcode() == spv::Op::OpPtrAccessChain || inst->opcode() == spv::Op::OpAccessChain || inst->opcode() == spv::Op::OpInBoundsAccessChain || @@ -212,10 +228,11 @@ ptr_input->opcode() != spv::Op::OpInBoundsAccessChain && ptr_input->opcode() != spv::Op::OpPtrAccessChain && ptr_input->opcode() != spv::Op::OpInBoundsPtrAccessChain) { - return false; + return Status::SuccessWithoutChange; } - if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) return false; + if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) + return Status::SuccessWithoutChange; // Handles the following cases: // 1. |ptr_input| is an index-less access chain. Replace the pointer @@ -237,7 +254,7 @@ // size/alignment of the type and converting the stride into an element // index. uint32_t array_stride = GetArrayStride(ptr_input); - if (array_stride != 0) return false; + if (array_stride != 0) return Status::SuccessWithoutChange; if (ptr_input->NumInOperands() == 1) { // The input is effectively a no-op. @@ -249,14 +266,15 @@ inst->SetOpcode(spv::Op::OpCopyObject); } else { std::vector<Operand> new_operands; - if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false; + auto status = CreateNewInputOperands(ptr_input, inst, &new_operands); + if (status != Status::SuccessWithChange) return status; // Update the instruction. inst->SetOpcode(UpdateOpcode(inst->opcode(), ptr_input->opcode())); inst->SetInOperands(std::move(new_operands)); context()->AnalyzeUses(inst); } - return true; + return Status::SuccessWithChange; } spv::Op CombineAccessChains::UpdateOpcode(spv::Op base_opcode,
diff --git a/source/opt/combine_access_chains.h b/source/opt/combine_access_chains.h index 32ee50d..1872720 100644 --- a/source/opt/combine_access_chains.h +++ b/source/opt/combine_access_chains.h
@@ -40,12 +40,12 @@ private: // Combine access chains in |function|. Blocks are processed in reverse // post-order. Returns true if the function is modified. - bool ProcessFunction(Function& function); + Status ProcessFunction(Function& function); // Combines an access chain (normal, in bounds or pointer) |inst| if its base // pointer is another access chain. Returns true if the access chain was // modified. - bool CombineAccessChain(Instruction* inst); + Status CombineAccessChain(Instruction* inst); // Returns the value of |constant_inst| as a uint32_t. uint32_t GetConstantValue(const analysis::Constant* constant_inst); @@ -59,13 +59,13 @@ // Populates |new_operands| with the operands for the combined access chain. // Returns false if the access chains cannot be combined. - bool CreateNewInputOperands(Instruction* ptr_input, Instruction* inst, - std::vector<Operand>* new_operands); + Status CreateNewInputOperands(Instruction* ptr_input, Instruction* inst, + std::vector<Operand>* new_operands); // Combines the last index of |ptr_input| with the element operand of |inst|. // Adds the combined operand to |new_operands|. - bool CombineIndices(Instruction* ptr_input, Instruction* inst, - std::vector<Operand>* new_operands); + Status CombineIndices(Instruction* ptr_input, Instruction* inst, + std::vector<Operand>* new_operands); // Returns the opcode to use for the combined access chain. spv::Op UpdateOpcode(spv::Op base_opcode, spv::Op input_opcode);
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index a5d4cbe..b6dd681 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp
@@ -14,6 +14,8 @@ #include "source/opt/const_folding_rules.h" +#include <optional> + #include "source/opt/ir_context.h" namespace spvtools { @@ -829,7 +831,9 @@ // Build the constant object and return it. std::vector<uint32_t> ids; for (const analysis::Constant* member : results_components) { - ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id()); + Instruction* def = const_mgr->GetDefiningInstruction(member); + if (!def) return nullptr; + ids.push_back(def->result_id()); } return const_mgr->GetConstant(vector_type, ids); } else { @@ -986,6 +990,32 @@ ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } +// x - x = 0 +ConstantFoldingRule FoldRedundantSub() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpISub); + + if (inst->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(1)) { + bool use_float = inst->opcode() == spv::Op::OpFSub; + if (use_float && !inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* type = type_mgr->GetType(inst->type_id()); + if (type->IsCooperativeMatrix()) { + return nullptr; + } + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + uint32_t null_id = const_mgr->GetNullConstId(type); + return const_mgr->FindDeclaredConstant(null_id); + } + return nullptr; + }; +} + // Returns the constant that results from evaluating |numerator| / 0.0. Returns // |nullptr| if the result could not be evaluated. const analysis::Constant* FoldFPScalarDivideByZero( @@ -1045,6 +1075,107 @@ // Returns the constant folding rule to fold |OpFDiv| with two constants. ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); } +// Get a singular uniform value, which is repeated when the |type| is a vector. +const analysis::Constant* GetConstantUniformValue( + analysis::ConstantManager* const_mgr, const analysis::Type* type, + std::optional<double> f = {}, std::optional<uint64_t> i = {}) { + const analysis::Constant* uniform = nullptr; + bool is_vector = false; + const analysis::Type* base_type = type; + + if (base_type->AsVector()) { + is_vector = true; + base_type = base_type->AsVector()->element_type(); + } + + if (f && base_type->AsFloat()) { + if (base_type->AsFloat()->width() == 32) { + uniform = const_mgr->GetConstant( + base_type, utils::FloatProxy<float>((float)f.value()).GetWords()); + } else if (base_type->AsFloat()->width() == 64) { + uniform = const_mgr->GetConstant( + base_type, utils::FloatProxy<double>(f.value()).GetWords()); + } + } else if (i && base_type->AsInteger()) { + uniform = + const_mgr->GenerateIntegerConstant(base_type->AsInteger(), i.value()); + } + + if (!uniform) { + return nullptr; + } + + if (is_vector) { + Instruction* uniform_inst = const_mgr->GetDefiningInstruction(uniform); + if (!uniform_inst) return nullptr; + + uint32_t uniform_id = uniform_inst->result_id(); + uniform = + const_mgr->GetConstant(type, std::vector<uint32_t>(4, uniform_id)); + } + + return uniform; +} + +// x / x = 1 +// -x / x = -1 +// x / -x = -1 +ConstantFoldingRule FoldRedundantDiv() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpFDiv || + inst->opcode() == spv::Op::OpSDiv || + inst->opcode() == spv::Op::OpUDiv); + + if (constants[0] || constants[1]) { + return nullptr; + } + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* type = type_mgr->GetType(inst->type_id()); + + if (type->IsCooperativeMatrix()) { + return nullptr; + } + + bool use_float = inst->opcode() == spv::Op::OpFDiv; + if (use_float && !inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + if (inst->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(1)) { + return GetConstantUniformValue(const_mgr, type, 1.0, 1); + } + + if (inst->opcode() == spv::Op::OpUDiv) { + return nullptr; + } + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + if ((lhs->opcode() == spv::Op::OpSNegate || + lhs->opcode() == spv::Op::OpFNegate) && + lhs->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(1) && + (!use_float || lhs->IsFloatingPointFoldingAllowed())) { + return GetConstantUniformValue(const_mgr, type, -1.0, UINT64_MAX); + } + + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + if ((rhs->opcode() == spv::Op::OpSNegate || + rhs->opcode() == spv::Op::OpFNegate) && + rhs->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(0) && + (!use_float || rhs->IsFloatingPointFoldingAllowed())) { + return GetConstantUniformValue(const_mgr, type, -1.0, UINT64_MAX); + } + + return nullptr; + }; +} + bool CompareFloatingPoint(bool op_result, bool op_unordered, bool need_ordered) { if (need_ordered) { @@ -1124,6 +1255,26 @@ return FoldFPBinaryOp(FOLD_FPCMP_OP(>=, false)); } +ConstantFoldingRule FoldInvariantSelect() { + return [](IRContext*, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpSelect); + (void)inst; + + if (!constants[1] || !constants[2]) { + return nullptr; + } + if (constants[1] == constants[2]) { + return constants[1]; + } + if (constants[1]->IsZero() && constants[2]->IsZero()) { + return constants[1]; + } + return nullptr; + }; +} + // Folds an OpDot where all of the inputs are constants to a // constant. A new constant is created if necessary. ConstantFoldingRule FoldOpDotWithConstants() { @@ -1395,13 +1546,18 @@ if (base_type->AsFloat()->width() == 32) { one = const_mgr->GetConstant(base_type, utils::FloatProxy<float>(1.0f).GetWords()); - } else { + } else if (base_type->AsFloat()->width() == 64) { one = const_mgr->GetConstant(base_type, utils::FloatProxy<double>(1.0).GetWords()); + } else { + // We won't support folding half types. + return nullptr; } if (is_vector) { - uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id(); + Instruction* one_inst = const_mgr->GetDefiningInstruction(one); + if (one_inst == nullptr) return nullptr; + uint32_t one_id = one_inst->result_id(); one = const_mgr->GetConstant(result_type, std::vector<uint32_t>(4, one_id)); } @@ -1428,19 +1584,46 @@ }; } +template <typename FloatType> +static bool NegZeroAwareLessThan(FloatType a, FloatType b) { + if (a == 0.0 && b == 0.0) { + bool sba = std::signbit(a); + bool sbb = std::signbit(b); + if (sba && !sbb) { + return true; + } + } + return a < b; +} + const analysis::Constant* FoldMin(const analysis::Type* result_type, const analysis::Constant* a, const analysis::Constant* b, analysis::ConstantManager*) { if (const analysis::Integer* int_type = result_type->AsInteger()) { - if (int_type->width() == 32) { + if (int_type->width() <= 32) { + assert( + (a->AsIntConstant() != nullptr || a->AsNullConstant() != nullptr) && + "Must be an integer or null constant."); + assert( + (b->AsIntConstant() != nullptr || b->AsNullConstant() != nullptr) && + "Must be an integer or null constant."); + if (int_type->IsSigned()) { - int32_t va = a->GetS32(); - int32_t vb = b->GetS32(); + int32_t va = (a->AsIntConstant() != nullptr) + ? a->AsIntConstant()->GetS32BitValue() + : 0; + int32_t vb = (b->AsIntConstant() != nullptr) + ? b->AsIntConstant()->GetS32BitValue() + : 0; return (va < vb ? a : b); } else { - uint32_t va = a->GetU32(); - uint32_t vb = b->GetU32(); + uint32_t va = (a->AsIntConstant() != nullptr) + ? a->AsIntConstant()->GetU32BitValue() + : 0; + uint32_t vb = (b->AsIntConstant() != nullptr) + ? b->AsIntConstant()->GetU32BitValue() + : 0; return (va < vb ? a : b); } } else if (int_type->width() == 64) { @@ -1458,11 +1641,11 @@ if (float_type->width() == 32) { float va = a->GetFloat(); float vb = b->GetFloat(); - return (va < vb ? a : b); + return NegZeroAwareLessThan(va, vb) ? a : b; } else if (float_type->width() == 64) { double va = a->GetDouble(); double vb = b->GetDouble(); - return (va < vb ? a : b); + return NegZeroAwareLessThan(va, vb) ? a : b; } } return nullptr; @@ -1473,14 +1656,29 @@ const analysis::Constant* b, analysis::ConstantManager*) { if (const analysis::Integer* int_type = result_type->AsInteger()) { - if (int_type->width() == 32) { + if (int_type->width() <= 32) { + assert( + (a->AsIntConstant() != nullptr || a->AsNullConstant() != nullptr) && + "Must be an integer or null constant."); + assert( + (b->AsIntConstant() != nullptr || b->AsNullConstant() != nullptr) && + "Must be an integer or null constant."); + if (int_type->IsSigned()) { - int32_t va = a->GetS32(); - int32_t vb = b->GetS32(); + int32_t va = (a->AsIntConstant() != nullptr) + ? a->AsIntConstant()->GetS32BitValue() + : 0; + int32_t vb = (b->AsIntConstant() != nullptr) + ? b->AsIntConstant()->GetS32BitValue() + : 0; return (va > vb ? a : b); } else { - uint32_t va = a->GetU32(); - uint32_t vb = b->GetU32(); + uint32_t va = (a->AsIntConstant() != nullptr) + ? a->AsIntConstant()->GetU32BitValue() + : 0; + uint32_t vb = (b->AsIntConstant() != nullptr) + ? b->AsIntConstant()->GetU32BitValue() + : 0; return (va > vb ? a : b); } } else if (int_type->width() == 64) { @@ -1498,11 +1696,71 @@ if (float_type->width() == 32) { float va = a->GetFloat(); float vb = b->GetFloat(); - return (va > vb ? a : b); + return NegZeroAwareLessThan(vb, va) ? a : b; } else if (float_type->width() == 64) { double va = a->GetDouble(); double vb = b->GetDouble(); - return (va > vb ? a : b); + return NegZeroAwareLessThan(vb, va) ? a : b; + } + } + return nullptr; +} + +const analysis::Constant* FoldNMin(const analysis::Type* result_type, + const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager*) { + if (const analysis::Float* float_type = result_type->AsFloat()) { + if (float_type->width() == 32) { + float va = a->GetFloat(); + float vb = b->GetFloat(); + if (std::isnan(va)) { + return b; + } + if (std::isnan(vb)) { + return a; + } + return NegZeroAwareLessThan(va, vb) ? a : b; + } else if (float_type->width() == 64) { + double va = a->GetDouble(); + double vb = b->GetDouble(); + if (std::isnan(va)) { + return b; + } + if (std::isnan(vb)) { + return a; + } + return NegZeroAwareLessThan(va, vb) ? a : b; + } + } + return nullptr; +} + +const analysis::Constant* FoldNMax(const analysis::Type* result_type, + const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager*) { + if (const analysis::Float* float_type = result_type->AsFloat()) { + if (float_type->width() == 32) { + float va = a->GetFloat(); + float vb = b->GetFloat(); + if (std::isnan(va)) { + return b; + } + if (std::isnan(vb)) { + return a; + } + return NegZeroAwareLessThan(vb, va) ? a : b; + } else if (float_type->width() == 64) { + double va = a->GetDouble(); + double vb = b->GetDouble(); + if (std::isnan(va)) { + return b; + } + if (std::isnan(vb)) { + return a; + } + return NegZeroAwareLessThan(vb, va) ? a : b; } } return nullptr; @@ -1590,6 +1848,88 @@ return nullptr; } +// Fold an clamp instruction when all three operands are constant. +const analysis::Constant* FoldNClamp1( + IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + // Make sure all Clamp operands are constants. + for (uint32_t i = 1; i < 4; i++) { + if (constants[i] == nullptr) { + return nullptr; + } + } + + const analysis::Constant* temp = FoldFPBinaryOp( + FoldNMax, inst->type_id(), {constants[1], constants[2]}, context); + if (temp == nullptr) { + return nullptr; + } + return FoldFPBinaryOp(FoldNMin, inst->type_id(), {temp, constants[3]}, + context); +} + +// Fold a clamp instruction when |x <= min_val|. +const analysis::Constant* FoldNClamp2( + IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + const analysis::Constant* x = constants[1]; + const analysis::Constant* min_val = constants[2]; + + if (x == nullptr || min_val == nullptr) { + return nullptr; + } + + const analysis::Constant* temp = + FoldFPBinaryOp(FoldNMax, inst->type_id(), {x, min_val}, context); + if (temp == min_val) { + // We can assume that |min_val| is less than |max_val|. Therefore, if the + // result of the max operation is |min_val|, we know the result of the min + // operation, even if |max_val| is not a constant. + return min_val; + } + return nullptr; +} + +// Fold a clamp instruction when |x >= max_val|. +const analysis::Constant* FoldNClamp3( + IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + const analysis::Constant* x = constants[1]; + const analysis::Constant* max_val = constants[3]; + + if (x == nullptr || max_val == nullptr) { + return nullptr; + } + + const analysis::Constant* temp = + FoldFPBinaryOp(FoldNMin, inst->type_id(), {x, max_val}, context); + if (temp == max_val) { + // We can assume that |min_val| is less than |max_val|. Therefore, if the + // result of the max operation is |min_val|, we know the result of the min + // operation, even if |max_val| is not a constant. + return max_val; + } + return nullptr; +} + UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) { return [fp](const analysis::Type* result_type, const analysis::Constant* a, @@ -1734,9 +2074,16 @@ rules_[spv::Op::OpDot].push_back(FoldOpDotWithConstants()); rules_[spv::Op::OpFAdd].push_back(FoldFAdd()); + rules_[spv::Op::OpFDiv].push_back(FoldFDiv()); + rules_[spv::Op::OpFDiv].push_back(FoldRedundantDiv()); + rules_[spv::Op::OpFMul].push_back(FoldFMul()); + rules_[spv::Op::OpFSub].push_back(FoldFSub()); + rules_[spv::Op::OpFSub].push_back(FoldRedundantSub()); + + rules_[spv::Op::OpSelect].push_back(FoldInvariantSelect()); rules_[spv::Op::OpFOrdEqual].push_back(FoldFOrdEqual()); @@ -1792,21 +2139,29 @@ rules_[spv::Op::OpIAdd].push_back( FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>( [](uint64_t a, uint64_t b) { return a + b; }))); + rules_[spv::Op::OpISub].push_back( FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>( [](uint64_t a, uint64_t b) { return a - b; }))); + rules_[spv::Op::OpISub].push_back(FoldRedundantSub()); + rules_[spv::Op::OpIMul].push_back( FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>( [](uint64_t a, uint64_t b) { return a * b; }))); + rules_[spv::Op::OpUDiv].push_back( FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>( [](uint64_t a, uint64_t b) { return (b != 0 ? a / b : 0); }))); + rules_[spv::Op::OpUDiv].push_back(FoldRedundantDiv()); + rules_[spv::Op::OpSDiv].push_back(FoldBinaryOp( FoldBinaryIntegerOperation<Signed>([](uint64_t a, uint64_t b) { return (b != 0 ? static_cast<uint64_t>(static_cast<int64_t>(a) / static_cast<int64_t>(b)) : 0); }))); + rules_[spv::Op::OpSDiv].push_back(FoldRedundantDiv()); + rules_[spv::Op::OpUMod].push_back( FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>( [](uint64_t a, uint64_t b) { return (b != 0 ? a % b : 0); }))); @@ -1841,12 +2196,16 @@ FoldFPBinaryOp(FoldMin)); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMin}].push_back( FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NMin}].push_back( + FoldFPBinaryOp(FoldNMin)); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMax}].push_back( FoldFPBinaryOp(FoldMax)); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMax}].push_back( FoldFPBinaryOp(FoldMax)); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMax}].push_back( FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NMax}].push_back( + FoldFPBinaryOp(FoldNMax)); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( FoldClamp1); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( @@ -1865,6 +2224,12 @@ FoldClamp2); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NClamp}].push_back( + FoldNClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NClamp}].push_back( + FoldNClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NClamp}].push_back( + FoldNClamp3); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back( FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin))); ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back(
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index 7dc02de..f443e2a 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp
@@ -302,6 +302,8 @@ inst->GetInOperand(i).words.end()); } + const Type* type = GetType(inst); + switch (inst->opcode()) { // OpConstant{True|False} have the value embedded in the opcode. So they // are not handled by the for-loop above. Here we add the value explicitly. @@ -316,11 +318,20 @@ case spv::Op::OpConstantComposite: case spv::Op::OpSpecConstantComposite: break; + // Replicated composite constant instructions have a single operand for the + // value. We need to replicate it as many times as there are components. + case spv::Op::OpConstantCompositeReplicateEXT: + case spv::Op::OpSpecConstantCompositeReplicateEXT: { + uint32_t value = literal_words_or_ids[0]; + literal_words_or_ids.assign( + static_cast<size_t>(type->NumberOfComponents()), value); + break; + } default: return nullptr; } - return GetConstant(GetType(inst), literal_words_or_ids); + return GetConstant(type, literal_words_or_ids); } std::unique_ptr<Instruction> ConstantManager::CreateInstruction( @@ -461,7 +472,9 @@ uint32_t ConstantManager::GetFloatConstId(float val) { const Constant* c = GetFloatConst(val); - return GetDefiningInstruction(c)->result_id(); + Instruction* inst = GetDefiningInstruction(c); + if (inst == nullptr) return 0; + return inst->result_id(); } const Constant* ConstantManager::GetFloatConst(float val) { @@ -473,7 +486,9 @@ uint32_t ConstantManager::GetDoubleConstId(double val) { const Constant* c = GetDoubleConst(val); - return GetDefiningInstruction(c)->result_id(); + Instruction* inst = GetDefiningInstruction(c); + if (inst == nullptr) return 0; + return inst->result_id(); } const Constant* ConstantManager::GetDoubleConst(double val) {
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index e243bed..a3527ff 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp
@@ -22,6 +22,7 @@ namespace opt { namespace { // Indices of operands in SPIR-V instructions +constexpr int kImageSampleCoordinateIdInIdx = 1; constexpr int kImageSampleDrefIdInIdx = 2; } // namespace @@ -75,6 +76,9 @@ analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len, uint32_t width) { analysis::Type* reg_float_ty = FloatScalarType(width); + if (reg_float_ty == nullptr) { + return nullptr; + } analysis::Vector vec_ty(reg_float_ty, v_len); return context()->get_type_mgr()->GetRegisteredType(&vec_ty); } @@ -85,6 +89,9 @@ Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id); uint32_t v_len = vty_inst->GetSingleWordInOperand(1); analysis::Type* reg_vec_ty = FloatVectorType(v_len, width); + if (reg_vec_ty == nullptr) { + return nullptr; + } analysis::Matrix mat_ty(reg_vec_ty, v_cnt); return context()->get_type_mgr()->GetRegisteredType(&mat_ty); } @@ -99,6 +106,9 @@ reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width); else // spv::Op::OpTypeFloat reg_equiv_ty = FloatScalarType(width); + if (reg_equiv_ty == nullptr) { + return 0; + } return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty); } @@ -107,6 +117,10 @@ Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp); uint32_t ty_id = val_inst->type_id(); uint32_t nty_id = EquivFloatTypeId(ty_id, width); + if (nty_id == 0) { + status_ = Status::Failure; + return; + } if (nty_id == ty_id) return; Instruction* cvt_inst; InstructionBuilder builder( @@ -116,6 +130,10 @@ cvt_inst = builder.AddNullaryOp(nty_id, spv::Op::OpUndef); else cvt_inst = builder.AddUnaryOp(nty_id, spv::Op::OpFConvert, *val_idp); + if (cvt_inst == nullptr) { + status_ = Status::Failure; + return; + } *val_idp = cvt_inst->result_id(); } @@ -137,22 +155,43 @@ uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16; uint32_t orig_mat_id = inst->GetSingleWordInOperand(0); uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width); + if (orig_vty_id == 0) { + status_ = Status::Failure; + return false; + } std::vector<Operand> opnds = {}; for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) { Instruction* ext_inst = builder.AddIdLiteralOp( orig_vty_id, spv::Op::OpCompositeExtract, orig_mat_id, vidx); + if (ext_inst == nullptr) { + status_ = Status::Failure; + return false; + } Instruction* cvt_inst = builder.AddUnaryOp(vty_id, spv::Op::OpFConvert, ext_inst->result_id()); + if (cvt_inst == nullptr) { + status_ = Status::Failure; + return false; + } opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}}); } uint32_t mat_id = TakeNextId(); + if (mat_id == 0) { + status_ = Status::Failure; + return false; + } std::unique_ptr<Instruction> mat_inst(new Instruction( context(), spv::Op::OpCompositeConstruct, mty_id, mat_id, opnds)); (void)builder.AddInstruction(std::move(mat_inst)); context()->ReplaceAllUsesWith(inst->result_id(), mat_id); // Turn original instruction into copy so it is valid. + uint32_t new_type_id = EquivFloatTypeId(mty_id, orig_width); + if (new_type_id == 0) { + status_ = Status::Failure; + return false; + } inst->SetOpcode(spv::Op::OpCopyObject); - inst->SetResultType(EquivFloatTypeId(mty_id, orig_width)); + inst->SetResultType(new_type_id); get_def_use_mgr()->AnalyzeInstUse(inst); return true; } @@ -187,13 +226,24 @@ // Convert all float32 based operands to float16 equivalent and change // instruction type to float16 equivalent. inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + if (status_ == Status::Failure) { + return; + } Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); if (!IsFloat(op_inst, 32)) return; GenConvert(idp, 16, inst); modified = true; }); + if (status_ == Status::Failure) { + return false; + } if (IsFloat(inst, 32)) { - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16); + if (new_type_id == 0) { + status_ = Status::Failure; + return false; + } + inst->SetResultType(new_type_id); converted_ids_.insert(inst->result_id()); modified = true; } @@ -211,6 +261,9 @@ bool modified = false; inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified, this](uint32_t* idp) { + if (status_ == Status::Failure) { + return; + } if (ocnt % 2 == 0) { prev_idp = idp; } else { @@ -230,8 +283,16 @@ } ++ocnt; }); + if (status_ == Status::Failure) { + return false; + } if (to_width == 16u) { - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u)); + uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16u); + if (new_type_id == 0) { + status_ = Status::Failure; + return false; + } + inst->SetResultType(new_type_id); converted_ids_.insert(inst->result_id()); modified = true; } @@ -242,7 +303,12 @@ bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { // If float32 and relaxed, change to float16 convert if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) { - inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16); + if (new_type_id == 0) { + status_ = Status::Failure; + return false; + } + inst->SetResultType(new_type_id); get_def_use_mgr()->AnalyzeInstUse(inst); converted_ids_.insert(inst->result_id()); } @@ -255,21 +321,37 @@ Instruction* val_inst = get_def_use_mgr()->GetDef(val_id); if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(spv::Op::OpCopyObject); - return true; // modified + return true; } bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) { bool modified = false; - // If image reference, only need to convert dref args back to float32 + // If image reference, some operands aren't allowed to be non-32 bit floats if (dref_image_ops_.count(inst->opcode()) != 0) { uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx); if (converted_ids_.count(dref_id) > 0) { GenConvert(&dref_id, 32, inst); + if (status_ == Status::Failure) { + return false; + } inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id}); get_def_use_mgr()->AnalyzeInstUse(inst); modified = true; } } + if (coordinate_image_ops_.count(inst->opcode()) != 0) { + uint32_t coordinate_id = + inst->GetSingleWordInOperand(kImageSampleCoordinateIdInIdx); + if (converted_ids_.count(coordinate_id) > 0) { + GenConvert(&coordinate_id, 32, inst); + if (status_ == Status::Failure) { + return false; + } + inst->SetInOperand(kImageSampleCoordinateIdInIdx, {coordinate_id}); + get_def_use_mgr()->AnalyzeInstUse(inst); + modified = true; + } + } return modified; } @@ -279,11 +361,17 @@ if (inst->opcode() == spv::Op::OpPhi) return ProcessPhi(inst, 16u, 32u); bool modified = false; inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + if (status_ == Status::Failure) { + return; + } if (converted_ids_.count(*idp) == 0) return; uint32_t old_id = *idp; GenConvert(idp, 32, inst); if (*idp != old_id) modified = true; }); + if (status_ == Status::Failure) { + return false; + } if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); return modified; } @@ -370,19 +458,38 @@ }); // Replace invalid converts of matrix into equivalent vector extracts, // converts and finally a composite construct + bool ok = true; cfg()->ForEachBlockInReversePostOrder( - func->entry().get(), [&modified, this](BasicBlock* bb) { - for (auto ii = bb->begin(); ii != bb->end(); ++ii) - modified |= MatConvertCleanup(&*ii); + func->entry().get(), [&modified, &ok, this](BasicBlock* bb) { + if (!ok) { + return; + } + for (auto ii = bb->begin(); ii != bb->end(); ++ii) { + bool Mmodified = MatConvertCleanup(&*ii); + if (status_ == Status::Failure) { + ok = false; + break; + } + modified |= Mmodified; + } }); + + if (!ok) { + return false; + } return modified; } Pass::Status ConvertToHalfPass::ProcessImpl() { + status_ = Status::SuccessWithoutChange; Pass::ProcessFunction pfn = [this](Function* fp) { return ProcessFunction(fp); }; bool modified = context()->ProcessReachableCallTree(pfn); + if (status_ == Status::Failure) { + return status_; + } + // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(spv::Capability::Float16); // Remove all RelaxedPrecision decorations from instructions and globals @@ -498,6 +605,30 @@ spv::Op::OpImageSparseSampleProjDrefExplicitLod, spv::Op::OpImageSparseDrefGather, }; + coordinate_image_ops_ = { + spv::Op::OpImageSampleImplicitLod, + spv::Op::OpImageSampleExplicitLod, + spv::Op::OpImageSampleDrefImplicitLod, + spv::Op::OpImageSampleDrefExplicitLod, + spv::Op::OpImageSampleProjImplicitLod, + spv::Op::OpImageSampleProjExplicitLod, + spv::Op::OpImageSampleProjDrefImplicitLod, + spv::Op::OpImageSampleProjDrefExplicitLod, + spv::Op::OpImageFetch, + spv::Op::OpImageGather, + spv::Op::OpImageDrefGather, + spv::Op::OpImageRead, + spv::Op::OpImageWrite, + spv::Op::OpImageQueryLod, + spv::Op::OpImageSparseSampleImplicitLod, + spv::Op::OpImageSparseSampleExplicitLod, + spv::Op::OpImageSparseSampleDrefImplicitLod, + spv::Op::OpImageSparseSampleDrefExplicitLod, + spv::Op::OpImageSparseFetch, + spv::Op::OpImageSparseGather, + spv::Op::OpImageSparseDrefGather, + spv::Op::OpImageSparseRead, + }; closure_ops_ = { spv::Op::OpVectorExtractDynamic, spv::Op::OpVectorInsertDynamic, @@ -514,4 +645,4 @@ } } // namespace opt -} // namespace spvtools +} // namespace spvtools \ No newline at end of file
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h index 8e10c4f..c04f27a 100644 --- a/source/opt/convert_to_half_pass.h +++ b/source/opt/convert_to_half_pass.h
@@ -130,6 +130,9 @@ } }; + // The status of the pass. + Pass::Status status_; + // Set of core operations to be processed std::unordered_set<spv::Op, hasher> target_ops_core_; @@ -142,6 +145,9 @@ // Set of only dref sample operations std::unordered_set<spv::Op, hasher> dref_image_ops_; + // Set of only sample operations that have a Coordinate operand + std::unordered_set<spv::Op, hasher> coordinate_image_ops_; + // Set of operations that can be marked as relaxed std::unordered_set<spv::Op, hasher> closure_ops_;
diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp index d2da4d1..e720392 100644 --- a/source/opt/convert_to_sampled_image_pass.cpp +++ b/source/opt/convert_to_sampled_image_pass.cpp
@@ -246,9 +246,10 @@ InstructionBuilder builder( context(), sampled_image->NextNode(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - return builder.AddUnaryOp( + Instruction* result = builder.AddUnaryOp( GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image), spv::Op::OpImage, sampled_image->result_id()); + return result; } uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage( @@ -270,6 +271,9 @@ if (uses_of_load.empty()) return nullptr; auto* extracted_image = CreateImageExtraction(sampled_image_load); + if (extracted_image == nullptr) { + return nullptr; + } for (auto* user : uses_of_load) { user->SetInOperand(0, {extracted_image->result_id()}); context()->get_def_use_mgr()->AnalyzeInstUse(user); @@ -306,8 +310,12 @@ def_use_mgr->AnalyzeInstUse(image_load); context()->KillInst(sampled_image_inst); } else { - if (!image_extraction) + if (!image_extraction) { image_extraction = CreateImageExtraction(image_load); + if (image_extraction == nullptr) { + return; + } + } sampled_image_inst->SetInOperand(0, {image_extraction->result_id()}); def_use_mgr->AnalyzeInstUse(sampled_image_inst); } @@ -333,6 +341,9 @@ // reference. uint32_t type_id = context()->get_type_mgr()->FindPointerToType( sampled_image_type_id, storage_class); + if (type_id == 0) { + return false; + } MoveInstructionNextToType(image_variable, type_id); return true; }
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp index 0a42074..547a5e4 100644 --- a/source/opt/copy_prop_arrays.cpp +++ b/source/opt/copy_prop_arrays.cpp
@@ -95,16 +95,25 @@ std::unique_ptr<MemoryObject> source_object = FindSourceObjectIfPossible(&*var_inst, store_inst); - if (source_object != nullptr) { - if (!IsPointerToArrayType(var_inst->type_id()) && - source_object->GetStorageClass() != spv::StorageClass::Input) { - continue; - } + if (source_object == nullptr) { + continue; + } - if (CanUpdateUses(&*var_inst, source_object->GetPointerTypeId(this))) { - modified = true; + if (!IsPointerToArrayType(var_inst->type_id()) && + source_object->GetStorageClass() != spv::StorageClass::Input) { + continue; + } - PropagateObject(&*var_inst, source_object.get(), store_inst); + uint32_t pointer_type_id = source_object->GetPointerTypeId(this); + if (pointer_type_id == 0) { + return Status::Failure; + } + + if (CanUpdateUses(&*var_inst, pointer_type_id)) { + modified = true; + + if (!PropagateObject(&*var_inst, source_object.get(), store_inst)) { + return Status::Failure; } } } @@ -168,15 +177,16 @@ return store_inst; } -void CopyPropagateArrays::PropagateObject(Instruction* var_inst, +bool CopyPropagateArrays::PropagateObject(Instruction* var_inst, MemoryObject* source, Instruction* insertion_point) { assert(var_inst->opcode() == spv::Op::OpVariable && "This function propagates variables."); Instruction* new_access_chain = BuildNewAccessChain(insertion_point, source); + if (!new_access_chain) return false; context()->KillNamesAndDecorates(var_inst); - UpdateUses(var_inst, new_access_chain); + return UpdateUses(var_inst, new_access_chain); } Instruction* CopyPropagateArrays::BuildNewAccessChain( @@ -190,7 +200,7 @@ return source->GetVariable(); } - source->BuildConstants(); + if (!source->BuildConstants()) return nullptr; std::vector<uint32_t> access_ids(source->AccessChain().size()); std::transform( source->AccessChain().cbegin(), source->AccessChain().cend(), @@ -219,6 +229,8 @@ return true; } else if (IsInterpolationInstruction(use)) { return true; + } else if (use->IsCommonDebugInstr()) { + return true; } // Some other instruction. Be conservative. return false; @@ -252,11 +264,14 @@ } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) { return true; } else if (use->opcode() == spv::Op::OpStore) { - // If we are storing to part of the object it is not an candidate. + // If we are storing to part of the object it is not a candidate. return ptr_inst->opcode() == spv::Op::OpVariable && store_inst->GetSingleWordInOperand(kStorePointerInOperand) == ptr_inst->result_id(); } else if (IsDebugDeclareOrValue(use)) { + // The store does not have to dominate debug instructions. We do not + // want debugging info to stop the transformation. It will be fixed + // up later. return true; } // Some other instruction. Be conservative. @@ -276,6 +291,7 @@ case spv::Op::OpCompositeConstruct: return BuildMemoryObjectFromCompositeConstruct(result_inst); case spv::Op::OpCopyObject: + case spv::Op::OpCopyLogical: return GetSourceObjectIfAny(result_inst->GetSingleWordInOperand(0)); case spv::Op::OpCompositeInsert: return BuildMemoryObjectFromInsert(result_inst); @@ -634,7 +650,7 @@ }); } -void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, +bool CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, Instruction* new_ptr_inst) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); @@ -651,6 +667,22 @@ uint32_t index = pair.second; if (use->IsCommonDebugInstr()) { + // It is possible that the debug instructions are not dominated by + // `new_ptr_inst`. If not, move the debug instruction to just after + // `new_ptr_inst`. + BasicBlock* store_block = context()->get_instr_block(new_ptr_inst); + if (store_block) { + Function* function = store_block->GetParent(); + DominatorAnalysis* dominator_analysis = + context()->GetDominatorAnalysis(function); + if (!dominator_analysis->Dominates(new_ptr_inst, use)) { + assert(dominator_analysis->Dominates(use, new_ptr_inst)); + use->InsertAfter(new_ptr_inst); + context()->set_instr_block(use, + context()->get_instr_block(new_ptr_inst)); + } + } + switch (use->GetCommonDebugOpcode()) { case CommonDebugInfoDebugDeclare: { if (new_ptr_inst->opcode() == spv::Op::OpVariable || @@ -675,6 +707,7 @@ def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1)); auto* deref_expr_instr = context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); + if (!deref_expr_instr) return false; use->SetOperand(index + 1, {deref_expr_instr->result_id()}); context()->AnalyzeUses(deref_expr_instr); @@ -759,6 +792,8 @@ uint32_t new_pointer_type_id = type_mgr->FindPointerToType(new_pointee_type_id, storage_class); + if (new_pointer_type_id == 0) return false; + if (new_pointer_type_id != use->type_id()) { use->SetResultType(new_pointer_type_id); context()->AnalyzeUses(use); @@ -805,8 +840,7 @@ uint32_t pointee_type_id = pointer_type->GetSingleWordInOperand(kTypePointerPointeeInIdx); uint32_t copy = GenerateCopy(original_ptr_inst, pointee_type_id, use); - assert(copy != 0 && - "Should not be updating uses unless we know it can be done."); + if (copy == 0) return false; context()->ForgetUses(use); use->SetInOperand(index, {copy}); @@ -828,6 +862,7 @@ break; } } + return true; } uint32_t CopyPropagateArrays::GetMemberTypeId( @@ -892,9 +927,7 @@ iterator begin, iterator end) : variable_inst_(var_inst) { std::transform(begin, end, std::back_inserter(access_chain_), - [](uint32_t id) { - return AccessChainEntry{true, {id}}; - }); + [](uint32_t id) { return AccessChainEntry{true, {id}}; }); } std::vector<uint32_t> CopyPropagateArrays::MemoryObject::GetAccessIds() const { @@ -933,7 +966,7 @@ return true; } -void CopyPropagateArrays::MemoryObject::BuildConstants() { +bool CopyPropagateArrays::MemoryObject::BuildConstants() { for (auto& entry : access_chain_) { if (entry.is_result_id) { continue; @@ -946,10 +979,13 @@ analysis::ConstantManager* const_mgr = context->get_constant_mgr(); const analysis::Constant* index_const = const_mgr->GetConstant(uint32_type, {entry.immediate}); - entry.result_id = - const_mgr->GetDefiningInstruction(index_const)->result_id(); + if (!index_const) return false; + Instruction* constant_inst = const_mgr->GetDefiningInstruction(index_const); + if (!constant_inst) return false; + entry.result_id = constant_inst->result_id(); entry.is_result_id = true; } + return true; } } // namespace opt
diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h index bf4bfb5..cb04a14 100644 --- a/source/opt/copy_prop_arrays.h +++ b/source/opt/copy_prop_arrays.h
@@ -118,7 +118,8 @@ // Converts all immediate values in the AccessChain their OpConstant // equivalent. - void BuildConstants(); + // Returns false if the constants could not be created. + bool BuildConstants(); // Returns the type id of the pointer type that can be used to point to this // memory object. @@ -175,7 +176,8 @@ // Replaces all loads of |var_inst| with a load from |source| instead. // |insertion_pos| is a position where it is possible to construct the // address of |source| and also dominates all of the loads of |var_inst|. - void PropagateObject(Instruction* var_inst, MemoryObject* source, + // Returns false if the propagation failed. + bool PropagateObject(Instruction* var_inst, MemoryObject* source, Instruction* insertion_pos); // Returns true if all of the references to |ptr_inst| can be rewritten and @@ -241,7 +243,7 @@ // types of other instructions as needed. This function should not be called // if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns // false. - void UpdateUses(Instruction* original_ptr_inst, + bool UpdateUses(Instruction* original_ptr_inst, Instruction* new_pointer_inst); // Return true if |UpdateUses| is able to change all of the uses of
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 24094b3..e7fa1d7 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp
@@ -19,7 +19,7 @@ #include "source/opt/ir_context.h" -// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100 +// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo // extension instructions. namespace spvtools { @@ -86,8 +86,7 @@ uint32_t setId = context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); if (setId == 0) { - setId = - context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + setId = context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo(); } return setId; } @@ -118,14 +117,14 @@ fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && "Register DebugFunction for a function that already has DebugFunction"); fn_id_to_dbg_fn_[fn_id] = inst; - } else if (inst->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + } else if (inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunctionDefinition) { auto fn_id = inst->GetSingleWordOperand( kDebugFunctionDefinitionOperandOpFunctionIndex); auto fn_inst = GetDbgInst(inst->GetSingleWordOperand( kDebugFunctionDefinitionOperandDebugFunctionIndex)); - assert(fn_inst && fn_inst->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugFunction); + assert(fn_inst && fn_inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunction); assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && "Register DebugFunctionDefinition for a function that already has " "DebugFunctionDefinition"); @@ -176,10 +175,10 @@ spv_operand_type_t line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER; - // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant, + // In NonSemantic.Shader.DebugInfo, all constants are IDs of OpConstant, // not literals. if (setId == - context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) + context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo()) line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID; uint32_t line_number = 0; @@ -214,8 +213,8 @@ } else { if (line->opcode() == spv::Op::OpLine) { line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); - } else if (line->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugLine) { + } else if (line->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugLine) { line_number = line->GetSingleWordOperand(kLineOperandIndexDebugLine); } else { assert(false && @@ -331,6 +330,7 @@ if (deref_operation_ != nullptr) return deref_operation_; uint32_t result_id = context()->TakeNextId(); + if (result_id == 0) return nullptr; std::unique_ptr<Instruction> deref_operation; if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { @@ -346,18 +346,17 @@ })); } else { uint32_t deref_id = context()->get_constant_mgr()->GetUIntConstId( - NonSemanticShaderDebugInfo100Deref); + NonSemanticShaderDebugInfoDeref); - deref_operation = std::unique_ptr<Instruction>( - new Instruction(context(), spv::Op::OpExtInst, - context()->get_type_mgr()->GetVoidTypeId(), result_id, - { - {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, - {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, - {static_cast<uint32_t>( - NonSemanticShaderDebugInfo100DebugOperation)}}, - {SPV_OPERAND_TYPE_ID, {deref_id}}, - })); + deref_operation = std::unique_ptr<Instruction>(new Instruction( + context(), spv::Op::OpExtInst, + context()->get_type_mgr()->GetVoidTypeId(), result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugOperation)}}, + {SPV_OPERAND_TYPE_ID, {deref_id}}, + })); } // Add to the front of |ext_inst_debuginfo_|. @@ -374,10 +373,13 @@ Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression); std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context())); - deref_expr->SetResultId(context()->TakeNextId()); - deref_expr->InsertOperand( - kDebugExpressOperandOperationIndex, - {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}}); + uint32_t result_id = context()->TakeNextId(); + if (result_id == 0) return nullptr; + deref_expr->SetResultId(result_id); + Instruction* deref_op = GetDebugOperationWithDeref(); + if (!deref_op) return nullptr; + deref_expr->InsertOperand(kDebugExpressOperandOperationIndex, + {SPV_OPERAND_TYPE_ID, {deref_op->result_id()}}); auto* deref_expr_instr = context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr)); AnalyzeDebugInst(deref_expr_instr); @@ -390,6 +392,7 @@ if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_; uint32_t result_id = context()->TakeNextId(); + if (result_id == 0) return nullptr; std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction( context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(), result_id, @@ -558,11 +561,11 @@ return false; } -bool DebugInfoManager::AddDebugValueForVariable(Instruction* scope_and_line, +bool DebugInfoManager::AddDebugValueForVariable(Instruction* line, uint32_t variable_id, uint32_t value_id, Instruction* insert_pos) { - assert(scope_and_line != nullptr); + assert(line != nullptr); auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false; @@ -577,14 +580,15 @@ insert_before = insert_before->NextNode(); } modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before, - scope_and_line) != nullptr; + line) != nullptr; } return modified; } -Instruction* DebugInfoManager::AddDebugValueForDecl( - Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before, - Instruction* scope_and_line) { +Instruction* DebugInfoManager::AddDebugValueForDecl(Instruction* dbg_decl, + uint32_t value_id, + Instruction* insert_before, + Instruction* line) { if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr; std::unique_ptr<Instruction> dbg_val(dbg_decl->Clone(context())); @@ -593,7 +597,7 @@ dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); dbg_val->SetOperand(kDebugValueOperandExpressionIndex, {GetEmptyDebugExpression()->result_id()}); - dbg_val->UpdateDebugInfoFrom(scope_and_line); + dbg_val->UpdateDebugInfoFrom(dbg_decl, line); auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val)); AnalyzeDebugInst(added_dbg_val); @@ -608,8 +612,8 @@ } uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) { - assert(inst->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugOperation && + assert(inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugOperation && "inst must be Vulkan DebugOperation"); return context() ->get_constant_mgr() @@ -640,7 +644,7 @@ } } else { uint32_t operation_const = GetVulkanDebugOperation(operation); - if (operation_const != NonSemanticShaderDebugInfo100Deref) { + if (operation_const != NonSemanticShaderDebugInfoDeref) { return 0; } } @@ -717,8 +721,8 @@ RegisterDbgInst(inst); if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction || - inst->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunctionDefinition) { RegisterDbgFunction(inst); } @@ -730,10 +734,10 @@ } if (deref_operation_ == nullptr && - inst->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugOperation) { + inst->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugOperation) { uint32_t operation_const = GetVulkanDebugOperation(inst); - if (operation_const == NonSemanticShaderDebugInfo100Deref) { + if (operation_const == NonSemanticShaderDebugInfoDeref) { deref_operation_ = inst; } } @@ -870,8 +874,8 @@ instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); } - if (instr->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + if (instr->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunctionDefinition) { auto fn_id = instr->GetSingleWordOperand( kDebugFunctionDefinitionOperandOpFunctionIndex); fn_id_to_dbg_fn_.erase(fn_id); @@ -903,10 +907,10 @@ deref_operation_ = &*dbg_instr_itr; break; } else if (instr != &*dbg_instr_itr && - dbg_instr_itr->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugOperation) { + dbg_instr_itr->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugOperation) { uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr); - if (operation_const == NonSemanticShaderDebugInfo100Deref) { + if (operation_const == NonSemanticShaderDebugInfoDeref) { deref_operation_ = &*dbg_instr_itr; break; }
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h index abb7b9a..a23e9ab 100644 --- a/source/opt/debug_info_manager.h +++ b/source/opt/debug_info_manager.h
@@ -69,7 +69,7 @@ }; // A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and -// NonSemantic.Shader.DebugInfo.100 extension instructions. +// NonSemantic.Shader.DebugInfo extension instructions. class DebugInfoManager { public: // Constructs a debug information manager from the given |context|. @@ -143,22 +143,21 @@ bool KillDebugDeclares(uint32_t variable_id); // Generates a DebugValue instruction with value |value_id| for every local - // variable that is in the scope of |scope_and_line| and whose memory is - // |variable_id| and inserts it after the instruction |insert_pos|. + // variable that is in the scope of |line| and whose memory is |variable_id| + // and inserts it after the instruction |insert_pos|. // Returns whether a DebugValue is added or not. - bool AddDebugValueForVariable(Instruction* scope_and_line, - uint32_t variable_id, uint32_t value_id, - Instruction* insert_pos); + bool AddDebugValueForVariable(Instruction* line, uint32_t variable_id, + uint32_t value_id, Instruction* insert_pos); // Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before - // |insert_before|. The new DebugValue has the same line and scope as - // |scope_and_line|, or no scope and line information if |scope_and_line| - // is nullptr. The new DebugValue has the same operands as DebugDeclare - // but it uses |value_id| for the value. Returns the created DebugValue, + // |insert_before|. The new DebugValue has the same line as |line} and the + // same scope as |dbg_decl|. The new DebugValue has the same operands as + // DebugDeclare but it uses |value_id| for the value. Returns the created + // DebugValue, // or nullptr if fails to create one. Instruction* AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before, - Instruction* scope_and_line); + Instruction* line); // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr);
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 3e95dbc..bee7d94 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp
@@ -543,7 +543,8 @@ const uint32_t num_operands = inst->NumOperands(); for (uint32_t i = 1; i < num_operands; i += 2) { Operand op = inst->GetOperand(i); - if (op.words[0] == from) { // add new pair of operands: (to, literal) + if (!op.words.empty() && + op.words[0] == from) { // add new pair of operands: (to, literal) inst->AddOperand( Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to})); op = inst->GetOperand(i + 1);
diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index 124a3d3..b2f1268 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp
@@ -58,7 +58,7 @@ std::vector<Instruction*> access_chain_work_list; std::vector<Instruction*> load_work_list; std::vector<Instruction*> entry_point_work_list; - bool failed = !get_def_use_mgr()->WhileEachUser( + bool ok = get_def_use_mgr()->WhileEachUser( var->result_id(), [this, &access_chain_work_list, &load_work_list, &entry_point_work_list](Instruction* use) { if (use->opcode() == spv::Op::OpName) { @@ -88,7 +88,7 @@ return true; }); - if (failed) { + if (!ok) { return false; } @@ -128,6 +128,9 @@ uint32_t idx = const_index->GetU32(); uint32_t replacement_var = GetReplacementVariable(var, idx); + if (replacement_var == 0) { + return false; + } if (use->NumInOperands() == 2) { // We are not indexing into the replacement variable. We can replaces the @@ -186,8 +189,11 @@ uint32_t num_replacement_vars = descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); for (uint32_t i = 0; i < num_replacement_vars; i++) { - new_operands.push_back( - {SPV_OPERAND_TYPE_ID, {GetReplacementVariable(var, i)}}); + uint32_t replacement_var_id = GetReplacementVariable(var, i); + if (replacement_var_id == 0) { + return false; + } + new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var_id}}); } use->ReplaceOperands(new_operands); @@ -310,7 +316,10 @@ element_type_id, storage_class); // Create the variable. - uint32_t id = TakeNextId(); + uint32_t id = context()->TakeNextId(); + if (id == 0) { + return 0; + } std::unique_ptr<Instruction> variable( new Instruction(context(), spv::Op::OpVariable, ptr_element_type_id, id, std::initializer_list<Operand>{ @@ -444,10 +453,16 @@ uint32_t replacement_var = GetReplacementVariable(var, extract->GetSingleWordInOperand(1)); + if (replacement_var == 0) { + return false; + } // The result type of the OpLoad is the same as the result type of the // OpCompositeExtract. - uint32_t load_id = TakeNextId(); + uint32_t load_id = context()->TakeNextId(); + if (load_id == 0) { + return false; + } std::unique_ptr<Instruction> load( new Instruction(context(), spv::Op::OpLoad, extract->type_id(), load_id, std::initializer_list<Operand>{
diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp index e440296..33c0503 100644 --- a/source/opt/eliminate_dead_members_pass.cpp +++ b/source/opt/eliminate_dead_members_pass.cpp
@@ -207,6 +207,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: type_id = type_inst->GetSingleWordInOperand(0); break; default: @@ -255,6 +256,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: type_id = type_inst->GetSingleWordInOperand(0); break; default: @@ -516,6 +518,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: new_operands.emplace_back(inst->GetInOperand(i)); type_id = type_inst->GetSingleWordInOperand(0); break; @@ -591,6 +594,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: type_id = type_inst->GetSingleWordInOperand(0); break; default: @@ -654,6 +658,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: type_id = type_inst->GetSingleWordInOperand(0); break; default:
diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index 5188370..c5c9c4c 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp
@@ -16,7 +16,7 @@ #include <string> -#include "source/enum_string_mapping.h" +#include "source/table2.h" namespace spvtools { namespace opt { @@ -34,10 +34,13 @@ } void FeatureManager::AddExtension(Instruction* ext) { - assert(ext->opcode() == spv::Op::OpExtension && + assert((ext->opcode() == spv::Op::OpExtension || + ext->opcode() == spv::Op::OpConditionalExtensionINTEL) && "Expecting an extension instruction."); - const std::string name = ext->GetInOperand(0u).AsString(); + const uint32_t name_i = + ext->opcode() == spv::Op::OpConditionalExtensionINTEL ? 1u : 0u; + const std::string name = ext->GetInOperand(name_i).AsString(); Extension extension; if (GetExtensionFromString(name.c_str(), &extension)) { extensions_.insert(extension); @@ -54,11 +57,12 @@ capabilities_.insert(cap); - spv_operand_desc desc = {}; - if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, - uint32_t(cap), &desc)) { + const spvtools::OperandDesc* desc = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + uint32_t(cap), &desc)) { for (auto capability : - CapabilitySet(desc->numCapabilities, desc->capabilities)) { + CapabilitySet(static_cast<uint32_t>(desc->capabilities().size()), + desc->capabilities().data())) { AddCapability(capability); } } @@ -71,7 +75,10 @@ void FeatureManager::AddCapabilities(Module* module) { for (Instruction& inst : module->capabilities()) { - AddCapability(static_cast<spv::Capability>(inst.GetSingleWordInOperand(0))); + const uint32_t i_cap = + inst.opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0; + AddCapability( + static_cast<spv::Capability>(inst.GetSingleWordInOperand(i_cap))); } } @@ -79,8 +86,14 @@ extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); extinst_importid_OpenCL100DebugInfo_ = module->GetExtInstImportId("OpenCL.DebugInfo.100"); - extinst_importid_Shader100DebugInfo_ = - module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100"); + // Match any version of NonSemantic.Shader.DebugInfo. + for (auto& ei : module->ext_inst_imports()) { + const std::string name = ei.GetInOperand(0).AsString(); + if (name.compare(0, 29, "NonSemantic.Shader.DebugInfo.") == 0) { + extinst_importid_ShaderDebugInfo_ = ei.result_id(); + break; + } + } } bool operator==(const FeatureManager& a, const FeatureManager& b) { @@ -108,8 +121,8 @@ return false; } - if (a.extinst_importid_Shader100DebugInfo_ != - b.extinst_importid_Shader100DebugInfo_) { + if (a.extinst_importid_ShaderDebugInfo_ != + b.extinst_importid_ShaderDebugInfo_) { return false; }
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index d150a2f..a0caac7 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h
@@ -47,8 +47,8 @@ return extinst_importid_OpenCL100DebugInfo_; } - uint32_t GetExtInstImportId_Shader100DebugInfo() const { - return extinst_importid_Shader100DebugInfo_; + uint32_t GetExtInstImportId_ShaderDebugInfo() const { + return extinst_importid_ShaderDebugInfo_; } friend bool operator==(const FeatureManager& a, const FeatureManager& b); @@ -100,9 +100,9 @@ // for performance. uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; - // Common NonSemanticShader100DebugInfo external instruction import ids, + // Common NonSemanticShaderDebugInfo external instruction import ids, // cached for performance. - uint32_t extinst_importid_Shader100DebugInfo_ = 0; + uint32_t extinst_importid_ShaderDebugInfo_ = 0; friend class IRContext; };
diff --git a/source/opt/fix_func_call_arguments.cpp b/source/opt/fix_func_call_arguments.cpp index f3486be..aa45c9f 100644 --- a/source/opt/fix_func_call_arguments.cpp +++ b/source/opt/fix_func_call_arguments.cpp
@@ -74,16 +74,19 @@ op_type->result_id(), spv::StorageClass::Function); // Create new variable builder.SetInsertPoint(variable_insertion_point); + // TODO(1841): Handle id overflow. Instruction* var = builder.AddVariable(varType, uint32_t(spv::StorageClass::Function)); // Load access chain to the new variable before function call builder.SetInsertPoint(func_call_inst); uint32_t operand_id = operand_inst->result_id(); + // TODO(1841): Handle id overflow. Instruction* load = builder.AddLoad(op_type->result_id(), operand_id); builder.AddStore(var->result_id(), load->result_id()); // Load return value to the acesschain after function call builder.SetInsertPoint(next_insert_point); + // TODO(1841): Handle id overflow. load = builder.AddLoad(op_type->result_id(), var->result_id()); builder.AddStore(operand_id, load->result_id());
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp index b64026e..608285e 100644 --- a/source/opt/fix_storage_class.cpp +++ b/source/opt/fix_storage_class.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2019 Google LLC +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -99,6 +101,7 @@ case spv::Op::OpCopyMemorySized: case spv::Op::OpVariable: case spv::Op::OpBitcast: + case spv::Op::OpAllocateNodePayloadsAMDX: // Nothing to change for these opcode. The result type is the same // regardless of the storage class of the operand. return false; @@ -319,6 +322,7 @@ switch (type_inst->opcode()) { case spv::Op::OpTypeArray: case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeNodePayloadArrayAMDX: case spv::Op::OpTypeMatrix: case spv::Op::OpTypeVector: case spv::Op::OpTypeCooperativeMatrixKHR:
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 942da68..64c947f 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp
@@ -50,7 +50,7 @@ if (s_operand == std::numeric_limits<int32_t>::min()) { return s_operand; } - return -s_operand; + return static_cast<uint32_t>(-s_operand); } case spv::Op::OpNot: return ~operand; @@ -597,6 +597,9 @@ const analysis::Constant* folded_const = nullptr; for (auto rule : GetConstantFoldingRules().GetRulesForInstruction(inst)) { folded_const = rule(context_, inst, constants); + if (folded_const == nullptr && inst->context()->id_overflow()) { + return nullptr; + } if (folded_const != nullptr) { Instruction* const_inst = const_mgr->GetDefiningInstruction(folded_const, inst->type_id());
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp index ddfe59f..edcf551 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp +++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -1,4 +1,5 @@ // Copyright (c) 2016 Google Inc. +// Copyright (c) 2025 Arm Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,21 +32,20 @@ // instructions, records their values in two internal maps: id_to_const_val_ // and const_val_to_id_ so that we can use them to infer the value of Spec // Constants later. - // For Spec Constants defined with OpSpecConstantComposite instructions, if - // all of their components are Normal Constants, they will be turned into - // Normal Constants too. For Spec Constants defined with OpSpecConstantOp - // instructions, we check if they only depends on Normal Constants and fold - // them when possible. The two maps for Normal Constants: id_to_const_val_ - // and const_val_to_id_ will be updated along the traversal so that the new - // Normal Constants generated from folding can be used to fold following Spec - // Constants. - // This algorithm depends on the SSA property of SPIR-V when - // defining constants. The dependent constants must be defined before the - // dependee constants. So a dependent Spec Constant must be defined and - // will be processed before its dependee Spec Constant. When we encounter - // the dependee Spec Constants, all its dependent constants must have been - // processed and all its dependent Spec Constants should have been folded if - // possible. + // For Spec Constants defined with OpSpecConstantComposite or + // OpSpecConstantCompositeReplicateEXT instructions, if all of their + // components are Normal Constants, they will be turned into Normal Constants + // too. For Spec Constants defined with OpSpecConstantOp instructions, we + // check if they only depends on Normal Constants and fold them when possible. + // The two maps for Normal Constants: id_to_const_val_ and const_val_to_id_ + // will be updated along the traversal so that the new Normal Constants + // generated from folding can be used to fold following Spec Constants. This + // algorithm depends on the SSA property of SPIR-V when defining constants. + // The dependent constants must be defined before the dependee constants. So a + // dependent Spec Constant must be defined and will be processed before its + // dependee Spec Constant. When we encounter the dependee Spec Constants, all + // its dependent constants must have been processed and all its dependent Spec + // Constants should have been folded if possible. Module::inst_iterator next_inst = context()->types_values_begin(); for (Module::inst_iterator inst_iter = next_inst; // Need to re-evaluate the end iterator since we may modify the list of @@ -54,8 +54,9 @@ ++next_inst; Instruction* inst = &*inst_iter; // Collect constant values of normal constants and process the - // OpSpecConstantOp and OpSpecConstantComposite instructions if possible. - // The constant values will be stored in analysis::Constant instances. + // OpSpecConstantOp, OpSpecConstantComposite, and + // OpSpecConstantCompositeReplicateEXT instructions if possible. The + // constant values will be stored in analysis::Constant instances. // OpConstantSampler instruction is not collected here because it cannot be // used in OpSpecConstant{Composite|Op} instructions. // TODO(qining): If the constant or its type has decoration, we may need @@ -70,21 +71,29 @@ case spv::Op::OpConstant: case spv::Op::OpConstantNull: case spv::Op::OpConstantComposite: - case spv::Op::OpSpecConstantComposite: { + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpSpecConstantCompositeReplicateEXT: { // A Constant instance will be created if the given instruction is a // Normal Constant whose value(s) are fixed. Note that for a composite - // Spec Constant defined with OpSpecConstantComposite instruction, if - // all of its components are Normal Constants already, the Spec - // Constant will be turned in to a Normal Constant. In that case, a - // Constant instance should also be created successfully and recorded - // in the id_to_const_val_ and const_val_to_id_ mapps. + // Spec Constant defined with OpSpecConstantComposite or + // OpSpecConstantCompositeReplicateEXT instruction, if all of its + // components are Normal Constants already, the Spec Constant will be + // turned in to a Normal Constant. In that case, a Constant instance + // should also be created successfully and recorded in the + // id_to_const_val_ and const_val_to_id_ mapps. if (auto const_value = const_mgr->GetConstantFromInst(inst)) { - // Need to replace the OpSpecConstantComposite instruction with a - // corresponding OpConstantComposite instruction. + // Need to replace the OpSpecConstantComposite or + // OpSpecConstantCompositeReplicateEXT instruction with a + // corresponding OpConstantComposite or + // OpConstantCompositeReplicateEXT instruction. if (opcode == spv::Op::OpSpecConstantComposite) { inst->SetOpcode(spv::Op::OpConstantComposite); modified = true; } + if (opcode == spv::Op::OpSpecConstantCompositeReplicateEXT) { + inst->SetOpcode(spv::Op::OpConstantCompositeReplicateEXT); + modified = true; + } const_mgr->MapConstantToInst(const_value, inst); } break; @@ -96,9 +105,16 @@ // Constants will be added to id_to_const_val_ and const_val_to_id_ so // that we can use the new Normal Constants when folding following Spec // Constants. - case spv::Op::OpSpecConstantOp: - modified |= ProcessOpSpecConstantOp(&inst_iter); + case spv::Op::OpSpecConstantOp: { + const auto status = ProcessOpSpecConstantOp(&inst_iter); + if (status == Status::Failure) { + return Status::Failure; + } + if (status == Status::SuccessWithChange) { + modified = true; + } break; + } default: break; } @@ -106,7 +122,7 @@ return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( +Pass::Status FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( Module::inst_iterator* pos) { Instruction* inst = &**pos; Instruction* folded_inst = nullptr; @@ -116,10 +132,17 @@ "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); folded_inst = FoldWithInstructionFolder(pos); + if (context()->id_overflow()) { + return Status::Failure; + } + if (!folded_inst) { folded_inst = DoComponentWiseOperation(pos); + if (context()->id_overflow()) { + return Status::Failure; + } } - if (!folded_inst) return false; + if (!folded_inst) return Status::SuccessWithoutChange; // Replace the original constant with the new folded constant, kill the // original constant. @@ -127,7 +150,7 @@ uint32_t old_id = inst->result_id(); context()->ReplaceAllUsesWith(old_id, new_id); context()->KillDef(old_id); - return true; + return Status::SuccessWithChange; } Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder( @@ -186,7 +209,11 @@ if (need_to_clone) { new_const_inst = new_const_inst->Clone(context()); - new_const_inst->SetResultId(TakeNextId()); + uint32_t new_id = TakeNextId(); + if (new_id == 0) { + return nullptr; + } + new_const_inst->SetResultId(new_id); new_const_inst->InsertAfter(insert_pos); get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst); }
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.h b/source/opt/fold_spec_constant_op_and_composite_pass.h index 9a8fb40..d48809c 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.h +++ b/source/opt/fold_spec_constant_op_and_composite_pass.h
@@ -19,6 +19,7 @@ #include <unordered_map> #include <vector> +#include "source/diagnostic.h" #include "source/opt/constants.h" #include "source/opt/def_use_manager.h" #include "source/opt/ir_context.h" @@ -45,14 +46,14 @@ private: // Processes the OpSpecConstantOp instruction pointed by the given // instruction iterator, folds it to normal constants if possible. Returns - // true if the spec constant is folded to normal constants. New instructions - // will be inserted before the OpSpecConstantOp instruction pointed by the - // instruction iterator. The instruction iterator, which is passed by - // pointer, will still point to the original OpSpecConstantOp instruction. If - // folding is done successfully, the original OpSpecConstantOp instruction - // will be changed to Nop and new folded instruction will be inserted before - // it. - bool ProcessOpSpecConstantOp(Module::inst_iterator* pos); + // kSuccess if the spec constant is folded to normal constants. New + // instructions will be inserted before the OpSpecConstantOp instruction + // pointed by the instruction iterator. The instruction iterator, which is + // passed by pointer, will still point to the original OpSpecConstantOp + // instruction. If folding is done successfully, the original OpSpecConstantOp + // instruction will be changed to Nop and new folded instruction will be + // inserted before it. Returns kFail if an id overflow occurs. + Status ProcessOpSpecConstantOp(Module::inst_iterator* pos); // Returns the result of folding the OpSpecConstantOp instruction // |inst_iter_ptr| using the instruction folder. @@ -62,7 +63,24 @@ // pointed by the given instruction iterator to a normal constant defining // instruction. Returns the pointer to the new constant defining instruction // if succeeded, otherwise return nullptr. + // instruction if succeeded, otherwise return nullptr. Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr); + + // Returns the next available id, or 0 if the id overflows. + uint32_t TakeNextId() { + uint32_t next_id = context()->TakeNextId(); + if (next_id == 0) { + Fail() << "ID overflow. Try running compact-ids."; + } + return next_id; + } + + // Records failure for the current module and returns a stream for printing + // diagnostics. + spvtools::DiagnosticStream Fail() { + return spvtools::DiagnosticStream({}, context()->consumer(), "", + SPV_ERROR_INTERNAL); + } }; } // namespace opt
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 5748f97..792b0f1 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp
@@ -16,6 +16,7 @@ #include <limits> #include <memory> +#include <optional> #include <utility> #include "ir_builder.h" @@ -77,7 +78,10 @@ // Returns the element width of |type|. uint32_t ElementWidth(const analysis::Type* type) { - if (const analysis::Vector* vec_type = type->AsVector()) { + if (const analysis::CooperativeVectorNV* coopvec_type = + type->AsCooperativeVectorNV()) { + return ElementWidth(coopvec_type->component_type()); + } else if (const analysis::Vector* vec_type = type->AsVector()) { return ElementWidth(vec_type->element_type()); } else if (const analysis::Float* float_type = type->AsFloat()) { return float_type->width(); @@ -112,12 +116,6 @@ } } -// Returns true if `type` is a cooperative matrix. -bool IsCooperativeMatrix(const analysis::Type* type) { - return type->kind() == analysis::Type::kCooperativeMatrixKHR || - type->kind() == analysis::Type::kCooperativeMatrixNV; -} - const analysis::Constant* ConstInput( const std::vector<const analysis::Constant*>& constants) { return constants[0] ? constants[0] : constants[1]; @@ -173,12 +171,57 @@ return GetWordsFromScalarIntConstant(int_constant); } else if (const auto* vec_constant = c->AsVectorConstant()) { std::vector<uint32_t> words; + // Retrieve all the components as 32bit words. for (const auto* comp : vec_constant->GetComponents()) { auto comp_in_words = GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp); words.insert(words.end(), comp_in_words.begin(), comp_in_words.end()); } - return words; + + if (ElementWidth(c->type()) >= 32) { + return words; + } + // Check the element width and concactenate if the width is less than 32. + if (ElementWidth(c->type()) == 8) { + assert(words.size() <= 4); + // Each 32-bit word will comprise 4 8-bit integers. + // reverse the order when compacting. + uint32_t compacted_word = 0; + for (int32_t i = static_cast<int32_t>(words.size()) - 1; i >= 0; --i) { + compacted_word <<= 8; + compacted_word |= (words[i] & 0xFF); + } + return {compacted_word}; + } else if (ElementWidth(c->type()) == 16) { + assert(words.size() <= 4); + std::vector<uint32_t> compacted_words; + // Each 32-bit word will comprise 2 16-bit integers. + // reverse the order pair-wise when compacting. + for (uint32_t i = 0; i < words.size(); i += 2) { + uint32_t word1 = words[i]; + uint32_t word2 = (i + 1 < words.size()) ? words[i + 1] : 0; + uint32_t compacted_word = (word2 << 16) | (word1 & 0xFFFF); + compacted_words.push_back(compacted_word); + } + return compacted_words; + } + assert(false && "Unhandled element width"); + } else if (c->AsNullConstant()) { + uint32_t num_elements = 1; + + if (const auto* vec_type = c->type()->AsVector()) { + num_elements = vec_type->element_count(); + } + + // We need to check the element width to determine how many 32-bit words are + // needed. + uint32_t element_width = ElementWidth(c->type()); + if (element_width < 32) { + num_elements = (num_elements + 1) / 2; + } else if (element_width == 64) { + num_elements = num_elements * 2; + } + return std::vector<uint32_t>(num_elements, 0); } return {}; } @@ -320,7 +363,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -406,7 +449,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -473,7 +516,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -631,6 +674,15 @@ case spv::Op::OpISub: FOLD_OP(-); break; + case spv::Op::OpBitwiseXor: + FOLD_OP(^); + break; + case spv::Op::OpBitwiseOr: + FOLD_OP(|); + break; + case spv::Op::OpBitwiseAnd: + FOLD_OP(&); + break; default: assert(false && "Unexpected operation"); break; @@ -709,7 +761,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -768,7 +820,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -846,7 +898,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -876,6 +928,46 @@ }; } +// Returns true if |inst| is negation op and is safe to fold. +static bool IsFoldableNegation(const Instruction* inst) { + return (inst->opcode() == spv::Op::OpSNegate || + (inst->opcode() == spv::Op::OpFNegate && + inst->IsFloatingPointFoldingAllowed())); +} + +// Merges multiplies / divisions of two negations. +// Cases: +// (-x) * (-y) = x * y +// (-x) / (-y) = x / y +FoldingRule MergeDivMulDoubleNegative() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == spv::Op::OpFMul || + inst->opcode() == spv::Op::OpVectorTimesScalar || + inst->opcode() == spv::Op::OpFDiv || + inst->opcode() == spv::Op::OpIMul || + inst->opcode() == spv::Op::OpSDiv); + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + + if (IsFoldableNegation(lhs) && IsFoldableNegation(rhs)) { + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {lhs->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {rhs->GetSingleWordInOperand(0u)}}}); + return true; + } + return false; + }; +} + // Merges consecutive divides if each instruction contains one constant operand. // Does not support integer division. // Cases: @@ -891,7 +983,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -969,7 +1061,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -1068,13 +1160,12 @@ }; } -// Folds addition of a constant and a negation. -// Cases: -// (-x) + 2 = 2 - x -// 2 + (-x) = 2 - x +// Folds addition, where one side is a negation. +// (-x) + y = y - x +// y + (-x) = y - x FoldingRule MergeAddNegateArithmetic() { return [](IRContext* context, Instruction* inst, - const std::vector<const analysis::Constant*>& constants) { + const std::vector<const analysis::Constant*>&) { assert(inst->opcode() == spv::Op::OpFAdd || inst->opcode() == spv::Op::OpIAdd); const analysis::Type* type = @@ -1082,73 +1173,65 @@ bool uses_float = HasFloatingPoint(type); if (uses_float && !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; + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); - if (other_inst->opcode() == spv::Op::OpSNegate || - other_inst->opcode() == spv::Op::OpFNegate) { - inst->SetOpcode(HasFloatingPoint(type) ? spv::Op::OpFSub - : spv::Op::OpISub); - uint32_t const_id = constants[0] ? inst->GetSingleWordInOperand(0u) - : inst->GetSingleWordInOperand(1u); - inst->SetInOperands( - {{SPV_OPERAND_TYPE_ID, {const_id}}, - {SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}}); - return true; - } - return false; + auto TrySubstitute = [inst, uses_float](Instruction* first, + Instruction* second) { + if (IsFoldableNegation(first)) { + inst->SetOpcode(uses_float ? spv::Op::OpFSub : spv::Op::OpISub); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {second->result_id()}}, + {SPV_OPERAND_TYPE_ID, {first->GetSingleWordInOperand(0u)}}}); + return true; + } + return false; + }; + + return TrySubstitute(lhs, rhs) || TrySubstitute(rhs, lhs); }; } -// Folds subtraction of a constant and a negation. +// Folds subtraction, where one side is a negation. // Cases: // (-x) - 2 = -2 - x -// 2 - (-x) = x + 2 +// y - (-x) = x + y FoldingRule MergeSubNegateArithmetic() { return [](IRContext* context, Instruction* inst, const std::vector<const analysis::Constant*>& constants) { assert(inst->opcode() == spv::Op::OpFSub || inst->opcode() == spv::Op::OpISub); - analysis::ConstantManager* const_mgr = context->get_constant_mgr(); const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { - return false; - } - bool uses_float = HasFloatingPoint(type); if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + + if (IsFoldableNegation(rhs)) { + inst->SetOpcode(uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {lhs->result_id()}}, + {SPV_OPERAND_TYPE_ID, {rhs->GetSingleWordInOperand(0)}}}); + return true; + } + + if (type->IsCooperativeMatrix()) { + return false; + } + uint32_t width = ElementWidth(type); if (width != 32 && width != 64) 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->opcode() == spv::Op::OpSNegate || - other_inst->opcode() == spv::Op::OpFNegate) { - uint32_t op1 = 0; - uint32_t op2 = 0; - spv::Op opcode = inst->opcode(); - if (constants[0] != nullptr) { - op1 = other_inst->GetSingleWordInOperand(0u); - op2 = inst->GetSingleWordInOperand(0u); - opcode = HasFloatingPoint(type) ? spv::Op::OpFAdd : spv::Op::OpIAdd; - } else { - op1 = NegateConstant(const_mgr, const_input1); - op2 = other_inst->GetSingleWordInOperand(0u); - } - - inst->SetOpcode(opcode); + if (constants[1] && IsFoldableNegation(lhs)) { inst->SetInOperands( - {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + {{SPV_OPERAND_TYPE_ID, + {NegateConstant(context->get_constant_mgr(), constants[1])}}, + {SPV_OPERAND_TYPE_ID, {lhs->GetSingleWordInOperand(0)}}}); return true; } return false; @@ -1169,7 +1252,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -1222,7 +1305,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -1287,7 +1370,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -1358,7 +1441,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -1456,7 +1539,7 @@ const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); - if (IsCooperativeMatrix(type)) { + if (type->IsCooperativeMatrix()) { return false; } @@ -1473,11 +1556,13 @@ }; } -// Helper function for FactorAddMuls. If |factor0_0| is the same as |factor1_0|, -// generate |factor0_0| * (|factor0_1| + |factor1_1|). -bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1, - uint32_t factor1_0, uint32_t factor1_1, - Instruction* inst) { +// Helper function for FactorAddSubMuls. +// If |factor0_0| is the same as |factor1_0|, generate: +// |factor0_0| * (|factor0_1| + |factor1_1|) +// |factor0_0| * (|factor0_1| - |factor1_1|) +bool FactorAddSubMulsOpnds(uint32_t factor0_0, uint32_t factor0_1, + uint32_t factor1_0, uint32_t factor1_1, + Instruction* inst) { IRContext* context = inst->context(); if (factor0_0 != factor1_0) return false; InstructionBuilder ir_builder( @@ -1485,8 +1570,13 @@ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* new_add_inst = ir_builder.AddBinaryOp( inst->type_id(), inst->opcode(), factor0_1, factor1_1); - inst->SetOpcode(inst->opcode() == spv::Op::OpFAdd ? spv::Op::OpFMul - : spv::Op::OpIMul); + if (!new_add_inst) { + return false; + } + + bool is_float = + inst->opcode() == spv::Op::OpFAdd || inst->opcode() == spv::Op::OpFSub; + inst->SetOpcode(is_float ? spv::Op::OpFMul : spv::Op::OpIMul); inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}}, {SPV_OPERAND_TYPE_ID, {new_add_inst->result_id()}}}); context->UpdateDefUse(inst); @@ -1494,12 +1584,16 @@ } // Perform the following factoring identity, handling all operand order -// combinations: (a * b) + (a * c) = a * (b + c) -FoldingRule FactorAddMuls() { +// combinations: +// (a * b) + (a * c) = a * (b + c) +// (a * b) - (a * c) = a * (b - c) +FoldingRule FactorAddSubMuls() { return [](IRContext* context, Instruction* inst, const std::vector<const analysis::Constant*>&) { assert(inst->opcode() == spv::Op::OpFAdd || - inst->opcode() == spv::Op::OpIAdd); + inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpIAdd || + inst->opcode() == spv::Op::OpISub); const analysis::Type* type = context->get_type_mgr()->GetType(inst->type_id()); bool uses_float = HasFloatingPoint(type); @@ -1530,11 +1624,11 @@ for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { // Check if operand i in add_op0_inst matches operand j in add_op1_inst. - if (FactorAddMulsOpnds(add_op0_inst->GetSingleWordInOperand(i), - add_op0_inst->GetSingleWordInOperand(1 - i), - add_op1_inst->GetSingleWordInOperand(j), - add_op1_inst->GetSingleWordInOperand(1 - j), - inst)) + if (FactorAddSubMulsOpnds(add_op0_inst->GetSingleWordInOperand(i), + add_op0_inst->GetSingleWordInOperand(1 - i), + add_op1_inst->GetSingleWordInOperand(j), + add_op1_inst->GetSingleWordInOperand(1 - j), + inst)) return true; } } @@ -1542,6 +1636,393 @@ }; } +// Reassociate integer instructions where both operands share the same opcode +// and both source instructions contain a constant. +// e.g: +// (a * C0) * (C1 * b) = (C0 * C1) * (a * b) +// (a ^ C0) ^ (b ^ C1) = (C0 ^ C1) ^ (a ^ b) +// (C0 | a) | (b | C1) = (C0 | C1) | (a | b) +// (a & C0) & (b & C1) = (C0 & C1) & (a & b) +static const constexpr spv::Op ReassociateNestedGenericIntOps[] = { + spv::Op::OpIMul, spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, + spv::Op::OpBitwiseAnd}; + +FoldingRule ReassociateNestedGenericInt(spv::Op opcode) { + assert(std::find(std::begin(ReassociateNestedGenericIntOps), + std::end(ReassociateNestedGenericIntOps), + opcode) != std::end(ReassociateNestedGenericIntOps) && + "Wrong opcode."); + + return [opcode](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + // Handled by other folding rules. + if (constants[0] || constants[1]) { + return false; + } + + if (inst->opcode() != opcode) { + return false; + } + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (type->IsCooperativeMatrix()) { + return false; + } + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + + if (lhs->opcode() != opcode || rhs->opcode() != opcode) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector<const analysis::Constant*> lhs_constants = + const_mgr->GetOperandConstants(lhs); + const analysis::Constant* lhs_const = ConstInput(lhs_constants); + if (!lhs_const) { + return false; + } + + std::vector<const analysis::Constant*> rhs_constants = + const_mgr->GetOperandConstants(rhs); + const analysis::Constant* rhs_const = ConstInput(rhs_constants); + if (!rhs_const) { + return false; + } + + uint32_t merged_constant = + PerformOperation(const_mgr, opcode, lhs_const, rhs_const); + if (!merged_constant) { + return false; + } + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* new_rhs = ir_builder.AddBinaryOp( + inst->type_id(), opcode, + NonConstInput(context, lhs_constants[0], lhs)->result_id(), + NonConstInput(context, rhs_constants[0], rhs)->result_id()); + + if (!new_rhs) { + return false; + } + + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_constant}}, + {SPV_OPERAND_TYPE_ID, {new_rhs->result_id()}}}); + return true; + }; +} + +// Reassociate floating point mul/div instructions, which have mul/div inputs, +// both of which contain a constant. +// e.g: +// (a * C0) / (C1 / b) = (C0 / C1) * (a * b) +// (C0 / a) * (b / C1) = (C0 / C1) * (b / a) +// (a / C0) / (b * C1) = (1 / (C0 * C1)) * (a / b) +FoldingRule ReassociateNestedMulDivFloat() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpFMul || + inst->opcode() == spv::Op::OpFDiv); + + // Handled by other folding rules. + if (constants[0] || constants[1]) { + return false; + } + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (type->IsCooperativeMatrix()) { + return false; + } + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + + bool lhs_is_mul = lhs->opcode() == spv::Op::OpFMul; + bool lhs_is_div = lhs->opcode() == spv::Op::OpFDiv; + bool rhs_is_mul = rhs->opcode() == spv::Op::OpFMul; + bool rhs_is_div = rhs->opcode() == spv::Op::OpFDiv; + if (!(lhs_is_mul || lhs_is_div) || !(rhs_is_mul || rhs_is_div)) { + return false; + } + + if (!lhs->IsFloatingPointFoldingAllowed() || + !rhs->IsFloatingPointFoldingAllowed()) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector<const analysis::Constant*> lhs_constants = + const_mgr->GetOperandConstants(lhs); + if (!lhs_constants[0] && !lhs_constants[1]) { + return false; + } + + std::vector<const analysis::Constant*> rhs_constants = + const_mgr->GetOperandConstants(rhs); + if (!rhs_constants[0] && !rhs_constants[1]) { + return false; + } + + const analysis::Constant* lhs_const = + lhs_constants[0] ? lhs_constants[0] : lhs_constants[1]; + const analysis::Constant* rhs_const = + rhs_constants[0] ? rhs_constants[0] : rhs_constants[1]; + if (!lhs_const || !rhs_const) return false; + + bool const_lhs_rcp = lhs_constants[0] ? false : lhs_is_div; + bool const_rhs_rcp = rhs_constants[0] ? false : rhs_is_div; + + uint32_t non_const_lhs = lhs_constants[0] ? lhs->GetSingleWordInOperand(1) + : lhs->GetSingleWordInOperand(0); + bool non_const_lhs_rcp = lhs_constants[0] ? lhs_is_div : false; + + uint32_t non_const_rhs = rhs_constants[0] ? rhs->GetSingleWordInOperand(1) + : rhs->GetSingleWordInOperand(0); + bool non_const_rhs_rcp = rhs_constants[0] ? rhs_is_div : false; + + // Rcp the rhs if we're actually dividing it. + if (inst->opcode() == spv::Op::OpFDiv) { + const_rhs_rcp = !const_rhs_rcp; + non_const_rhs_rcp = !non_const_rhs_rcp; + } + + if (const_lhs_rcp) { + lhs_const = + const_mgr->FindDeclaredConstant(Reciprocal(const_mgr, lhs_const)); + if (!lhs_const) { + return false; + } + } + if (const_rhs_rcp) { + rhs_const = + const_mgr->FindDeclaredConstant(Reciprocal(const_mgr, rhs_const)); + if (!rhs_const) { + return false; + } + } + + uint32_t merged_constant = + PerformOperation(const_mgr, spv::Op::OpFMul, lhs_const, rhs_const); + + if (!merged_constant) { + return false; + } + + spv::Op op = spv::Op::OpNop; + Instruction* new_rhs = nullptr; + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + // a * b => C * (b * a) + if (!non_const_lhs_rcp && !non_const_rhs_rcp) { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFMul, + non_const_lhs, non_const_rhs); + op = spv::Op::OpFMul; + } + // 1/a * b => C * (b / a) + else if (non_const_lhs_rcp && !non_const_rhs_rcp) { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFDiv, + non_const_rhs, non_const_lhs); + op = spv::Op::OpFMul; + } + // a * 1/b => C * (a / b) + else if (!non_const_lhs_rcp && non_const_rhs_rcp) { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFDiv, + non_const_lhs, non_const_rhs); + op = spv::Op::OpFMul; + } + // 1/a * 1/b => C / (a * b) + else { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFMul, + non_const_lhs, non_const_rhs); + op = spv::Op::OpFDiv; + } + + if (!new_rhs) { + return false; + } + + inst->SetOpcode(op); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_constant}}, + {SPV_OPERAND_TYPE_ID, {new_rhs->result_id()}}}); + return true; + }; +} + +// Reassociate add/sub instructions, which have add/sub inputs, +// both of which contain a constant. +// e.g: +// (a + C0) - (C1 - b) = (C0 - C1) + (a + b) +// (C0 - a) + (b - C1) = (C0 - C1) + (b - a) +// (a - C0) - (b + C1) = (-C0 - C1) + (a - b) +FoldingRule ReassociateNestedAddSub() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpFAdd || + inst->opcode() == spv::Op::OpIAdd || + inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpISub); + + // Handled by other folding rules. + if (constants[0] || constants[1]) { + return false; + } + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (type->IsCooperativeMatrix()) { + return false; + } + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + + spv::Op add_op = uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd; + spv::Op sub_op = uses_float ? spv::Op::OpFSub : spv::Op::OpISub; + + bool lhs_is_add = lhs->opcode() == add_op; + bool lhs_is_sub = lhs->opcode() == sub_op; + bool rhs_is_add = rhs->opcode() == add_op; + bool rhs_is_sub = rhs->opcode() == sub_op; + if (!(lhs_is_add || lhs_is_sub) || !(rhs_is_add || rhs_is_sub)) { + return false; + } + + if (uses_float && (!lhs->IsFloatingPointFoldingAllowed() || + !rhs->IsFloatingPointFoldingAllowed())) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector<const analysis::Constant*> lhs_constants = + const_mgr->GetOperandConstants(lhs); + if (!lhs_constants[0] && !lhs_constants[1]) { + return false; + } + + std::vector<const analysis::Constant*> rhs_constants = + const_mgr->GetOperandConstants(rhs); + if (!rhs_constants[0] && !rhs_constants[1]) { + return false; + } + + const analysis::Constant* lhs_const = + lhs_constants[0] ? lhs_constants[0] : lhs_constants[1]; + const analysis::Constant* rhs_const = + rhs_constants[0] ? rhs_constants[0] : rhs_constants[1]; + if (!lhs_const || !rhs_const) return false; + + bool const_lhs_neg = lhs_constants[0] ? false : lhs_is_sub; + bool const_rhs_neg = rhs_constants[0] ? false : rhs_is_sub; + + uint32_t non_const_lhs = lhs_constants[0] ? lhs->GetSingleWordInOperand(1) + : lhs->GetSingleWordInOperand(0); + bool non_const_lhs_neg = lhs_constants[0] ? lhs_is_sub : false; + + uint32_t non_const_rhs = rhs_constants[0] ? rhs->GetSingleWordInOperand(1) + : rhs->GetSingleWordInOperand(0); + bool non_const_rhs_neg = rhs_constants[0] ? rhs_is_sub : false; + + // Negate the rhs if we're actually subtracting it. + if (inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpISub) { + const_rhs_neg = !const_rhs_neg; + non_const_rhs_neg = !non_const_rhs_neg; + } + + if (const_lhs_neg) { + lhs_const = + const_mgr->FindDeclaredConstant(NegateConstant(const_mgr, lhs_const)); + if (!lhs_const) { + return false; + } + } + if (const_rhs_neg) { + rhs_const = + const_mgr->FindDeclaredConstant(NegateConstant(const_mgr, rhs_const)); + if (!rhs_const) { + return false; + } + } + + uint32_t merged_constant = + PerformOperation(const_mgr, add_op, lhs_const, rhs_const); + + if (!merged_constant) { + return false; + } + + spv::Op op = spv::Op::OpNop; + Instruction* new_rhs = nullptr; + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + // a + b => C + (b + a) + if (!non_const_lhs_neg && !non_const_rhs_neg) { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), add_op, non_const_lhs, + non_const_rhs); + op = add_op; + } + // -a + b => C + (b - a) + else if (non_const_lhs_neg && !non_const_rhs_neg) { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), sub_op, non_const_rhs, + non_const_lhs); + op = add_op; + } + // a + -b => C + (a - b) + else if (!non_const_lhs_neg && non_const_rhs_neg) { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), sub_op, non_const_lhs, + non_const_rhs); + op = add_op; + } + // -a + -b => C - (a + b) + else { + new_rhs = ir_builder.AddBinaryOp(inst->type_id(), add_op, non_const_lhs, + non_const_rhs); + op = sub_op; + } + + if (!new_rhs) { + return false; + } + + inst->SetOpcode(op); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_constant}}, + {SPV_OPERAND_TYPE_ID, {new_rhs->result_id()}}}); + return true; + }; +} + FoldingRule IntMultipleBy1() { return [](IRContext*, Instruction* inst, const std::vector<const analysis::Constant*>& constants) { @@ -1712,6 +2193,174 @@ return type_id; } +// If the input to an OpCompositeExtract is an OpCopyLogical, then we can +// hoist the extraction before the copy. +bool CopyLogicalFeedingExtract(IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != spv::Op::OpCopyLogical) { + return false; + } + + uint32_t original_composite_id = cinst->GetSingleWordInOperand(0); + Instruction* original_composite_inst = + def_use_mgr->GetDef(original_composite_id); + + std::vector<uint32_t> indices; + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + indices.push_back(inst->GetSingleWordInOperand(i)); + } + + uint32_t original_element_type_id = + GetElementType(original_composite_inst->type_id(), inst->begin() + 3, + inst->end(), def_use_mgr); + assert(original_element_type_id != 0 && + "Could not find the element type. Invalid SPIR-V."); + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* new_extract = ir_builder.AddCompositeExtract( + original_element_type_id, original_composite_id, indices); + + if (original_element_type_id == inst->type_id()) + inst->SetOpcode(spv::Op::OpCopyObject); + else + inst->SetOpcode(spv::Op::OpCopyLogical); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_extract->result_id()}}}); + return true; +} + +// If the input to an OpCompositeExtract is an OpLoad, we can change the +// load into a load of an OpAccessChain. +bool LoadFeedingExtract(IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != spv::Op::OpLoad) { + return false; + } + + Instruction* composite_type_inst = def_use_mgr->GetDef(cinst->type_id()); + if (composite_type_inst->opcode() != spv::Op::OpTypeStruct && + composite_type_inst->opcode() != spv::Op::OpTypeArray) { + return false; + } + + // Check the memory operands. + if (cinst->NumInOperands() > 1) { + uint32_t memory_access_mask = cinst->GetSingleWordInOperand(1); + if (memory_access_mask & uint32_t(spv::MemoryAccessMask::Volatile)) { + return false; + } + } + + uint32_t ptr_id = cinst->GetSingleWordInOperand(0); + Instruction* ptr_inst = def_use_mgr->GetDef(ptr_id); + Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_inst->type_id()); + assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer); + spv::StorageClass storage_class = + static_cast<spv::StorageClass>(ptr_type_inst->GetSingleWordInOperand(0)); + + // If the storage class is Function or Private, we do not want to fold. + // These are the storage classes that the local-access-chain-convert pass + // works on. + if (storage_class == spv::StorageClass::Function || + storage_class == spv::StorageClass::Private) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + std::vector<uint32_t> index_ids; + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + uint32_t index = inst->GetSingleWordInOperand(i); + const analysis::Constant* index_const = + const_mgr->GetConstant(type_mgr->GetUIntType(), {index}); + index_ids.push_back( + const_mgr->GetDefiningInstruction(index_const)->result_id()); + } + + InstructionBuilder ir_builder( + context, cinst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t element_ptr_type_id = + type_mgr->FindPointerToType(inst->type_id(), storage_class); + if (element_ptr_type_id == 0) { + return false; + } + + Instruction* access_chain = + ir_builder.AddAccessChain(element_ptr_type_id, ptr_id, index_ids); + std::vector<Operand> load_operands; + load_operands.push_back({SPV_OPERAND_TYPE_ID, {access_chain->result_id()}}); + + if (cinst->NumInOperands() > 1) { + uint32_t memory_access_mask = cinst->GetSingleWordInOperand(1); + load_operands.push_back( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {memory_access_mask}}); + + uint32_t current_operand_index = 2; + if (memory_access_mask & uint32_t(spv::MemoryAccessMask::Aligned)) { + uint32_t original_alignment = + cinst->GetSingleWordInOperand(current_operand_index); + + std::vector<uint32_t> extract_indices; + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + extract_indices.push_back(inst->GetSingleWordInOperand(i)); + } + + std::optional<uint32_t> offset = + type_mgr->GetType(cinst->type_id())->GetByteOffset(extract_indices); + if (!offset) { + return false; + } + + uint32_t new_alignment = original_alignment; + if (*offset != 0) { + uint32_t offset_alignment = *offset & ~(*offset - 1); + new_alignment = std::min(original_alignment, offset_alignment); + } + + load_operands.push_back( + {SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {new_alignment}}); + current_operand_index++; + } + + // Copy the remaining operands + for (; current_operand_index < cinst->NumInOperands(); + ++current_operand_index) { + load_operands.push_back(cinst->GetInOperand(current_operand_index)); + } + } + + uint32_t load_result_id = context->TakeNextId(); + if (load_result_id == 0) return false; + + std::unique_ptr<Instruction> new_load_inst( + new Instruction(context, spv::Op::OpLoad, inst->type_id(), load_result_id, + load_operands)); + Instruction* new_load = ir_builder.AddInstruction(std::move(new_load_inst)); + + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_load->result_id()}}}); + + return true; +} + // Returns true of |inst_1| and |inst_2| have the same indexes that will be used // to index into a composite object, excluding the last index. The two // instructions must have the same opcode, and be either OpCompositeExtract or @@ -1995,6 +2644,15 @@ bool use_x = false; assert(a_const->type()->AsFloat()); + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) { + // We won't support folding half float values. + return false; + } + double element_value = a_const->GetValueAsDouble(); if (element_value == 0.0) { use_x = true; @@ -2227,6 +2885,81 @@ }; } +// Remove indirect bitcasts which have no effect. +// uint32 x; asuint32(x) => x +// uint32 x; asuint32(asint32(x)) => x +// float32 x; asuint32(asint32(x)) => asuint32(x) +FoldingRule RedundantBitcast() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == spv::Op::OpBitcast); + + analysis::DefUseManager* def_mgr = context->get_def_use_mgr(); + Instruction* child = def_mgr->GetDef(inst->GetSingleWordInOperand(0)); + + if (inst->type_id() == child->type_id()) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {child->result_id()}}}); + return true; + } + + if (child->opcode() != spv::Op::OpBitcast) { + return false; + } + + if (def_mgr->GetDef(child->GetSingleWordInOperand(0))->type_id() == + inst->type_id()) { + inst->SetOpcode(spv::Op::OpCopyObject); + } + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(0)}}}); + + return true; + }; +} + +FoldingRule BitReverseScalarOrVector() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpBitReverse && constants.size() == 1); + if (constants[0] == nullptr) return false; + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + assert(!HasFloatingPoint(type) && + "BitReverse cannot be applied to floating point types."); + assert((type->AsInteger() || type->AsVector()) && + "BitReverse can only be applied to integer scalars or vectors."); + assert((ElementWidth(type) == 32) && + "BitReverse can only be applied to integer types of width 32"); + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector<uint32_t> words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]); + if (words.size() == 0) return false; + + for (uint32_t& word : words) { + // Reverse the bits in each word. + word = ((word & 0x55555555) << 1) | ((word >> 1) & 0x55555555); + word = ((word & 0x33333333) << 2) | ((word >> 2) & 0x33333333); + word = ((word & 0x0F0F0F0F) << 4) | ((word >> 4) & 0x0F0F0F0F); + word = ((word & 0x00FF00FF) << 8) | ((word >> 8) & 0x00FF00FF); + word = (word << 16) | (word >> 16); + } + + const analysis::Constant* bitreversed_constant = + ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type); + if (!bitreversed_constant) return false; + + auto new_feeder_id = + const_mgr->GetDefiningInstruction(bitreversed_constant, inst->type_id()) + ->result_id(); + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}}); + return true; + }; +} + FoldingRule RedundantSelect() { // An OpSelect instruction where both values are the same or the condition is // constant can be replaced by one of the values @@ -2299,6 +3032,421 @@ }; } +std::optional<bool> GetBoolConstantKind(const analysis::Constant* c) { + if (!c) { + return {}; + } + if (auto composite = c->AsCompositeConstant()) { + auto& components = composite->GetComponents(); + if (components.empty()) { + return {}; + } + auto first = GetBoolConstantKind(components[0]); + if (!first) { + return {}; + } + if (std::all_of(std::begin(components) + 1, std::end(components), + [first](const analysis::Constant* c2) { + return GetBoolConstantKind(c2) == first; + })) { + return first; + } + return {}; + } else if (c->AsNullConstant()) { + return false; + } else if (c->AsBoolConstant()) { + return c->AsBoolConstant()->value(); + } + return {}; +} + +// Fold OpSelect instructions which have constant booleans as their result. +// x ? true : false = x +// x ? false : true = !x +FoldingRule FoldConstantBooleanSelect() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpSelect); + assert(inst->NumInOperands() == 3); + assert(constants.size() == 3); + + if (!constants[1] || !constants[2]) { + return false; + } + + analysis::DefUseManager* def_mgr = context->get_def_use_mgr(); + if (inst->type_id() != + def_mgr->GetDef(inst->GetSingleWordInOperand(0))->type_id()) { + return false; + } + + std::optional<bool> uniform_true = GetBoolConstantKind(constants[1]); + std::optional<bool> uniform_false = GetBoolConstantKind(constants[2]); + + if (!uniform_true || !uniform_false) { + return false; + } + + if (uniform_true.value() && !uniform_false.value()) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } else if (!uniform_true.value() && uniform_false.value()) { + inst->SetOpcode(spv::Op::OpLogicalNot); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + return false; + }; +} + +// Fold OpLogicalAnd instructions which have a constant true on one side. +// x && true = x +// true && x = x +FoldingRule RedundantLogicalAnd() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpLogicalAnd); + + if (GetBoolConstantKind(ConstInput(constants)) == + std::optional<bool>(true)) { + Instruction* other_inst = NonConstInput(context, constants[0], inst); + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {other_inst->result_id()}}}); + return true; + } + return false; + }; +} + +// Fold OpLogicalOr instructions which have a constant false on one side. +// x || false = x +// false || x = x +FoldingRule RedundantLogicalOr() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpLogicalOr); + + if (GetBoolConstantKind(ConstInput(constants)) == + std::optional<bool>(false)) { + Instruction* other_inst = NonConstInput(context, constants[0], inst); + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {other_inst->result_id()}}}); + return true; + } + return false; + }; +} + +// Fold concurrent OpLogicalNot instructions: +// !!x = x +FoldingRule RedundantLogicalNot() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == spv::Op::OpLogicalNot); + Instruction* child = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0)); + if (child->opcode() == spv::Op::OpLogicalNot) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(0)}}}); + return true; + } + return false; + }; +} + +// Cases handled: +// ((a ? C0 : C1) == C2) = ((a ? (C0 == C2) : (C1 == C2)) +// ((a ? C0 : C1) != C2) = ((a ? (C0 != C2) : (C1 != C2)) +// ((a ? C0 : C1) < C2) = ((a ? (C0 < C2) : (C1 < C2)) +// ((a ? C0 : C1) <= C2) = ((a ? (C0 <= C2) : (C1 <= C2)) +// ((a ? C0 : C1) > C2) = ((a ? (C0 > C2) : (C1 > C2)) +// ((a ? C0 : C1) >= C2) = ((a ? (C0 >= C2) : (C1 >= C2)) +// ((a ? C0 : C1) || C2) = ((a ? (C0 || C2) : (C1 || C2)) +// ((a ? C0 : C1) && C2) = ((a ? (C0 && C2) : (C1 && C2)) +// ((a ? C0 : C1) + C2) = ((a ? (C0 + C2) : (C1 + C2)) +// ((a ? C0 : C1) - C2) = ((a ? (C0 - C2) : (C1 - C2)) +// ((a ? C0 : C1) * C2) = ((a ? (C0 * C2) : (C1 * C2)) +// ((a ? C0 : C1) / C2) = ((a ? (C0 / C2) : (C1 / C2)) +// ((a ? C0 : C1) >> C2) = ((a ? (C0 >> C2) : (C1 >> C2)) +// ((a ? C0 : C1) << C2) = ((a ? (C0 << C2) : (C1 << C2)) +// ((a ? C0 : C1) ^ C2) = ((a ? (C0 ^ C2) : (C1 ^ C2)) +// ((a ? C0 : C1) | C2) = ((a ? (C0 | C2) : (C1 | C2)) +// ((a ? C0 : C1) & C2) = ((a ? (C0 & C2) : (C1 & C2)) +static const constexpr spv::Op MergeBinaryOpSelectOps[] = { + spv::Op::OpLogicalEqual, + spv::Op::OpLogicalNotEqual, + spv::Op::OpLogicalAnd, + spv::Op::OpLogicalOr, + spv::Op::OpIEqual, + spv::Op::OpINotEqual, + spv::Op::OpUGreaterThan, + spv::Op::OpSGreaterThan, + spv::Op::OpUGreaterThanEqual, + spv::Op::OpSGreaterThanEqual, + spv::Op::OpULessThan, + spv::Op::OpSLessThan, + spv::Op::OpULessThanEqual, + spv::Op::OpSLessThanEqual, + spv::Op::OpFOrdEqual, + spv::Op::OpFUnordEqual, + spv::Op::OpFOrdNotEqual, + spv::Op::OpFUnordNotEqual, + spv::Op::OpFOrdLessThan, + spv::Op::OpFUnordLessThan, + spv::Op::OpFOrdGreaterThan, + spv::Op::OpFUnordGreaterThan, + spv::Op::OpFOrdLessThanEqual, + spv::Op::OpFUnordLessThanEqual, + spv::Op::OpFOrdGreaterThanEqual, + spv::Op::OpFUnordGreaterThanEqual, + spv::Op::OpIAdd, + spv::Op::OpFAdd, + spv::Op::OpISub, + spv::Op::OpFSub, + spv::Op::OpIMul, + spv::Op::OpFMul, + spv::Op::OpUDiv, + spv::Op::OpSDiv, + spv::Op::OpFDiv, + spv::Op::OpVectorTimesScalar, + spv::Op::OpShiftRightLogical, + spv::Op::OpShiftRightArithmetic, + spv::Op::OpShiftLeftLogical, + spv::Op::OpBitwiseXor, + spv::Op::OpBitwiseOr, + spv::Op::OpBitwiseAnd}; + +FoldingRule MergeBinaryOpSelect(spv::Op opcode) { + assert(std::find(std::begin(MergeBinaryOpSelectOps), + std::end(MergeBinaryOpSelectOps), + opcode) != std::end(MergeBinaryOpSelectOps) && + "Wrong opcode."); + + return [opcode](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + const analysis::Constant* const_input = ConstInput(constants); + if (!const_input) { + return false; + } + Instruction* non_const = NonConstInput(context, constants[0], inst); + if (non_const->opcode() != spv::Op::OpSelect) { + return false; + } + std::vector<const analysis::Constant*> select_constants = + context->get_constant_mgr()->GetOperandConstants(non_const); + if (!select_constants[1] || !select_constants[2]) { + return false; + } + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction *lhs, *rhs; + if (constants[0]) { + lhs = ir_builder.AddBinaryOp(inst->type_id(), opcode, + inst->GetSingleWordInOperand(0), + non_const->GetSingleWordInOperand(1)); + rhs = ir_builder.AddBinaryOp(inst->type_id(), opcode, + inst->GetSingleWordInOperand(0), + non_const->GetSingleWordInOperand(2)); + } else { + lhs = ir_builder.AddBinaryOp(inst->type_id(), opcode, + non_const->GetSingleWordInOperand(1), + inst->GetSingleWordInOperand(1)); + rhs = ir_builder.AddBinaryOp(inst->type_id(), opcode, + non_const->GetSingleWordInOperand(2), + inst->GetSingleWordInOperand(1)); + } + + if (!lhs || !rhs) { + return false; + } + + if (context->get_instruction_folder().FoldInstruction(lhs)) { + context->AnalyzeDefUse(lhs); + while (lhs->opcode() == spv::Op::OpCopyObject) { + lhs = + context->get_def_use_mgr()->GetDef(lhs->GetSingleWordInOperand(0)); + } + } + if (context->get_instruction_folder().FoldInstruction(rhs)) { + context->AnalyzeDefUse(rhs); + while (rhs->opcode() == spv::Op::OpCopyObject) { + rhs = + context->get_def_use_mgr()->GetDef(rhs->GetSingleWordInOperand(0)); + } + } + inst->SetOpcode(spv::Op::OpSelect); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {non_const->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {lhs->result_id()}}, + {SPV_OPERAND_TYPE_ID, {rhs->result_id()}}}); + return true; + }; +} + +// Fold OpLogicalNot instructions that follow a comparison, +// if the comparison is only used by that instruction. +// +// !(a == b) = (a != b) +// !(a != b) = (a == b) +// !(a < b) = (a >= b) +// !(a >= b) = (a < b) +// !(a > b) = (a <= b) +// !(a <= b) = (a > b) +FoldingRule FoldLogicalNotComparison() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + assert(inst->opcode() == spv::Op::OpLogicalNot); + analysis::DefUseManager* def_mgr = context->get_def_use_mgr(); + Instruction* child = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0)); + + if (def_mgr->NumUses(child) > 1) { + return false; + } + + spv::Op new_opcode = spv::Op::OpNop; + switch (child->opcode()) { + // (a == b) <=> (a != b) + case spv::Op::OpIEqual: + new_opcode = spv::Op::OpINotEqual; + break; + case spv::Op::OpINotEqual: + new_opcode = spv::Op::OpIEqual; + break; + case spv::Op::OpFOrdEqual: + new_opcode = spv::Op::OpFUnordNotEqual; + break; + case spv::Op::OpFOrdNotEqual: + new_opcode = spv::Op::OpFUnordEqual; + break; + case spv::Op::OpFUnordEqual: + new_opcode = spv::Op::OpFOrdNotEqual; + break; + case spv::Op::OpFUnordNotEqual: + new_opcode = spv::Op::OpFOrdEqual; + break; + case spv::Op::OpLogicalEqual: + new_opcode = spv::Op::OpLogicalNotEqual; + break; + case spv::Op::OpLogicalNotEqual: + new_opcode = spv::Op::OpLogicalEqual; + break; + + // (a > b) <=> (a <= b) + case spv::Op::OpUGreaterThan: + new_opcode = spv::Op::OpULessThanEqual; + break; + case spv::Op::OpULessThanEqual: + new_opcode = spv::Op::OpUGreaterThan; + break; + case spv::Op::OpSGreaterThan: + new_opcode = spv::Op::OpSLessThanEqual; + break; + case spv::Op::OpSLessThanEqual: + new_opcode = spv::Op::OpSGreaterThan; + break; + case spv::Op::OpFOrdGreaterThan: + new_opcode = spv::Op::OpFUnordLessThanEqual; + break; + case spv::Op::OpFOrdLessThanEqual: + new_opcode = spv::Op::OpFUnordGreaterThan; + break; + case spv::Op::OpFUnordGreaterThan: + new_opcode = spv::Op::OpFOrdLessThanEqual; + break; + case spv::Op::OpFUnordLessThanEqual: + new_opcode = spv::Op::OpFOrdGreaterThan; + break; + + // (a < b) <=> (a >= b) + case spv::Op::OpULessThan: + new_opcode = spv::Op::OpUGreaterThanEqual; + break; + case spv::Op::OpUGreaterThanEqual: + new_opcode = spv::Op::OpULessThan; + break; + case spv::Op::OpSLessThan: + new_opcode = spv::Op::OpSGreaterThanEqual; + break; + case spv::Op::OpSGreaterThanEqual: + new_opcode = spv::Op::OpSLessThan; + break; + case spv::Op::OpFOrdLessThan: + new_opcode = spv::Op::OpFUnordGreaterThanEqual; + break; + case spv::Op::OpFOrdGreaterThanEqual: + new_opcode = spv::Op::OpFUnordLessThan; + break; + case spv::Op::OpFUnordLessThan: + new_opcode = spv::Op::OpFOrdGreaterThanEqual; + break; + case spv::Op::OpFUnordGreaterThanEqual: + new_opcode = spv::Op::OpFOrdLessThan; + break; + + default: + break; + } + + if (new_opcode == spv::Op::OpNop) { + return false; + } + + inst->SetOpcode(new_opcode); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(1)}}}); + + return true; + }; +} + +// (a == true) = a +// (a == false) = !a +// (a != true) = !a +// (a != false) = a +FoldingRule RedundantLogicalEqual() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpLogicalEqual || + inst->opcode() == spv::Op::OpLogicalNotEqual); + + const analysis::Constant* const_input = ConstInput(constants); + if (!const_input) { + return false; + } + + analysis::DefUseManager* def_mgr = context->get_def_use_mgr(); + if (inst->type_id() != + def_mgr->GetDef(inst->GetSingleWordInOperand(0))->type_id()) { + return false; + } + + std::optional<bool> uniform_const = GetBoolConstantKind(const_input); + if (!uniform_const) { + return false; + } + + bool direct_copy = inst->opcode() == spv::Op::OpLogicalEqual + ? uniform_const.value() + : !uniform_const.value(); + + inst->SetOpcode(direct_copy ? spv::Op::OpCopyObject + : spv::Op::OpLogicalNot); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {NonConstInput(context, constants[0], inst)->result_id()}}}); + return true; + }; +} + enum class FloatConstantKind { Unknown, Zero, One }; FloatConstantKind getFloatConstantKind(const analysis::Constant* constant) { @@ -2451,14 +3599,31 @@ FloatConstantKind kind0 = getFloatConstantKind(constants[0]); FloatConstantKind kind1 = getFloatConstantKind(constants[1]); - if (kind0 == FloatConstantKind::Zero) { + if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::One) { inst->SetOpcode(spv::Op::OpCopyObject); inst->SetInOperands( {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); return true; } - if (kind1 == FloatConstantKind::One) { + return false; + }; +} + +FoldingRule RedundantFMod() { + return [](IRContext*, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpFMod && + "Wrong opcode. Should be OpFMod."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + + if (kind0 == FloatConstantKind::Zero) { inst->SetOpcode(spv::Op::OpCopyObject); inst->SetInOperands( {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); @@ -2504,24 +3669,18 @@ }; } -// This rule handles addition of zero for integers. -FoldingRule RedundantIAdd() { - return [](IRContext* context, Instruction* inst, - const std::vector<const analysis::Constant*>& constants) { - assert(inst->opcode() == spv::Op::OpIAdd && - "Wrong opcode. Should be OpIAdd."); +// Returns a folding rule that folds the instruction to operand |foldToArg| +// (0 or 1) if operand |arg| (0 or 1) is a zero constant. +FoldingRule RedundantBinaryOpWithZeroOperand(uint32_t arg, uint32_t foldToArg) { + return [arg, foldToArg]( + IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(constants.size() == 2); - uint32_t operand = std::numeric_limits<uint32_t>::max(); - const analysis::Type* operand_type = nullptr; - if (constants[0] && constants[0]->IsZero()) { - operand = inst->GetSingleWordInOperand(1); - operand_type = constants[0]->type(); - } else if (constants[1] && constants[1]->IsZero()) { - operand = inst->GetSingleWordInOperand(0); - operand_type = constants[1]->type(); - } + if (constants[arg] && constants[arg]->IsZero()) { + auto operand = inst->GetSingleWordInOperand(foldToArg); + auto operand_type = constants[arg]->type(); - if (operand != std::numeric_limits<uint32_t>::max()) { const analysis::Type* inst_type = context->get_type_mgr()->GetType(inst->type_id()); if (inst_type->IsSame(operand_type)) { @@ -2536,6 +3695,421 @@ }; } +// This rule handles any of RedundantBinaryRhs0Ops with a 0 or vector 0 on the +// right-hand side (a | 0 => a). +static const constexpr spv::Op RedundantBinaryRhs0Ops[] = { + spv::Op::OpBitwiseOr, + spv::Op::OpBitwiseXor, + spv::Op::OpShiftRightLogical, + spv::Op::OpShiftRightArithmetic, + spv::Op::OpShiftLeftLogical, + spv::Op::OpIAdd, + spv::Op::OpISub}; +FoldingRule RedundantBinaryRhs0(spv::Op op) { + assert(std::find(std::begin(RedundantBinaryRhs0Ops), + std::end(RedundantBinaryRhs0Ops), + op) != std::end(RedundantBinaryRhs0Ops) && + "Wrong opcode."); + (void)op; + return RedundantBinaryOpWithZeroOperand(1, 0); +} + +// This rule handles any of RedundantBinaryLhs0Ops with a 0 or vector 0 on the +// left-hand side (0 | a => a). +static const constexpr spv::Op RedundantBinaryLhs0Ops[] = { + spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpIAdd}; +FoldingRule RedundantBinaryLhs0(spv::Op op) { + assert(std::find(std::begin(RedundantBinaryLhs0Ops), + std::end(RedundantBinaryLhs0Ops), + op) != std::end(RedundantBinaryLhs0Ops) && + "Wrong opcode."); + (void)op; + return RedundantBinaryOpWithZeroOperand(0, 1); +} + +// This rule handles shifts and divisions of 0 or vector 0 by any amount +// (0 >> a => 0). +static const constexpr spv::Op RedundantBinaryLhs0To0Ops[] = { + spv::Op::OpShiftRightLogical, + spv::Op::OpShiftRightArithmetic, + spv::Op::OpShiftLeftLogical, + spv::Op::OpSDiv, + spv::Op::OpUDiv, + spv::Op::OpSMod, + spv::Op::OpUMod}; +FoldingRule RedundantBinaryLhs0To0(spv::Op op) { + assert(std::find(std::begin(RedundantBinaryLhs0To0Ops), + std::end(RedundantBinaryLhs0To0Ops), + op) != std::end(RedundantBinaryLhs0To0Ops) && + "Wrong opcode."); + (void)op; + return RedundantBinaryOpWithZeroOperand(0, 0); +} + +FoldingRule ReassociateCommutiveOp() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + uint32_t width = ElementWidth(type); + if (width != 32) return false; + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + + if (other_inst->opcode() == inst->opcode()) { + std::vector<const analysis::Constant*> other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + + if (merged_id == 0) return false; + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + return true; + } + + return false; + }; +} + +// A | (b | C) = b | (A | C) +// A ^ (b ^ C) = b ^ (A ^ C) +// A & (b & C) = b & (A & C) +// Where A and C are constants +static const constexpr spv::Op ReassociateCommutiveBitwiseOps[] = { + spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpBitwiseAnd}; +FoldingRule ReassociateCommutiveBitwise(spv::Op op) { + assert(std::find(std::begin(ReassociateCommutiveBitwiseOps), + std::end(ReassociateCommutiveBitwiseOps), + op) != std::end(ReassociateCommutiveBitwiseOps) && + "Wrong opcode."); + (void)op; + return ReassociateCommutiveOp(); +} + +// Returns true if all elements in |c| are 1. +bool IsAllInt1(const analysis::Constant* c) { + if (auto composite = c->AsCompositeConstant()) { + auto& components = composite->GetComponents(); + return std::all_of(std::begin(components), std::end(components), IsAllInt1); + } else if (c->AsIntConstant()) { + return c->GetSignExtendedValue() == 1; + } + + return false; +} + +// This rule handles divisions by 1 or vector 1 (a / 1 => a). +FoldingRule RedundantSUDiv() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(constants.size() == 2); + assert((inst->opcode() == spv::Op::OpUDiv || + inst->opcode() == spv::Op::OpSDiv) && + "Wrong opcode."); + + if (constants[1] && IsAllInt1(constants[1])) { + auto operand = inst->GetSingleWordInOperand(0); + auto operand_type = constants[1]->type(); + + const analysis::Type* inst_type = + context->get_type_mgr()->GetType(inst->type_id()); + if (inst_type->IsSame(operand_type)) { + inst->SetOpcode(spv::Op::OpCopyObject); + } else { + inst->SetOpcode(spv::Op::OpBitcast); + } + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}}); + return true; + } + return false; + }; +} + +// This rule handles modulo from division by 1 or vector 1 (a % 1 => 0). +FoldingRule RedundantSUMod() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(constants.size() == 2); + assert((inst->opcode() == spv::Op::OpUMod || + inst->opcode() == spv::Op::OpSMod) && + "Wrong opcode."); + + if (constants[1] && IsAllInt1(constants[1])) { + auto type = context->get_type_mgr()->GetType(inst->type_id()); + auto zero_id = context->get_constant_mgr()->GetNullConstId(type); + + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {zero_id}}}); + return true; + } + return false; + }; +} + +// Utility function for applying |callback| to |input1| and |input2|. +// If they are vectors it applies element wise. +// The constants |input1| and |input2| must be integers or a vector of integers. +template <typename Callback> +void ForEachIntegerConstantPair(analysis::ConstantManager* const_mgr, + const analysis::Constant* input1, + const analysis::Constant* input2, + Callback&& callback) { + assert(input1 && input2); + + auto Dispatch = [&callback](const analysis::Constant* lhs, + const analysis::Constant* rhs) { + assert(lhs->type()->AsInteger()); + const analysis::Integer* type = lhs->type()->AsInteger(); + uint32_t width = type->AsInteger()->width(); + assert(width == 32 || width == 64); + if (width == 32) { + callback(lhs->GetU32(), rhs->GetU32()); + } else { + callback(lhs->GetU64(), rhs->GetU64()); + } + }; + + const analysis::Type* type = input1->type(); + if (const analysis::Vector* vector_type = type->AsVector()) { + const analysis::Type* ele_type = vector_type->element_type(); + assert(ele_type->AsInteger()); + for (uint32_t i = 0; i != vector_type->element_count(); ++i) { + const analysis::Constant* input1_comp = nullptr; + if (const analysis::VectorConstant* input1_vector = + input1->AsVectorConstant()) { + input1_comp = input1_vector->GetComponents()[i]; + } else { + assert(input1->AsNullConstant()); + input1_comp = const_mgr->GetConstant(ele_type, {}); + } + + const analysis::Constant* input2_comp = nullptr; + if (const analysis::VectorConstant* input2_vector = + input2->AsVectorConstant()) { + input2_comp = input2_vector->GetComponents()[i]; + } else { + assert(input2->AsNullConstant()); + input2_comp = const_mgr->GetConstant(ele_type, {}); + } + + assert(ele_type->AsInteger()); + Dispatch(input1_comp, input2_comp); + } + + } else { + assert(type->AsInteger()); + Dispatch(input1, input2); + } +} + +// Folds redundant xor and or ops that are part of an and. +// Cases handled: +// 0b1110 & (a | 0b0001) = a & 0b1110 +// 0b1110 & (a ^ 0b0001) = a & 0b1110 +// 0b0110 & (a | 0b1110) = 0b0110 +FoldingRule RedundantAndOrXor() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode."); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + uint32_t width = ElementWidth(type); + if ((width != 32) && (width != 64)) return false; + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + + if (other_inst->opcode() == spv::Op::OpBitwiseOr || + other_inst->opcode() == spv::Op::OpBitwiseXor) { + std::vector<const analysis::Constant*> other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool can_convert_to_const = other_inst->opcode() == spv::Op::OpBitwiseOr; + bool can_remove_inner = true; + + ForEachIntegerConstantPair( + const_mgr, const_input1, const_input2, + [&can_remove_inner, &can_convert_to_const](auto lhs, auto rhs) { + // Only convert to const if 'and' is a subset of 'or' + can_convert_to_const = can_convert_to_const && ((lhs & rhs) == lhs); + // Only remove 'xor'/'or' if no bits intersect with 'and' + can_remove_inner = can_remove_inner && ((lhs & rhs) == 0); + }); + + if (can_convert_to_const) { + Instruction* const_inst = + const_mgr->GetDefiningInstruction(const_input1); + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}}); + return true; + } else if (can_remove_inner) { + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + Instruction* const_inst = + const_mgr->GetDefiningInstruction(const_input1); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}}, + {SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}}); + return true; + } + } + return false; + }; +} + +// Folds redundant add and sub ops that are part of an and. +// Cases handled: +// 1 & (b + 2) = b & 1 +// 1 & (b - 2) = b & 1 +FoldingRule RedundantAndAddSub() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode."); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + uint32_t width = ElementWidth(type); + if ((width != 32) && (width != 64)) return false; + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + + if (other_inst->opcode() != spv::Op::OpIAdd && + other_inst->opcode() != spv::Op::OpISub) { + return false; + } + std::vector<const analysis::Constant*> other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + // Only valid for subtraction if const is on the right + if ((other_inst->opcode() == spv::Op::OpISub) && other_constants[0]) { + return false; + } + + bool can_remove_inner = true; + ForEachIntegerConstantPair(const_mgr, const_input1, const_input2, + [&can_remove_inner](auto and_op, auto add_op) { + if (can_remove_inner) { + // Only valid if no bits from the +/- could + // affect bits from the & operation. + can_remove_inner = + utils::LSB(add_op) > and_op; + } + }); + + if (can_remove_inner) { + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + Instruction* const_inst = const_mgr->GetDefiningInstruction(const_input1); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}}, + {SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}}); + return true; + } + return false; + }; +} + +// Folds redundant shift ops that are part of an and. +// Cases handled: +// 1 & (b << 1) = 0 +// 0x80000000 & (b >> 1) = 0 +FoldingRule RedundantAndShift() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode."); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + uint32_t width = ElementWidth(type); + if (width != 8 && width != 16 && width != 32 && width != 64) return false; + const uint64_t width_mask = + (width == 64) ? ~0ull : ((1ull << width) - 1ull); + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + + spv::Op other_op = other_inst->opcode(); + if (other_op != spv::Op::OpShiftLeftLogical && + other_op != spv::Op::OpShiftRightLogical) { + return false; + } + + std::vector<const analysis::Constant*> other_constants = + const_mgr->GetOperandConstants(other_inst); + + // Only valid if const is on the right. + if (other_constants[0]) return false; + const analysis::Constant* const_input2 = other_constants[1]; + if (!const_input2) return false; + + auto get_value_u64 = + [](const analysis::Constant* c) -> std::optional<uint64_t> { + if (!c) return std::nullopt; + const analysis::Integer* int_t = c->type()->AsInteger(); + if (!int_t) return std::nullopt; + return c->GetZeroExtendedValue(); + }; + + auto can_fold_component = + [&](const analysis::Constant* mask_const, + const analysis::Constant* shift_const) -> std::optional<bool> { + auto lhs = get_value_u64(mask_const); + auto rhs = get_value_u64(shift_const); + if (!lhs || !rhs) return std::nullopt; + if (*rhs >= width) return false; + uint64_t lhs_masked = *lhs & width_mask; + if (other_op == spv::Op::OpShiftRightLogical) { + return ((lhs_masked << *rhs) & width_mask) == 0; + } + return ((lhs_masked >> *rhs) & width_mask) == 0; + }; + + if (const analysis::Vector* mask_vec = type->AsVector()) { + const analysis::Vector* shift_vec = const_input2->type()->AsVector(); + if (!shift_vec || + shift_vec->element_count() != mask_vec->element_count()) { + return false; + } + const auto mask_components = const_input1->GetVectorComponents(const_mgr); + const auto shift_components = + const_input2->GetVectorComponents(const_mgr); + for (uint32_t i = 0; i != mask_vec->element_count(); ++i) { + auto result = + can_fold_component(mask_components[i], shift_components[i]); + if (!result || !*result) return false; + } + } else { + if (const_input2->type()->AsVector()) return false; + auto result = can_fold_component(const_input1, const_input2); + if (!result || !*result) return false; + } + + auto zero_id = context->get_constant_mgr()->GetNullConstId(type); + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {zero_id}}}); + return true; + }; +} + // This rule look for a dot with a constant vector containing a single 1 and // the rest 0s. This is the same as doing an extract. FoldingRule DotProductDoingExtract() { @@ -2873,7 +4447,27 @@ // Note that the order in which rules are added to the list matters. If a rule // applies to the instruction, the rest of the rules will not be attempted. // Take that into consideration. + for (auto op : RedundantBinaryRhs0Ops) + rules_[op].push_back(RedundantBinaryRhs0(op)); + for (auto op : RedundantBinaryLhs0Ops) + rules_[op].push_back(RedundantBinaryLhs0(op)); + for (auto op : RedundantBinaryLhs0To0Ops) + rules_[op].push_back(RedundantBinaryLhs0To0(op)); + for (auto op : ReassociateCommutiveBitwiseOps) + rules_[op].push_back(ReassociateCommutiveBitwise(op)); + for (auto op : ReassociateNestedGenericIntOps) + rules_[op].push_back(ReassociateNestedGenericInt(op)); + for (auto op : MergeBinaryOpSelectOps) + rules_[op].push_back(MergeBinaryOpSelect(op)); + rules_[spv::Op::OpSDiv].push_back(RedundantSUDiv()); + rules_[spv::Op::OpUDiv].push_back(RedundantSUDiv()); + rules_[spv::Op::OpSMod].push_back(RedundantSUMod()); + rules_[spv::Op::OpUMod].push_back(RedundantSUMod()); + rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector()); + rules_[spv::Op::OpBitcast].push_back(RedundantBitcast()); + + rules_[spv::Op::OpBitReverse].push_back(BitReverseScalarOrVector()); rules_[spv::Op::OpCompositeConstruct].push_back( CompositeExtractFeedingConstruct); @@ -2883,6 +4477,8 @@ CompositeConstructFeedingExtract); rules_[spv::Op::OpCompositeExtract].push_back(VectorShuffleFeedingExtract()); rules_[spv::Op::OpCompositeExtract].push_back(FMixFeedingExtract()); + rules_[spv::Op::OpCompositeExtract].push_back(CopyLogicalFeedingExtract); + rules_[spv::Op::OpCompositeExtract].push_back(LoadFeedingExtract); rules_[spv::Op::OpCompositeInsert].push_back( CompositeInsertToCompositeConstruct); @@ -2896,18 +4492,27 @@ rules_[spv::Op::OpFAdd].push_back(MergeAddAddArithmetic()); rules_[spv::Op::OpFAdd].push_back(MergeAddSubArithmetic()); rules_[spv::Op::OpFAdd].push_back(MergeGenericAddSubArithmetic()); - rules_[spv::Op::OpFAdd].push_back(FactorAddMuls()); + rules_[spv::Op::OpFAdd].push_back(ReassociateNestedAddSub()); + rules_[spv::Op::OpFAdd].push_back(FactorAddSubMuls()); rules_[spv::Op::OpFDiv].push_back(RedundantFDiv()); rules_[spv::Op::OpFDiv].push_back(ReciprocalFDiv()); rules_[spv::Op::OpFDiv].push_back(MergeDivDivArithmetic()); rules_[spv::Op::OpFDiv].push_back(MergeDivMulArithmetic()); rules_[spv::Op::OpFDiv].push_back(MergeDivNegateArithmetic()); + rules_[spv::Op::OpFDiv].push_back(MergeDivMulDoubleNegative()); + rules_[spv::Op::OpFDiv].push_back(ReassociateNestedMulDivFloat()); + + rules_[spv::Op::OpFMod].push_back(RedundantFMod()); rules_[spv::Op::OpFMul].push_back(RedundantFMul()); rules_[spv::Op::OpFMul].push_back(MergeMulMulArithmetic()); rules_[spv::Op::OpFMul].push_back(MergeMulDivArithmetic()); rules_[spv::Op::OpFMul].push_back(MergeMulNegateArithmetic()); + rules_[spv::Op::OpFMul].push_back(MergeDivMulDoubleNegative()); + rules_[spv::Op::OpFMul].push_back(ReassociateNestedMulDivFloat()); + + rules_[spv::Op::OpVectorTimesScalar].push_back(MergeDivMulDoubleNegative()); rules_[spv::Op::OpFNegate].push_back(MergeNegateArithmetic()); rules_[spv::Op::OpFNegate].push_back(MergeNegateAddSubArithmetic()); @@ -2917,21 +4522,32 @@ rules_[spv::Op::OpFSub].push_back(MergeSubNegateArithmetic()); rules_[spv::Op::OpFSub].push_back(MergeSubAddArithmetic()); rules_[spv::Op::OpFSub].push_back(MergeSubSubArithmetic()); + rules_[spv::Op::OpFSub].push_back(ReassociateNestedAddSub()); + rules_[spv::Op::OpFSub].push_back(FactorAddSubMuls()); - rules_[spv::Op::OpIAdd].push_back(RedundantIAdd()); rules_[spv::Op::OpIAdd].push_back(MergeAddNegateArithmetic()); rules_[spv::Op::OpIAdd].push_back(MergeAddAddArithmetic()); rules_[spv::Op::OpIAdd].push_back(MergeAddSubArithmetic()); rules_[spv::Op::OpIAdd].push_back(MergeGenericAddSubArithmetic()); - rules_[spv::Op::OpIAdd].push_back(FactorAddMuls()); + rules_[spv::Op::OpIAdd].push_back(ReassociateNestedAddSub()); + rules_[spv::Op::OpIAdd].push_back(FactorAddSubMuls()); + + rules_[spv::Op::OpSDiv].push_back(MergeDivMulDoubleNegative()); rules_[spv::Op::OpIMul].push_back(IntMultipleBy1()); rules_[spv::Op::OpIMul].push_back(MergeMulMulArithmetic()); rules_[spv::Op::OpIMul].push_back(MergeMulNegateArithmetic()); + rules_[spv::Op::OpIMul].push_back(MergeDivMulDoubleNegative()); rules_[spv::Op::OpISub].push_back(MergeSubNegateArithmetic()); rules_[spv::Op::OpISub].push_back(MergeSubAddArithmetic()); rules_[spv::Op::OpISub].push_back(MergeSubSubArithmetic()); + rules_[spv::Op::OpISub].push_back(ReassociateNestedAddSub()); + rules_[spv::Op::OpISub].push_back(FactorAddSubMuls()); + + rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndOrXor()); + rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndAddSub()); + rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndShift()); rules_[spv::Op::OpPhi].push_back(RedundantPhi()); @@ -2940,6 +4556,17 @@ rules_[spv::Op::OpSNegate].push_back(MergeNegateAddSubArithmetic()); rules_[spv::Op::OpSelect].push_back(RedundantSelect()); + rules_[spv::Op::OpSelect].push_back(FoldConstantBooleanSelect()); + + rules_[spv::Op::OpLogicalAnd].push_back(RedundantLogicalAnd()); + + rules_[spv::Op::OpLogicalOr].push_back(RedundantLogicalOr()); + + rules_[spv::Op::OpLogicalNot].push_back(RedundantLogicalNot()); + rules_[spv::Op::OpLogicalNot].push_back(FoldLogicalNotComparison()); + + rules_[spv::Op::OpLogicalEqual].push_back(RedundantLogicalEqual()); + rules_[spv::Op::OpLogicalNotEqual].push_back(RedundantLogicalEqual()); rules_[spv::Op::OpStore].push_back(StoringUndef());
diff --git a/source/opt/freeze_spec_constant_value_pass.cpp b/source/opt/freeze_spec_constant_value_pass.cpp index 3f89e56..5ed8a92 100644 --- a/source/opt/freeze_spec_constant_value_pass.cpp +++ b/source/opt/freeze_spec_constant_value_pass.cpp
@@ -35,6 +35,10 @@ inst->SetOpcode(spv::Op::OpConstantFalse); modified = true; break; + case spv::Op::OpSpecConstantDataKHR: + inst->SetOpcode(spv::Op::OpConstantDataKHR); + modified = true; + break; case spv::Op::OpDecorate: if (spv::Decoration(inst->GetSingleWordInOperand(1)) == spv::Decoration::SpecId) {
diff --git a/source/opt/function.cpp b/source/opt/function.cpp index 2ee88ec..560953b 100644 --- a/source/opt/function.cpp +++ b/source/opt/function.cpp
@@ -40,6 +40,10 @@ clone->blocks_.reserve(blocks_.size()); for (const auto& b : blocks_) { std::unique_ptr<BasicBlock> bb(b->Clone(ctx)); + if (!bb) { + delete clone; + return nullptr; + } clone->AddBasicBlock(std::move(bb)); }
diff --git a/source/opt/function.h b/source/opt/function.h index 8c0472c..39ecd59 100644 --- a/source/opt/function.h +++ b/source/opt/function.h
@@ -17,6 +17,7 @@ #include <algorithm> #include <functional> +#include <iterator> #include <memory> #include <string> #include <unordered_set> @@ -39,6 +40,7 @@ public: using iterator = UptrVectorIterator<BasicBlock>; using const_iterator = UptrVectorIterator<BasicBlock, true>; + using ParamList = std::vector<std::unique_ptr<Instruction>>; // Creates a function instance declared by the given OpFunction instruction // |def_inst|. @@ -46,7 +48,7 @@ explicit Function(const Function& f) = delete; - // Creates a clone of the instruction in the given |context| + // Creates a clone of the function in the given |context| // // The parent module will default to null and needs to be explicitly set by // the user. @@ -77,6 +79,23 @@ // Does nothing if the function doesn't have such a parameter. inline void RemoveParameter(uint32_t id); + // Rewrites the function parameters by calling a replacer callback. + // The replacer takes two parameters: an expiring unique pointer to a current + // instruction, and a back-inserter into a new list of unique pointers to + // instructions. The replacer is called for each current parameter, in order. + // Not valid to call while also iterating through the parameter list, e.g. + // within the ForEachParam method. + using RewriteParamFn = std::function<void( + std::unique_ptr<Instruction>&&, std::back_insert_iterator<ParamList>&)>; + void RewriteParams(RewriteParamFn& replacer) { + ParamList new_params; + auto appender = std::back_inserter(new_params); + for (auto& param : params_) { + replacer(std::move(param), appender); + } + params_ = std::move(new_params); + } + // Saves the given function end instruction. inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst); @@ -197,7 +216,7 @@ // The OpFunction instruction that begins the definition of this function. std::unique_ptr<Instruction> def_inst_; // All parameters to this function. - std::vector<std::unique_ptr<Instruction>> params_; + ParamList params_; // All debug instructions in this function's header. InstructionList debug_insts_in_header_; // All basic blocks inside this function in specification order
diff --git a/source/opt/graph.cpp b/source/opt/graph.cpp new file mode 100644 index 0000000..ba1030d --- /dev/null +++ b/source/opt/graph.cpp
@@ -0,0 +1,90 @@ +// Copyright (c) 2022-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/graph.h" + +namespace spvtools { +namespace opt { + +Graph* Graph::Clone(IRContext* ctx) const { + Graph* clone = new Graph(std::unique_ptr<Instruction>(DefInst().Clone(ctx))); + + clone->inputs_.reserve(inputs_.size()); + for (const auto& i : inputs()) { + clone->AddInput(std::unique_ptr<Instruction>(i->Clone(ctx))); + } + + clone->insts_.reserve(insts_.size()); + for (const auto& i : instructions()) { + clone->AddInstruction(std::unique_ptr<Instruction>(i->Clone(ctx))); + } + + clone->outputs_.reserve(outputs_.size()); + for (const auto& i : outputs()) { + clone->AddOutput(std::unique_ptr<Instruction>(i->Clone(ctx))); + } + + clone->SetGraphEnd(std::unique_ptr<Instruction>(EndInst()->Clone(ctx))); + + return clone; +} + +void Graph::ForEachInst(const std::function<void(Instruction*)>& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) { + (void)run_on_debug_line_insts; + (void)run_on_non_semantic_insts; + + f(def_inst_.get()); + + for (auto& inst : inputs_) { + f(inst.get()); + } + + for (auto& inst : insts_) { + f(inst.get()); + } + + for (auto& inst : outputs_) { + f(inst.get()); + } + + f(end_inst_.get()); +} + +void Graph::ForEachInst(const std::function<void(const Instruction*)>& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) const { + (void)run_on_debug_line_insts; + (void)run_on_non_semantic_insts; + + f(def_inst_.get()); + + for (auto& inst : inputs_) { + f(inst.get()); + } + + for (auto& inst : insts_) { + f(inst.get()); + } + + for (auto& inst : outputs_) { + f(inst.get()); + } + + f(end_inst_.get()); +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/graph.h b/source/opt/graph.h new file mode 100644 index 0000000..3b533b2 --- /dev/null +++ b/source/opt/graph.h
@@ -0,0 +1,124 @@ +// Copyright (c) 2022-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_GRAPH_H_ +#define SOURCE_OPT_GRAPH_H_ + +#include "source/opt/instruction.h" + +namespace spvtools { +namespace opt { + +struct Graph { + // Creates a graph instance declared by the given OpGraph instruction + // |def_inst|. + inline explicit Graph(std::unique_ptr<Instruction> def_inst); + explicit Graph(const Graph& f) = delete; + + // Creates a clone of the graph in the given |context| + // + // The parent module will default to null and needs to be explicitly set by + // the user. + Graph* Clone(IRContext*) const; + + // The OpGraph instruction that begins the definition of this graph. + Instruction& DefInst() { return *def_inst_; } + const Instruction& DefInst() const { return *def_inst_; } + + // Appends an input to this graph. + inline void AddInput(std::unique_ptr<Instruction> inst); + + // Appends an instruction to this graph. + inline void AddInstruction(std::unique_ptr<Instruction> inst); + + // Appends an output to this graph. + inline void AddOutput(std::unique_ptr<Instruction> inst); + + // Saves the given graph end instruction. + void SetGraphEnd(std::unique_ptr<Instruction> end_inst); + + // Returns the given graph end instruction. + inline Instruction* EndInst() { return end_inst_.get(); } + inline const Instruction* EndInst() const { return end_inst_.get(); } + + // Returns graph's id + inline uint32_t result_id() const { return def_inst_->result_id(); } + + // Returns graph's return type id + inline uint32_t type_id() const { return def_inst_->type_id(); } + + // Return a read-only reference to the instructions that define the body of + // the graph. + const std::vector<std::unique_ptr<Instruction>>& instructions() const { + return insts_; + } + + // Return a read-only reference to the instructions that define the inputs + // of the graph. + const std::vector<std::unique_ptr<Instruction>>& inputs() const { + return inputs_; + } + + // Return a read-only reference to the instructions that define the outputs + // of the graph. + const std::vector<std::unique_ptr<Instruction>>& outputs() const { + return outputs_; + } + + // Runs the given function |f| on instructions in this graph, in order, + // and optionally on debug line instructions that might precede them and + // non-semantic instructions that succceed the function. + void ForEachInst(const std::function<void(Instruction*)>& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false); + void ForEachInst(const std::function<void(const Instruction*)>& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false) const; + + private: + // The OpGraph instruction that begins the definition of this graph. + std::unique_ptr<Instruction> def_inst_; + // All inputs to this graph. + std::vector<std::unique_ptr<Instruction>> inputs_; + // All instructions describing this graph + std::vector<std::unique_ptr<Instruction>> insts_; + // All outputs of this graph. + std::vector<std::unique_ptr<Instruction>> outputs_; + // The OpGraphEnd instruction. + std::unique_ptr<Instruction> end_inst_; +}; + +inline Graph::Graph(std::unique_ptr<Instruction> def_inst) + : def_inst_(std::move(def_inst)) {} + +inline void Graph::AddInput(std::unique_ptr<Instruction> inst) { + inputs_.emplace_back(std::move(inst)); +} + +inline void Graph::AddInstruction(std::unique_ptr<Instruction> inst) { + insts_.emplace_back(std::move(inst)); +} + +inline void Graph::AddOutput(std::unique_ptr<Instruction> inst) { + outputs_.emplace_back(std::move(inst)); +} + +inline void Graph::SetGraphEnd(std::unique_ptr<Instruction> end_inst) { + end_inst_ = std::move(end_inst); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_GRAPH_H_
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index e765c39..15076ec 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp
@@ -283,9 +283,14 @@ // use 0 for %min_value). auto clamp_index = [&inst, type_mgr, this, &replace_index]( uint32_t operand_index, Instruction* old_value, - Instruction* min_value, Instruction* max_value) { + Instruction* min_value, + Instruction* max_value) -> spv_result_t { auto* clamp_inst = MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst); + if (clamp_inst == nullptr) { + Fail(); + return SPV_ERROR_INTERNAL; + } return replace_index(operand_index, clamp_inst); }; @@ -304,7 +309,11 @@ if (count <= 1) { // Replace the index with 0. - return replace_index(operand_index, GetValueForType(0, index_type)); + Instruction* new_value = GetValueForType(0, index_type); + if (new_value == nullptr) { + return Fail(); + } + return replace_index(operand_index, new_value); } uint64_t maxval = count - 1; @@ -318,8 +327,15 @@ // Determine the type for |maxval|. uint32_t next_id = context()->module()->IdBound(); analysis::Integer signed_type_for_query(maxval_width, true); - auto* maxval_type = - type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger(); + auto* maxval_type_registered = + type_mgr->GetRegisteredType(&signed_type_for_query); + if (maxval_type_registered == nullptr) { + return Fail(); + } + auto* maxval_type = maxval_type_registered->AsInteger(); + if (maxval_type == nullptr) { + return Fail(); + } if (next_id != context()->module()->IdBound()) { module_status_.modified = true; } @@ -352,15 +368,22 @@ value = int_index_constant->GetS64BitValue(); } if (value < 0) { - return replace_index(operand_index, GetValueForType(0, index_type)); + Instruction* new_value = GetValueForType(0, index_type); + if (new_value == nullptr) { + return Fail(); + } + return replace_index(operand_index, new_value); } else if (uint64_t(value) <= maxval) { // Nothing to do. return SPV_SUCCESS; } else { // Replace with maxval. assert(count > 0); // Already took care of this case above. - return replace_index(operand_index, - GetValueForType(maxval, maxval_type)); + Instruction* new_value = GetValueForType(maxval, maxval_type); + if (new_value == nullptr) { + return Fail(); + } + return replace_index(operand_index, new_value); } } else { // Generate a clamp instruction. @@ -389,6 +412,9 @@ } index_inst = WidenInteger(index_type->IsSigned(), maxval_width, index_inst, &inst); + if (index_inst == nullptr) { + return Fail(); + } } // Finally, clamp the index. @@ -438,28 +464,51 @@ if (index_type->width() < target_width) { // Access chain indices are treated as signed integers. index_inst = WidenInteger(true, target_width, index_inst, &inst); + if (index_inst == nullptr) { + return Fail(); + } } else if (count_type->width() < target_width) { // Assume type sizes are treated as unsigned. count_inst = WidenInteger(false, target_width, count_inst, &inst); + if (count_inst == nullptr) { + return Fail(); + } } // Compute count - 1. // It doesn't matter if 1 is signed or unsigned. auto* one = GetValueForType(1, wider_type); - auto* count_minus_1 = InsertInst( - &inst, spv::Op::OpISub, type_mgr->GetId(wider_type), TakeNextId(), - {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}}, - {SPV_OPERAND_TYPE_ID, {one->result_id()}}}); + if (!one) { + return Fail(); + } + auto* count_minus_1 = + InsertInst(&inst, spv::Op::OpISub, type_mgr->GetId(wider_type), + context()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {one->result_id()}}}); + if (count_minus_1 == nullptr) { + return Fail(); + } auto* zero = GetValueForType(0, wider_type); + if (!zero) { + return Fail(); + } // Make sure we clamp to an upper bound that is at most the signed max // for the target type. const uint64_t max_signed_value = ((uint64_t(1) << (target_width - 1)) - 1); + Instruction* max_signed_inst = + GetValueForType(max_signed_value, wider_type); + if (!max_signed_inst) { + return Fail(); + } // Use unsigned-min to ensure that the result is always non-negative. // That ensures we satisfy the invariant for SClamp, where the "min" // argument we give it (zero), is no larger than the third argument. auto* upper_bound = - MakeUMinInst(*type_mgr, count_minus_1, - GetValueForType(max_signed_value, wider_type), &inst); + MakeUMinInst(*type_mgr, count_minus_1, max_signed_inst, &inst); + if (upper_bound == nullptr) { + return Fail(); + } // Now clamp the index to this upper bound. return clamp_index(operand_index, index_inst, zero, upper_bound); } @@ -485,7 +534,7 @@ case spv::Op::OpTypeVector: // Use component count { const uint32_t count = pointee_type->GetSingleWordOperand(2); - clamp_to_literal_count(idx, count); + if (clamp_to_literal_count(idx, count) != SPV_SUCCESS) return; pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); } break; @@ -493,7 +542,7 @@ // The array length can be a spec constant, so go through the general // case. Instruction* array_len = GetDef(pointee_type->GetSingleWordOperand(2)); - clamp_to_count(idx, array_len); + if (clamp_to_count(idx, array_len) != SPV_SUCCESS) return; pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); } break; @@ -537,7 +586,7 @@ if (!array_len) { // We've already signaled an error. return; } - clamp_to_count(idx, array_len); + if (clamp_to_count(idx, array_len) != SPV_SUCCESS) return; if (module_status_.failed) return; pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); } break; @@ -563,7 +612,10 @@ } if (module_status_.glsl_insts_id == 0) { // Make a new import instruction. - module_status_.glsl_insts_id = TakeNextId(); + module_status_.glsl_insts_id = context()->TakeNextId(); + if (module_status_.glsl_insts_id == 0) { + return 0; + } std::vector<uint32_t> words = spvtools::utils::MakeVector(glsl); auto import_inst = MakeUnique<Instruction>( context(), spv::Op::OpExtInstImport, 0, module_status_.glsl_insts_id, @@ -602,7 +654,10 @@ auto* type_mgr = context()->get_type_mgr(); auto* unsigned_type = type_mgr->GetRegisteredType(&unsigned_type_for_query); auto type_id = context()->get_type_mgr()->GetId(unsigned_type); - auto conversion_id = TakeNextId(); + auto conversion_id = context()->TakeNextId(); + if (conversion_id == 0) { + return nullptr; + } auto* conversion = InsertInst( before_inst, (sign_extend ? spv::Op::OpSConvert : spv::Op::OpUConvert), type_id, conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}}); @@ -616,7 +671,13 @@ // the function so we force a deterministic ordering in case both of them need // to take a new ID. const uint32_t glsl_insts_id = GetGlslInsts(); - uint32_t smin_id = TakeNextId(); + if (glsl_insts_id == 0) { + return nullptr; + } + uint32_t smin_id = context()->TakeNextId(); + if (smin_id == 0) { + return nullptr; + } const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width(); assert(xwidth == ywidth); @@ -640,7 +701,13 @@ // the function so we force a deterministic ordering in case both of them need // to take a new ID. const uint32_t glsl_insts_id = GetGlslInsts(); - uint32_t clamp_id = TakeNextId(); + if (glsl_insts_id == 0) { + return nullptr; + } + uint32_t clamp_id = context()->TakeNextId(); + if (clamp_id == 0) { + return nullptr; + } const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width(); const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width(); @@ -755,7 +822,11 @@ base_ptr_type->storage_class()); // Create the instruction and insert it. - const auto new_access_chain_id = TakeNextId(); + const auto new_access_chain_id = context()->TakeNextId(); + if (new_access_chain_id == 0) { + Fail(); + return nullptr; + } auto* new_access_chain = InsertInst(current_access_chain, current_access_chain->opcode(), new_access_chain_type_id, new_access_chain_id, ops); @@ -784,7 +855,11 @@ uint32_t(struct_type->element_types().size() - 1); // Create the length-of-array instruction before the original access chain, // but after the generation of the pointer to the struct. - const auto array_len_id = TakeNextId(); + const auto array_len_id = context()->TakeNextId(); + if (array_len_id == 0) { + Fail(); + return nullptr; + } analysis::Integer uint_type_for_query(32, false); auto* uint_type = type_mgr->GetRegisteredType(&uint_type_for_query); auto* array_len = InsertInst( @@ -935,12 +1010,18 @@ return type_mgr->GetRegisteredType(&proposed); }(); - const uint32_t image_id = TakeNextId(); + const uint32_t image_id = context()->TakeNextId(); + if (image_id == 0) { + return Fail(); + } auto* image = InsertInst(image_texel_pointer, spv::Op::OpLoad, image_type_id, image_id, {{SPV_OPERAND_TYPE_ID, {image_ptr->result_id()}}}); - const uint32_t query_size_id = TakeNextId(); + const uint32_t query_size_id = context()->TakeNextId(); + if (query_size_id == 0) { + return Fail(); + } auto* query_size = InsertInst(image_texel_pointer, spv::Op::OpImageQuerySize, type_mgr->GetTypeInstruction(query_size_type), query_size_id, @@ -968,7 +1049,10 @@ query_size_type, {component_1_id, component_1_id, component_6_id}); auto* multiplicand_inst = constant_mgr->GetDefiningInstruction(multiplicand); - const auto query_size_including_faces_id = TakeNextId(); + const auto query_size_including_faces_id = context()->TakeNextId(); + if (query_size_including_faces_id == 0) { + return Fail(); + } query_size_including_faces = InsertInst( image_texel_pointer, spv::Op::OpIMul, type_mgr->GetTypeInstruction(query_size_type), @@ -992,7 +1076,10 @@ query_size_type, std::vector<uint32_t>(query_num_components, component_0_id)); - const uint32_t query_max_including_faces_id = TakeNextId(); + const uint32_t query_max_including_faces_id = context()->TakeNextId(); + if (query_max_including_faces_id == 0) { + return Fail(); + } auto* query_max_including_faces = InsertInst( image_texel_pointer, spv::Op::OpISub, type_mgr->GetTypeInstruction(query_size_type), @@ -1005,18 +1092,27 @@ auto* clamp_coord = MakeSClampInst( *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0), query_max_including_faces, image_texel_pointer); + if (clamp_coord == nullptr) { + return Fail(); + } image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()}); // Clamp the sample index if (multisampled) { // Get the sample count via OpImageQuerySamples - const auto query_samples_id = TakeNextId(); + const auto query_samples_id = context()->TakeNextId(); + if (query_samples_id == 0) { + return Fail(); + } auto* query_samples = InsertInst( image_texel_pointer, spv::Op::OpImageQuerySamples, constant_mgr->GetDefiningInstruction(component_0)->type_id(), query_samples_id, {{SPV_OPERAND_TYPE_ID, {image->result_id()}}}); - const auto max_samples_id = TakeNextId(); + const auto max_samples_id = context()->TakeNextId(); + if (max_samples_id == 0) { + return Fail(); + } auto* max_samples = InsertInst(image_texel_pointer, spv::Op::OpImageQuerySamples, query_samples->type_id(), max_samples_id, {{SPV_OPERAND_TYPE_ID, {query_samples_id}}, @@ -1025,6 +1121,9 @@ auto* clamp_samples = MakeSClampInst( *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0), max_samples, image_texel_pointer); + if (clamp_samples == nullptr) { + return Fail(); + } image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()}); } else { @@ -1041,6 +1140,9 @@ opt::Instruction* GraphicsRobustAccessPass::InsertInst( opt::Instruction* where_inst, spv::Op opcode, uint32_t type_id, uint32_t result_id, const Instruction::OperandList& operands) { + if (result_id == 0) { + return nullptr; + } module_status_.modified = true; auto* result = where_inst->InsertBefore( MakeUnique<Instruction>(context(), opcode, type_id, result_id, operands));
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp index 5912cf1..2b71626 100644 --- a/source/opt/if_conversion.cpp +++ b/source/opt/if_conversion.cpp
@@ -126,6 +126,7 @@ condition = SplatCondition(vec_data_ty, condition, &builder); } + // TODO(1841): Handle id overflow. Instruction* select = builder.AddSelect(phi->type_id(), condition, true_value->result_id(), false_value->result_id()); @@ -205,6 +206,7 @@ uint32_t bool_vec_id = context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty); std::vector<uint32_t> ids(vec_data_ty->element_count(), cond); + // TODO(1841): Handle id overflow. return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id(); }
diff --git a/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp index bef4501..c04fb04 100644 --- a/source/opt/inline_exhaustive_pass.cpp +++ b/source/opt/inline_exhaustive_pass.cpp
@@ -16,6 +16,7 @@ #include "source/opt/inline_exhaustive_pass.h" +#include <iterator> #include <utility> namespace spvtools { @@ -27,6 +28,10 @@ for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end();) { if (IsInlinableFunctionCall(&*ii)) { + // Save instruction before the call to avoid redundant re-scanning. + Instruction* prev_inst = + (ii == bi->begin()) ? nullptr : &*std::prev(ii); + // Inline call. std::vector<std::unique_ptr<BasicBlock>> newBlocks; std::vector<std::unique_ptr<Instruction>> newVars; @@ -47,14 +52,19 @@ // Insert new function variables. if (newVars.size() > 0) func->begin()->begin().InsertBefore(std::move(newVars)); - // Restart inlining at beginning of calling block. - ii = bi->begin(); + // Restart inlining at the first instruction of the inlined code. + ii = prev_inst ? ++InstructionList::iterator(prev_inst) : bi->begin(); modified = true; } else { ++ii; } } } + + if (modified) { + FixDebugDeclares(func); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); }
diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp index 90a4c22..0831c25 100644 --- a/source/opt/inline_opaque_pass.cpp +++ b/source/opt/inline_opaque_pass.cpp
@@ -16,6 +16,7 @@ #include "source/opt/inline_opaque_pass.h" +#include <iterator> #include <utility> namespace spvtools { @@ -67,6 +68,10 @@ for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end();) { if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) { + // Save instruction before the call to avoid redundant re-scanning. + Instruction* prev_inst = + (ii == bi->begin()) ? nullptr : &*std::prev(ii); + // Inline call. std::vector<std::unique_ptr<BasicBlock>> newBlocks; std::vector<std::unique_ptr<Instruction>> newVars; @@ -79,18 +84,27 @@ if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); // Replace old calling block with new block(s). bi = bi.Erase(); + + for (auto& bb : newBlocks) { + bb->SetParent(func); + } bi = bi.InsertBefore(&newBlocks); // Insert new function variables. if (newVars.size() > 0) func->begin()->begin().InsertBefore(std::move(newVars)); - // Restart inlining at beginning of calling block. - ii = bi->begin(); + // Restart inlining at the first instruction of the inlined code. + ii = prev_inst ? ++InstructionList::iterator(prev_inst) : bi->begin(); modified = true; } else { ++ii; } } } + + if (modified) { + FixDebugDeclares(func); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); }
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 3186433..dbbb31f 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp
@@ -30,6 +30,8 @@ constexpr int kSpvFunctionCallFunctionId = 2; constexpr int kSpvFunctionCallArgumentId = 3; constexpr int kSpvReturnValueId = 0; +constexpr int kSpvDebugDeclareVarInIdx = 3; +constexpr int kSpvAccessChainBaseInIdx = 0; } // namespace uint32_t InlinePass::AddPointerToType(uint32_t type_id, @@ -422,8 +424,8 @@ while (callee_inst_itr != callee_first_block->end()) { // Don't inline function definition links, the calling function is not a // definition. - if (callee_inst_itr->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + if (callee_inst_itr->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunctionDefinition) { ++callee_inst_itr; continue; } @@ -460,8 +462,8 @@ ++inst_itr) { // Don't inline function definition links, the calling function is not a // definition - if (inst_itr->GetShader100DebugOpcode() == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) + if (inst_itr->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunctionDefinition) continue; if (!InlineSingleInstruction( callee2caller, new_blk_ptr.get(), &*inst_itr, @@ -858,5 +860,92 @@ InlinePass::InlinePass() {} +void InlinePass::FixDebugDeclares(Function* func) { + std::map<uint32_t, Instruction*> access_chains; + std::vector<Instruction*> debug_declare_insts; + + func->ForEachInst([&access_chains, &debug_declare_insts](Instruction* inst) { + if (inst->opcode() == spv::Op::OpAccessChain) { + access_chains[inst->result_id()] = inst; + } + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + debug_declare_insts.push_back(inst); + } + }); + + for (auto& inst : debug_declare_insts) { + FixDebugDeclare(inst, access_chains); + } +} + +void InlinePass::FixDebugDeclare( + Instruction* dbg_declare_inst, + const std::map<uint32_t, Instruction*>& access_chains) { + do { + uint32_t var_id = + dbg_declare_inst->GetSingleWordInOperand(kSpvDebugDeclareVarInIdx); + + // The def-use chains are not kept up to date while inlining, so we need to + // get the variable by traversing the functions. + auto it = access_chains.find(var_id); + if (it == access_chains.end()) { + return; + } + Instruction* access_chain = it->second; + + // If the variable id in the debug declare is an access chain, it is + // invalid. it needs to be fixed up. The debug declare will be updated so + // that its Var operand becomes the base of the access chain. The indexes of + // the access chain are prepended before the indexes of the debug declare. + + // DebugDeclare Indexes must be constant integers. If any access chain + // index is non-constant (e.g. the result of an OpLoad), we cannot + // produce a valid DebugDeclare. Kill it rather than emit invalid SPIR-V. + bool has_non_constant_index = false; + for (uint32_t i = kSpvAccessChainBaseInIdx + 1; + i < access_chain->NumInOperands(); ++i) { + uint32_t idx_id = access_chain->GetSingleWordInOperand(i); + bool found_constant = false; + for (auto& inst : context()->module()->types_values()) { + if (inst.result_id() == idx_id) { + found_constant = spvOpcodeIsConstant(inst.opcode()); + break; + } + } + if (!found_constant) { + has_non_constant_index = true; + break; + } + } + if (has_non_constant_index) { + context()->KillInst(dbg_declare_inst); + return; + } + + std::vector<Operand> operands; + for (int i = 0; i < kSpvDebugDeclareVarInIdx; i++) { + operands.push_back(dbg_declare_inst->GetInOperand(i)); + } + + uint32_t access_chain_base = + access_chain->GetSingleWordInOperand(kSpvAccessChainBaseInIdx); + operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {access_chain_base})); + operands.push_back( + dbg_declare_inst->GetInOperand(kSpvDebugDeclareVarInIdx + 1)); + + for (uint32_t i = kSpvAccessChainBaseInIdx + 1; + i < access_chain->NumInOperands(); ++i) { + operands.push_back(access_chain->GetInOperand(i)); + } + + for (uint32_t i = kSpvDebugDeclareVarInIdx + 2; + i < dbg_declare_inst->NumInOperands(); ++i) { + operands.push_back(dbg_declare_inst->GetInOperand(i)); + } + + dbg_declare_inst->SetInOperands(std::move(operands)); + } while (true); +} + } // namespace opt } // namespace spvtools
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h index 1c9d60e..7bea31d 100644 --- a/source/opt/inline_pass.h +++ b/source/opt/inline_pass.h
@@ -150,6 +150,12 @@ // Initialize state for optimization of |module| void InitializeInline(); + // Fixes invalid debug declare functions in `func` that were caused by + // inlining. This function cannot be called while in the middle of inlining + // because it needs to be able to find the instructions that define an + // id. + void FixDebugDeclares(Function* func); + // Map from function's result id to function. std::unordered_map<uint32_t, Function*> id2function_; @@ -241,6 +247,11 @@ // structural dominance. void UpdateSingleBlockLoopContinueTarget( uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); + + // Replaces the `var` operand of `dbg_declare_inst` and updates the indexes + // accordingly, if it is the id of an access chain in `access_chains`. + void FixDebugDeclare(Instruction* dbg_declare_inst, + const std::map<uint32_t, Instruction*>& access_chains); }; } // namespace opt
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index aa4ae26..346eed6 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp
@@ -32,7 +32,7 @@ constexpr uint32_t kVariableStorageClassIndex = 0; constexpr uint32_t kTypeImageSampledIndex = 5; -// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo // extension instructions. constexpr uint32_t kExtInstSetIdInIdx = 0; constexpr uint32_t kExtInstInstructionInIdx = 1; @@ -168,7 +168,13 @@ 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()); + if (i.IsDebugLineInst()) { + uint32_t new_id = c->TakeNextId(); + if (new_id == 0) { + return nullptr; + } + i.SetResultId(new_id); + } } clone->dbg_scope_ = dbg_scope_; return clone; @@ -246,12 +252,12 @@ switch (base_inst->opcode()) { case spv::Op::OpAccessChain: case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpUntypedAccessChainKHR: case spv::Op::OpPtrAccessChain: case spv::Op::OpInBoundsPtrAccessChain: case spv::Op::OpImageTexelPointer: case spv::Op::OpCopyObject: - // All of these instructions have the base pointer use a base pointer - // in in-operand 0. + // All of these instructions have their base pointer in in-operand 0. base = base_inst->GetSingleWordInOperand(0); base_inst = context()->get_def_use_mgr()->GetDef(base); break; @@ -547,45 +553,57 @@ clear_dbg_line_insts(); } -void Instruction::UpdateDebugInfoFrom(const Instruction* from) { - if (from == nullptr) return; +bool Instruction::UpdateDebugInfoFrom(const Instruction* from, + const Instruction* line) { + if (from == nullptr) return true; ClearDbgLineInsts(); - if (!from->dbg_line_insts().empty()) - AddDebugLine(&from->dbg_line_insts().back()); + const Instruction* fromLine = line != nullptr ? line : from; + if (!fromLine->dbg_line_insts().empty()) { + if (!AddDebugLine(&fromLine->dbg_line_insts().back())) { + return false; + } + } SetDebugScope(from->GetDebugScope()); if (!IsLineInst() && context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { context()->get_debug_info_mgr()->AnalyzeDebugInst(this); } + return true; } -void Instruction::AddDebugLine(const Instruction* inst) { +bool 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 (inst->IsDebugLineInst()) { + uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) { + return false; + } + dbg_line_insts_.back().SetResultId(new_id); + } if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back()); + return true; } bool Instruction::IsDebugLineInst() const { - NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); - return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) || - (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine)); + NonSemanticShaderDebugInfoInstructions ext_opt = GetShaderDebugOpcode(); + return ((ext_opt == NonSemanticShaderDebugInfoDebugLine) || + (ext_opt == NonSemanticShaderDebugInfoDebugNoLine)); } bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); } bool Instruction::IsLine() const { if (opcode() == spv::Op::OpLine) return true; - NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); - return ext_opt == NonSemanticShaderDebugInfo100DebugLine; + NonSemanticShaderDebugInfoInstructions ext_opt = GetShaderDebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfoDebugLine; } bool Instruction::IsNoLine() const { if (opcode() == spv::Op::OpNoLine) return true; - NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); - return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine; + NonSemanticShaderDebugInfoInstructions ext_opt = GetShaderDebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfoDebugNoLine; } Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) { @@ -656,7 +674,8 @@ } OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { - if (opcode() != spv::Op::OpExtInst) { + if (opcode() != spv::Op::OpExtInst && + opcode() != spv::Op::OpExtInstWithForwardRefsKHR) { return OpenCLDebugInfo100InstructionsMax; } @@ -673,38 +692,40 @@ GetSingleWordInOperand(kExtInstInstructionInIdx)); } -NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() +NonSemanticShaderDebugInfoInstructions Instruction::GetShaderDebugOpcode() const { - if (opcode() != spv::Op::OpExtInst) { - return NonSemanticShaderDebugInfo100InstructionsMax; + if (opcode() != spv::Op::OpExtInst && + opcode() != spv::Op::OpExtInstWithForwardRefsKHR) { + return NonSemanticShaderDebugInfoInstructionsMax; } - if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { - return NonSemanticShaderDebugInfo100InstructionsMax; + if (!context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo()) { + return NonSemanticShaderDebugInfoInstructionsMax; } if (GetSingleWordInOperand(kExtInstSetIdInIdx) != - context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { - return NonSemanticShaderDebugInfo100InstructionsMax; + context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo()) { + return NonSemanticShaderDebugInfoInstructionsMax; } uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx); - if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) { - return NonSemanticShaderDebugInfo100InstructionsMax; + if (opcode >= NonSemanticShaderDebugInfoInstructionsMax) { + return NonSemanticShaderDebugInfoInstructionsMax; } - return NonSemanticShaderDebugInfo100Instructions(opcode); + return NonSemanticShaderDebugInfoInstructions(opcode); } CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { - if (opcode() != spv::Op::OpExtInst) { + if (opcode() != spv::Op::OpExtInst && + opcode() != spv::Op::OpExtInstWithForwardRefsKHR) { return CommonDebugInfoInstructionsMax; } const uint32_t opencl_set_id = context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); const uint32_t shader_set_id = - context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo(); if (!opencl_set_id && !shader_set_id) { return CommonDebugInfoInstructionsMax; @@ -769,7 +790,7 @@ // Even if the type of the instruction is foldable, its operands may not be // foldable (e.g., comparisons of 64bit types). Check that all operand types // are foldable before accepting the instruction. - return WhileEachInOperand([&folder, this](const uint32_t* op_id) { + return WhileEachInId([&folder, this](const uint32_t* op_id) { Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id); Instruction* def_inst_type = context()->get_def_use_mgr()->GetDef(def_inst->type_id()); @@ -791,7 +812,7 @@ // Even if the type of the instruction is foldable, its operands may not be // foldable (e.g., comparisons of 64bit types). Check that all operand types // are foldable before accepting the instruction. - return WhileEachInOperand([&folder, this](const uint32_t* op_id) { + return WhileEachInId([&folder, this](const uint32_t* op_id) { Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id); Instruction* def_inst_type = context()->get_def_use_mgr()->GetDef(def_inst->type_id()); @@ -1034,6 +1055,12 @@ return true; } + if (IsNonSemanticInstruction() && + (GetShaderDebugOpcode() == NonSemanticShaderDebugInfoDebugDeclare || + GetShaderDebugOpcode() == NonSemanticShaderDebugInfoDebugValue)) { + return true; + } + switch (opcode()) { case spv::Op::OpDPdx: case spv::Op::OpDPdy:
diff --git a/source/opt/instruction.h b/source/opt/instruction.h index c2617fb..9ab9f0e 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h
@@ -22,7 +22,6 @@ #include <utility> #include <vector> -#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/binary.h" #include "source/common_debug_info.h" @@ -35,6 +34,7 @@ #include "source/util/small_vector.h" #include "source/util/string_utils.h" #include "spirv-tools/libspirv.h" +#include "spirv/unified1/NonSemanticShaderDebugInfo.h" constexpr uint32_t kNoDebugScope = 0; constexpr uint32_t kNoInlinedAt = 0; @@ -318,19 +318,19 @@ 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); + bool 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 + // Return true if NSDI:Debug[No]Line bool IsDebugLineInst() const; - // Return true if Op[No]Line or Shader100:Debug[No]Line + // Return true if Op[No]Line or NSDI:Debug[No]Line bool IsLineInst() const; - // Return true if OpLine or Shader100:DebugLine + // Return true if OpLine or NSDI:DebugLine bool IsLine() const; - // Return true if OpNoLine or Shader100:DebugNoLine + // Return true if OpNoLine or NSDI:DebugNoLine bool IsNoLine() const; inline uint32_t GetDebugInlinedAt() const { return dbg_scope_.GetInlinedAt(); @@ -338,7 +338,8 @@ // Updates lexical scope of DebugScope and OpLine. void UpdateLexicalScope(uint32_t scope); // Updates OpLine and DebugScope based on the information of |from|. - void UpdateDebugInfoFrom(const Instruction* from); + bool UpdateDebugInfoFrom(const Instruction* from, + const Instruction* line = nullptr); // Remove the |index|-th operand void RemoveOperand(uint32_t index) { operands_.erase(operands_.begin() + index); @@ -580,13 +581,13 @@ // OpenCLDebugInfo100InstructionsMax. OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; - // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If - // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return - // NonSemanticShaderDebugInfo100InstructionsMax. - NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const; + // Returns debug opcode of a NonSemantic.Shader.DebugInfo instruction. If + // it is not a NonSemantic.Shader.DebugInfo instruction, just return + // NonSemanticShaderDebugInfoInstructionsMax. + NonSemanticShaderDebugInfoInstructions GetShaderDebugOpcode() const; // Returns debug opcode of an OpenCL.100.DebugInfo or - // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we + // NonSemantic.Shader.DebugInfo instruction. Since these overlap, we // return the OpenCLDebugInfo code CommonDebugInfoInstructions GetCommonDebugOpcode() const; @@ -595,10 +596,9 @@ return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; } - // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction. - bool IsShader100DebugInstr() const { - return GetShader100DebugOpcode() != - NonSemanticShaderDebugInfo100InstructionsMax; + // Returns true if it is a NonSemantic.Shader.DebugInfo instruction. + bool IsShaderDebugInstr() const { + return GetShaderDebugOpcode() != NonSemanticShaderDebugInfoInstructionsMax; } bool IsCommonDebugInstr() const { return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax;
diff --git a/source/opt/interface_var_sroa.cpp b/source/opt/interface_var_sroa.cpp index 08477cb..8c0b91f 100644 --- a/source/opt/interface_var_sroa.cpp +++ b/source/opt/interface_var_sroa.cpp
@@ -239,28 +239,34 @@ }); } -bool InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars( +Pass::Status +InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars( Instruction* interface_var, Instruction* interface_var_type, uint32_t location, uint32_t component, uint32_t extra_array_length) { - NestedCompositeComponents scalar_interface_vars = + std::optional<NestedCompositeComponents> scalar_interface_vars = CreateScalarInterfaceVarsForReplacement(interface_var_type, GetStorageClass(interface_var), extra_array_length); - AddLocationAndComponentDecorations(scalar_interface_vars, &location, + if (!scalar_interface_vars) { + return Status::Failure; + } + + AddLocationAndComponentDecorations(*scalar_interface_vars, &location, component); KillLocationAndComponentDecorations(interface_var->result_id()); - if (!ReplaceInterfaceVarWith(interface_var, extra_array_length, - scalar_interface_vars)) { - return false; + Status status = ReplaceInterfaceVarWith(interface_var, extra_array_length, + *scalar_interface_vars); + if (status == Status::Failure) { + return status; } context()->KillInst(interface_var); - return true; + return status; } -bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith( +Pass::Status InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith( Instruction* interface_var, uint32_t extra_array_length, const NestedCompositeComponents& scalar_interface_vars) { std::vector<Instruction*> users; @@ -276,21 +282,24 @@ // interface variable. for (uint32_t index = 0; index < extra_array_length; ++index) { std::unordered_map<Instruction*, Instruction*> loads_to_component_values; - if (!ReplaceComponentsOfInterfaceVarWith( - interface_var, users, scalar_interface_vars, - interface_var_component_indices, &index, - &loads_to_component_values, - &loads_for_access_chain_to_composites)) { - return false; + Status status = ReplaceComponentsOfInterfaceVarWith( + interface_var, users, scalar_interface_vars, + interface_var_component_indices, &index, &loads_to_component_values, + &loads_for_access_chain_to_composites); + if (status == Status::Failure) { + return Status::Failure; } AddComponentsToCompositesForLoads(loads_to_component_values, &loads_to_composites, 0); } - } else if (!ReplaceComponentsOfInterfaceVarWith( - interface_var, users, scalar_interface_vars, - interface_var_component_indices, nullptr, &loads_to_composites, - &loads_for_access_chain_to_composites)) { - return false; + } else { + Status status = ReplaceComponentsOfInterfaceVarWith( + interface_var, users, scalar_interface_vars, + interface_var_component_indices, nullptr, &loads_to_composites, + &loads_for_access_chain_to_composites); + if (status == Status::Failure) { + return Status::Failure; + } } ReplaceLoadWithCompositeConstruct(context(), loads_to_composites); @@ -298,7 +307,7 @@ loads_for_access_chain_to_composites); KillInstructionsAndUsers(users); - return true; + return Status::SuccessWithChange; } void InterfaceVariableScalarReplacement::AddLocationAndComponentDecorations( @@ -318,7 +327,8 @@ } } -bool InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith( +Pass::Status +InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith( Instruction* interface_var, const std::vector<Instruction*>& interface_var_users, const NestedCompositeComponents& scalar_interface_vars, @@ -329,15 +339,16 @@ loads_for_access_chain_to_composites) { if (!scalar_interface_vars.HasMultipleComponents()) { for (Instruction* interface_var_user : interface_var_users) { - if (!ReplaceComponentOfInterfaceVarWith( - interface_var, interface_var_user, - scalar_interface_vars.GetComponentVariable(), - interface_var_component_indices, extra_array_index, - loads_to_composites, loads_for_access_chain_to_composites)) { - return false; + Status status = ReplaceComponentOfInterfaceVarWith( + interface_var, interface_var_user, + scalar_interface_vars.GetComponentVariable(), + interface_var_component_indices, extra_array_index, + loads_to_composites, loads_for_access_chain_to_composites); + if (status == Status::Failure) { + return Status::Failure; } } - return true; + return Status::SuccessWithChange; } return ReplaceMultipleComponentsOfInterfaceVarWith( interface_var, interface_var_users, scalar_interface_vars.GetComponents(), @@ -345,27 +356,28 @@ loads_for_access_chain_to_composites); } -bool InterfaceVariableScalarReplacement:: - ReplaceMultipleComponentsOfInterfaceVarWith( - Instruction* interface_var, - const std::vector<Instruction*>& interface_var_users, - const std::vector<NestedCompositeComponents>& components, - std::vector<uint32_t>& interface_var_component_indices, - const uint32_t* extra_array_index, - std::unordered_map<Instruction*, Instruction*>* loads_to_composites, - std::unordered_map<Instruction*, Instruction*>* - loads_for_access_chain_to_composites) { +Pass::Status +InterfaceVariableScalarReplacement::ReplaceMultipleComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector<Instruction*>& interface_var_users, + const std::vector<NestedCompositeComponents>& components, + std::vector<uint32_t>& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map<Instruction*, Instruction*>* loads_to_composites, + std::unordered_map<Instruction*, Instruction*>* + loads_for_access_chain_to_composites) { for (uint32_t i = 0; i < components.size(); ++i) { interface_var_component_indices.push_back(i); std::unordered_map<Instruction*, Instruction*> loads_to_component_values; std::unordered_map<Instruction*, Instruction*> loads_for_access_chain_to_component_values; - if (!ReplaceComponentsOfInterfaceVarWith( - interface_var, interface_var_users, components[i], - interface_var_component_indices, extra_array_index, - &loads_to_component_values, - &loads_for_access_chain_to_component_values)) { - return false; + Status status = ReplaceComponentsOfInterfaceVarWith( + interface_var, interface_var_users, components[i], + interface_var_component_indices, extra_array_index, + &loads_to_component_values, + &loads_for_access_chain_to_component_values); + if (status == Status::Failure) { + return Status::Failure; } interface_var_component_indices.pop_back(); @@ -378,10 +390,11 @@ AddComponentsToCompositesForLoads(loads_to_component_values, loads_to_composites, depth_to_component); } - return true; + return Status::SuccessWithChange; } -bool InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith( +Pass::Status +InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith( Instruction* interface_var, Instruction* interface_var_user, Instruction* scalar_var, const std::vector<uint32_t>& interface_var_component_indices, @@ -395,42 +408,49 @@ StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices, scalar_var, extra_array_index, interface_var_user); - return true; + return Status::SuccessWithChange; } if (opcode == spv::Op::OpLoad) { Instruction* scalar_load = LoadScalarVar(scalar_var, extra_array_index, interface_var_user); + if (scalar_load == nullptr) { + return Status::Failure; + } loads_to_component_values->insert({interface_var_user, scalar_load}); - return true; + return Status::SuccessWithChange; } // Copy OpName and annotation instructions only once. Therefore, we create // them only for the first element of the extra array. - if (extra_array_index && *extra_array_index != 0) return true; + if (extra_array_index && *extra_array_index != 0) + return Status::SuccessWithChange; if (opcode == spv::Op::OpDecorateId || opcode == spv::Op::OpDecorateString || opcode == spv::Op::OpDecorate) { CloneAnnotationForVariable(interface_var_user, scalar_var->result_id()); - return true; + return Status::SuccessWithChange; } if (opcode == spv::Op::OpName) { std::unique_ptr<Instruction> new_inst(interface_var_user->Clone(context())); new_inst->SetInOperand(0, {scalar_var->result_id()}); context()->AddDebug2Inst(std::move(new_inst)); - return true; + return Status::SuccessWithChange; } if (opcode == spv::Op::OpEntryPoint) { - return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user, - scalar_var->result_id()); + if (ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user, + scalar_var->result_id())) { + return Status::SuccessWithChange; + } + return Status::Failure; } if (opcode == spv::Op::OpAccessChain) { ReplaceAccessChainWith(interface_var_user, interface_var_component_indices, scalar_var, loads_for_access_chain_to_component_values); - return true; + return Status::SuccessWithChange; } std::string message("Unhandled instruction"); @@ -440,7 +460,7 @@ "\nfor interface variable scalar replacement\n " + interface_var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); - return false; + return Status::Failure; } void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain( @@ -470,10 +490,14 @@ uint32_t ptr_type_id = GetPointerType(*component_type_id, GetStorageClass(var)); - std::unique_ptr<Instruction> new_access_chain(new Instruction( - context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(), - std::initializer_list<Operand>{ - {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + uint32_t new_id = TakeNextId(); + if (new_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_access_chain( + new Instruction(context(), spv::Op::OpAccessChain, ptr_type_id, new_id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); for (uint32_t index_id : index_ids) { new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}}); } @@ -490,12 +514,16 @@ uint32_t ptr_type_id = GetPointerType(component_type_id, GetStorageClass(var)); uint32_t index_id = context()->get_constant_mgr()->GetUIntConstId(index); - std::unique_ptr<Instruction> new_access_chain(new Instruction( - context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(), - std::initializer_list<Operand>{ - {SPV_OPERAND_TYPE_ID, {var->result_id()}}, - {SPV_OPERAND_TYPE_ID, {index_id}}, - })); + uint32_t new_id = TakeNextId(); + if (new_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_access_chain( + new Instruction(context(), spv::Op::OpAccessChain, ptr_type_id, new_id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_ID, {index_id}}, + })); Instruction* inst = new_access_chain.get(); context()->get_def_use_mgr()->AnalyzeInstDefUse(inst); insert_before->InsertBefore(std::move(new_access_chain)); @@ -617,6 +645,9 @@ component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, *extra_array_index, insert_before); + if (ptr == nullptr) { + return; + } } StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, @@ -635,6 +666,9 @@ component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, *extra_array_index, insert_before); + if (ptr == nullptr) { + return nullptr; + } } return CreateLoad(component_type_id, ptr, insert_before); @@ -642,8 +676,12 @@ Instruction* InterfaceVariableScalarReplacement::CreateLoad( uint32_t type_id, Instruction* ptr, Instruction* insert_before) { + uint32_t new_id = TakeNextId(); + if (new_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> load( - new Instruction(context(), spv::Op::OpLoad, type_id, TakeNextId(), + new Instruction(context(), spv::Op::OpLoad, type_id, new_id, std::initializer_list<Operand>{ {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}})); Instruction* load_inst = load.get(); @@ -658,6 +696,9 @@ const uint32_t* extra_array_index, Instruction* insert_before) { std::unique_ptr<Instruction> composite_extract(CreateCompositeExtract( component_type_id, value_id, component_indices, extra_array_index)); + if (composite_extract == nullptr) { + return; + } std::unique_ptr<Instruction> new_store( new Instruction(context(), spv::Op::OpStore)); @@ -677,6 +718,9 @@ uint32_t type_id, uint32_t composite_id, const std::vector<uint32_t>& indexes, const uint32_t* extra_first_index) { uint32_t component_id = TakeNextId(); + if (component_id == 0) { + return nullptr; + } Instruction* composite_extract = new Instruction( context(), spv::Op::OpCompositeExtract, type_id, component_id, std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {composite_id}}}); @@ -716,6 +760,9 @@ if (!indexes.empty()) { ptr = CreateAccessChainToVar(component_type_id, var, indexes, insert_before, &component_type_id); + if (ptr == nullptr) { + return nullptr; + } } return CreateLoad(component_type_id, ptr, insert_before); @@ -730,7 +777,10 @@ type_id = GetComponentTypeOfArrayMatrix(def_use_mgr, load->type_id(), depth_to_component); } - uint32_t new_id = context()->TakeNextId(); + uint32_t new_id = TakeNextId(); + if (new_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> new_composite_construct(new Instruction( context(), spv::Op::OpCompositeConstruct, type_id, new_id, {})); Instruction* composite_construct = new_composite_construct.get(); @@ -767,6 +817,10 @@ if (itr == loads_to_composites->end()) { composite_construct = CreateCompositeConstructForComponentOfLoad(load, depth_to_component); + if (composite_construct == nullptr) { + assert(false && "Could not create composite construct"); + return; + } loads_to_composites->insert({load, composite_construct}); } else { composite_construct = itr->second; @@ -795,7 +849,7 @@ return context()->get_type_mgr()->GetTypeInstruction(&ptr_type); } -InterfaceVariableScalarReplacement::NestedCompositeComponents +std::optional<InterfaceVariableScalarReplacement::NestedCompositeComponents> InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray( Instruction* interface_var_type, spv::StorageClass storage_class, uint32_t extra_array_length) { @@ -807,16 +861,19 @@ NestedCompositeComponents scalar_vars; while (array_length > 0) { - NestedCompositeComponents scalar_vars_for_element = + std::optional<NestedCompositeComponents> scalar_vars_for_element = CreateScalarInterfaceVarsForReplacement(elem_type, storage_class, extra_array_length); - scalar_vars.AddComponent(scalar_vars_for_element); + if (!scalar_vars_for_element) { + return std::nullopt; + } + scalar_vars.AddComponent(*scalar_vars_for_element); --array_length; } return scalar_vars; } -InterfaceVariableScalarReplacement::NestedCompositeComponents +std::optional<InterfaceVariableScalarReplacement::NestedCompositeComponents> InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix( Instruction* interface_var_type, spv::StorageClass storage_class, uint32_t extra_array_length) { @@ -830,16 +887,19 @@ NestedCompositeComponents scalar_vars; while (column_count > 0) { - NestedCompositeComponents scalar_vars_for_column = + std::optional<NestedCompositeComponents> scalar_vars_for_column = CreateScalarInterfaceVarsForReplacement(column_type, storage_class, extra_array_length); - scalar_vars.AddComponent(scalar_vars_for_column); + if (!scalar_vars_for_column) { + return std::nullopt; + } + scalar_vars.AddComponent(*scalar_vars_for_column); --column_count; } return scalar_vars; } -InterfaceVariableScalarReplacement::NestedCompositeComponents +std::optional<InterfaceVariableScalarReplacement::NestedCompositeComponents> InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement( Instruction* interface_var_type, spv::StorageClass storage_class, uint32_t extra_array_length) { @@ -864,6 +924,9 @@ uint32_t ptr_type_id = context()->get_type_mgr()->FindPointerToType(type_id, storage_class); uint32_t id = TakeNextId(); + if (id == 0) { + return std::nullopt; + } std::unique_ptr<Instruction> variable( new Instruction(context(), spv::Op::OpVariable, ptr_type_id, id, std::initializer_list<Operand>{ @@ -953,9 +1016,9 @@ continue; } - if (!ReplaceInterfaceVariableWithScalars(interface_var, interface_var_type, - location, component, - extra_array_length)) { + if (ReplaceInterfaceVariableWithScalars( + interface_var, interface_var_type, location, component, + extra_array_length) == Pass::Status::Failure) { return Pass::Status::Failure; } status = Pass::Status::SuccessWithChange;
diff --git a/source/opt/interface_var_sroa.h b/source/opt/interface_var_sroa.h index 45ed371..743cdfb 100644 --- a/source/opt/interface_var_sroa.h +++ b/source/opt/interface_var_sroa.h
@@ -15,6 +15,7 @@ #ifndef SOURCE_OPT_INTERFACE_VAR_SROA_H_ #define SOURCE_OPT_INTERFACE_VAR_SROA_H_ +#include <optional> #include <unordered_set> #include "source/opt/pass.h" @@ -100,25 +101,26 @@ // If |extra_array_length| is 0, it means |interface_var| has a Patch // decoration. Otherwise, |extra_array_length| denotes the length of the extra // array of |interface_var|. - bool ReplaceInterfaceVariableWithScalars(Instruction* interface_var, - Instruction* interface_var_type, - uint32_t location, - uint32_t component, - uint32_t extra_array_length); + Status ReplaceInterfaceVariableWithScalars(Instruction* interface_var, + Instruction* interface_var_type, + uint32_t location, + uint32_t component, + uint32_t extra_array_length); // Creates scalar variables with the storage classe |storage_class| to replace // an interface variable whose type is |interface_var_type|. If // |extra_array_length| is not zero, adds the extra arrayness to the created // scalar variables. - NestedCompositeComponents CreateScalarInterfaceVarsForReplacement( - Instruction* interface_var_type, spv::StorageClass storage_class, - uint32_t extra_array_length); + std::optional<NestedCompositeComponents> + CreateScalarInterfaceVarsForReplacement(Instruction* interface_var_type, + spv::StorageClass storage_class, + uint32_t extra_array_length); // Creates scalar variables with the storage classe |storage_class| to replace // the interface variable whose type is OpTypeArray |interface_var_type| with. // If |extra_array_length| is not zero, adds the extra arrayness to all the // scalar variables. - NestedCompositeComponents CreateScalarInterfaceVarsForArray( + std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForArray( Instruction* interface_var_type, spv::StorageClass storage_class, uint32_t extra_array_length); @@ -126,7 +128,7 @@ // the interface variable whose type is OpTypeMatrix |interface_var_type| // with. If |extra_array_length| is not zero, adds the extra arrayness to all // the scalar variables. - NestedCompositeComponents CreateScalarInterfaceVarsForMatrix( + std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForMatrix( Instruction* interface_var_type, spv::StorageClass storage_class, uint32_t extra_array_length); @@ -142,7 +144,7 @@ // |extra_arrayness| is the extra arrayness of the interface variable. // |scalar_interface_vars| contains the nested variables to replace the // interface variable with. - bool ReplaceInterfaceVarWith( + Status ReplaceInterfaceVarWith( Instruction* interface_var, uint32_t extra_arrayness, const NestedCompositeComponents& scalar_interface_vars); @@ -155,7 +157,7 @@ // construct instructions to be replaced with load instructions of access // chain instructions in |interface_var_users| via // |loads_for_access_chain_to_composites|. - bool ReplaceComponentsOfInterfaceVarWith( + Status ReplaceComponentsOfInterfaceVarWith( Instruction* interface_var, const std::vector<Instruction*>& interface_var_users, const NestedCompositeComponents& scalar_interface_vars, @@ -174,7 +176,7 @@ // via |loads_to_composites|. Returns composite construct instructions to be // replaced with load instructions of access chain instructions in // |interface_var_users| via |loads_for_access_chain_to_composites|. - bool ReplaceMultipleComponentsOfInterfaceVarWith( + Status ReplaceMultipleComponentsOfInterfaceVarWith( Instruction* interface_var, const std::vector<Instruction*>& interface_var_users, const std::vector<NestedCompositeComponents>& components, @@ -192,7 +194,7 @@ // |loads_to_component_values|. If |interface_var_user| is an access chain, // returns the component value for loads of |interface_var_user| via // |loads_for_access_chain_to_component_values|. - bool ReplaceComponentOfInterfaceVarWith( + Status ReplaceComponentOfInterfaceVarWith( Instruction* interface_var, Instruction* interface_var_user, Instruction* scalar_var, const std::vector<uint32_t>& interface_var_component_indices, @@ -389,6 +391,9 @@ // A set of interface variables without the extra arrayness for any of the // entry points. std::unordered_set<Instruction*> vars_without_extra_arrayness; + + // Returns the next available id, or 0 if the id overflows. + uint32_t TakeNextId() { return context()->TakeNextId(); } }; } // namespace opt
diff --git a/source/opt/invocation_interlock_placement_pass.cpp b/source/opt/invocation_interlock_placement_pass.cpp index 642e2d2..b31697c 100644 --- a/source/opt/invocation_interlock_placement_pass.cpp +++ b/source/opt/invocation_interlock_placement_pass.cpp
@@ -26,10 +26,10 @@ #include <vector> #include "source/enum_set.h" -#include "source/enum_string_mapping.h" #include "source/opt/ir_context.h" #include "source/opt/reflect.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/util/string_utils.h" namespace spvtools { @@ -294,8 +294,12 @@ BasicBlock* InvocationInterlockPlacementPass::splitEdge(BasicBlock* block, uint32_t succ_id) { // Create a new block to replace the critical edge. + uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) { + return nullptr; + } auto new_succ_temp = MakeUnique<BasicBlock>( - MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, TakeNextId(), + MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, new_id, std::initializer_list<Operand>{})); auto* new_succ = new_succ_temp.get(); @@ -325,7 +329,7 @@ return new_succ; } -bool InvocationInterlockPlacementPass::placeInstructionsForEdge( +Pass::Status InvocationInterlockPlacementPass::placeInstructionsForEdge( BasicBlock* block, uint32_t next_id, BlockSet& inside, BlockSet& previous_inside, spv::Op opcode, bool reverse_cfg) { bool modified = false; @@ -372,31 +376,45 @@ new_branch = splitEdge(cfg()->block(next_id), block->id()); } + if (!new_branch) { + return Status::Failure; + } + auto inst = new Instruction(context(), opcode); inst->InsertBefore(&*new_branch->tail()); } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -bool InvocationInterlockPlacementPass::placeInstructions(BasicBlock* block) { - bool modified = false; +Pass::Status InvocationInterlockPlacementPass::placeInstructions( + BasicBlock* block) { + Status status = Status::SuccessWithoutChange; - block->ForEachSuccessorLabel([this, block, &modified](uint32_t succ_id) { - modified |= placeInstructionsForEdge( + block->ForEachSuccessorLabel([this, block, &status](uint32_t succ_id) { + if (status == Status::Failure) { + return; + } + Status edge_status = placeInstructionsForEdge( block, succ_id, after_begin_, predecessors_after_begin_, spv::Op::OpBeginInvocationInterlockEXT, /* reverse_cfg= */ true); - modified |= placeInstructionsForEdge(cfg()->block(succ_id), block->id(), - before_end_, successors_before_end_, - spv::Op::OpEndInvocationInterlockEXT, - /* reverse_cfg= */ false); + status = CombineStatus(status, edge_status); + if (status == Status::Failure) { + return; + } + + edge_status = placeInstructionsForEdge(cfg()->block(succ_id), block->id(), + before_end_, successors_before_end_, + spv::Op::OpEndInvocationInterlockEXT, + /* reverse_cfg= */ false); + status = CombineStatus(status, edge_status); }); - return modified; + return status; } -bool InvocationInterlockPlacementPass::processFragmentShaderEntry( +Pass::Status InvocationInterlockPlacementPass::processFragmentShaderEntry( Function* entry_func) { bool modified = false; @@ -417,9 +435,15 @@ for (BasicBlock* block : original_blocks) { modified |= removeUnneededInstructions(block); - modified |= placeInstructions(block); + Status place_status = placeInstructions(block); + if (place_status == Status::Failure) { + return Status::Failure; + } + if (place_status == Status::SuccessWithChange) { + modified = true; + } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } bool InvocationInterlockPlacementPass::isFragmentShaderInterlockEnabled() { @@ -452,7 +476,7 @@ return Status::SuccessWithoutChange; } - bool modified = false; + Status status = Status::SuccessWithoutChange; std::unordered_set<Function*> entry_points; for (Instruction& entry_inst : context()->module()->entry_points()) { @@ -466,7 +490,9 @@ Function* func = &*fi; recordBeginOrEndInFunction(func); if (!entry_points.count(func) && extracted_functions_.count(func)) { - modified |= removeBeginAndEndInstructionsFromFunction(func); + if (removeBeginAndEndInstructionsFromFunction(func)) { + status = Status::SuccessWithChange; + } } } @@ -482,11 +508,14 @@ continue; } - modified |= processFragmentShaderEntry(entry_func); + Status frag_status = processFragmentShaderEntry(entry_func); + if (frag_status == Status::Failure) { + return Status::Failure; + } + status = CombineStatus(status, frag_status); } - return modified ? Pass::Status::SuccessWithChange - : Pass::Status::SuccessWithoutChange; + return status; } } // namespace opt
diff --git a/source/opt/invocation_interlock_placement_pass.h b/source/opt/invocation_interlock_placement_pass.h index 4e85be8..497af28 100644 --- a/source/opt/invocation_interlock_placement_pass.h +++ b/source/opt/invocation_interlock_placement_pass.h
@@ -120,14 +120,14 @@ // For the edge from block to next_id, places a begin or end instruction on // the edge, based on the direction we are walking the CFG, specified in // reverse_cfg. - bool placeInstructionsForEdge(BasicBlock* block, uint32_t next_id, - BlockSet& inside, BlockSet& previous_inside, - spv::Op opcode, bool reverse_cfg); + Status placeInstructionsForEdge(BasicBlock* block, uint32_t next_id, + BlockSet& inside, BlockSet& previous_inside, + spv::Op opcode, bool reverse_cfg); // Calls placeInstructionsForEdge for each edge in block. - bool placeInstructions(BasicBlock* block); + Status placeInstructions(BasicBlock* block); // Processes a single fragment shader entry function. - bool processFragmentShaderEntry(Function* entry_func); + Status processFragmentShaderEntry(Function* entry_func); // Returns whether the module has the SPV_EXT_fragment_shader_interlock // extension and one of the FragmentShader*InterlockEXT capabilities.
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h index f3e0afc..529bd8f 100644 --- a/source/opt/ir_builder.h +++ b/source/opt/ir_builder.h
@@ -15,6 +15,7 @@ #ifndef SOURCE_OPT_IR_BUILDER_H_ #define SOURCE_OPT_IR_BUILDER_H_ +#include <cassert> #include <limits> #include <memory> #include <utility> @@ -165,10 +166,14 @@ for (size_t i = 0; i < operands.size(); i++) { ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}}); } - // TODO(1841): Handle id overflow. - std::unique_ptr<Instruction> new_inst(new Instruction( - GetContext(), opcode, type_id, - result != 0 ? result : GetContext()->TakeNextId(), ops)); + if (result == 0) { + result = GetContext()->TakeNextId(); + if (result == 0) { + return nullptr; + } + } + std::unique_ptr<Instruction> new_inst( + new Instruction(GetContext(), opcode, type_id, result, ops)); return AddInstruction(std::move(new_inst)); } @@ -296,9 +301,12 @@ // The id |op1| is the left hand side of the operation. // The id |op2| is the right hand side of the operation. Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) { - // TODO(1841): Handle id overflow. + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> inst(new Instruction( - GetContext(), spv::Op::OpIAdd, type, GetContext()->TakeNextId(), + GetContext(), spv::Op::OpIAdd, type, result_id, {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); return AddInstruction(std::move(inst)); } @@ -310,9 +318,12 @@ Instruction* AddULessThan(uint32_t op1, uint32_t op2) { analysis::Bool bool_type; uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); - // TODO(1841): Handle id overflow. + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> inst(new Instruction( - GetContext(), spv::Op::OpULessThan, type, GetContext()->TakeNextId(), + GetContext(), spv::Op::OpULessThan, type, result_id, {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); return AddInstruction(std::move(inst)); } @@ -324,9 +335,12 @@ Instruction* AddSLessThan(uint32_t op1, uint32_t op2) { analysis::Bool bool_type; uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); - // TODO(1841): Handle id overflow. + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> inst(new Instruction( - GetContext(), spv::Op::OpSLessThan, type, GetContext()->TakeNextId(), + GetContext(), spv::Op::OpSLessThan, type, result_id, {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); return AddInstruction(std::move(inst)); } @@ -354,9 +368,12 @@ // bool) for |type|. Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value, uint32_t false_value) { - // TODO(1841): Handle id overflow. + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> select(new Instruction( - GetContext(), spv::Op::OpSelect, type, GetContext()->TakeNextId(), + GetContext(), spv::Op::OpSelect, type, result_id, std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}}, {SPV_OPERAND_TYPE_ID, {true_value}}, {SPV_OPERAND_TYPE_ID, {false_value}}})); @@ -380,10 +397,12 @@ ops.emplace_back(SPV_OPERAND_TYPE_ID, std::initializer_list<uint32_t>{id}); } - // TODO(1841): Handle id overflow. - std::unique_ptr<Instruction> construct( - new Instruction(GetContext(), spv::Op::OpCompositeConstruct, type, - GetContext()->TakeNextId(), ops)); + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> construct(new Instruction( + GetContext(), spv::Op::OpCompositeConstruct, type, result_id, ops)); return AddInstruction(std::move(construct)); } @@ -465,10 +484,12 @@ operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); } - // TODO(1841): Handle id overflow. - std::unique_ptr<Instruction> new_inst( - new Instruction(GetContext(), spv::Op::OpCompositeExtract, type, - GetContext()->TakeNextId(), operands)); + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_inst(new Instruction( + GetContext(), spv::Op::OpCompositeExtract, type, result_id, operands)); return AddInstruction(std::move(new_inst)); } @@ -480,8 +501,11 @@ return AddInstruction(std::move(select)); } - Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id, - std::vector<uint32_t> ids) { + Instruction* AddOpcodeAccessChain(spv::Op opcode, uint32_t type_id, + uint32_t base_ptr_id, + const std::vector<uint32_t>& ids) { + assert(opcode == spv::Op::OpAccessChain || + opcode == spv::Op::OpInBoundsAccessChain); std::vector<Operand> operands; operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); @@ -489,13 +513,26 @@ operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); } - // TODO(1841): Handle id overflow. + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } std::unique_ptr<Instruction> new_inst( - new Instruction(GetContext(), spv::Op::OpAccessChain, type_id, - GetContext()->TakeNextId(), operands)); + new Instruction(GetContext(), opcode, type_id, result_id, operands)); return AddInstruction(std::move(new_inst)); } + Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id, + const std::vector<uint32_t>& ids) { + return AddOpcodeAccessChain(spv::Op::OpAccessChain, type_id, base_ptr_id, + ids); + } + Instruction* AddInBoundsAccessChain(uint32_t type_id, uint32_t base_ptr_id, + const std::vector<uint32_t>& ids) { + return AddOpcodeAccessChain(spv::Op::OpInBoundsAccessChain, type_id, + base_ptr_id, ids); + } + Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id, uint32_t alignment = 0) { std::vector<Operand> operands; @@ -507,19 +544,36 @@ operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}}); } - // TODO(1841): Handle id overflow. - std::unique_ptr<Instruction> new_inst( - new Instruction(GetContext(), spv::Op::OpLoad, type_id, - GetContext()->TakeNextId(), operands)); + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_inst(new Instruction( + GetContext(), spv::Op::OpLoad, type_id, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddCopyObject(uint32_t type_id, uint32_t value_id) { + std::vector<Operand> operands{{SPV_OPERAND_TYPE_ID, {value_id}}}; + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_inst(new Instruction( + GetContext(), spv::Op::OpCopyObject, type_id, result_id, operands)); return AddInstruction(std::move(new_inst)); } Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) { std::vector<Operand> operands; - operands.push_back({SPV_OPERAND_TYPE_ID, {storage_class}}); - std::unique_ptr<Instruction> new_inst( - new Instruction(GetContext(), spv::Op::OpVariable, type_id, - GetContext()->TakeNextId(), operands)); + operands.push_back({SPV_OPERAND_TYPE_STORAGE_CLASS, {storage_class}}); + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_inst(new Instruction( + GetContext(), spv::Op::OpVariable, type_id, result_id, operands)); return AddInstruction(std::move(new_inst)); } @@ -572,6 +626,26 @@ return AddInstruction(std::move(new_inst)); } + Instruction* AddDecoration(uint32_t target_id, spv::Decoration d, + const std::vector<uint32_t>& literals) { + std::vector<Operand> operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {target_id}}); + operands.push_back({SPV_OPERAND_TYPE_DECORATION, {uint32_t(d)}}); + for (uint32_t literal : literals) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}}); + } + + std::unique_ptr<Instruction> new_inst( + new Instruction(GetContext(), spv::Op::OpDecorate, 0, 0, operands)); + // Decorations are annotation instructions. Add it via the IR context, + // so the decoration manager will be updated. + // Decorations don't belong to basic blocks, so there is no need + // to update the instruction to block mapping. + Instruction* result = new_inst.get(); + GetContext()->AddAnnotationInst(std::move(new_inst)); + return result; + } + Instruction* AddNaryExtendedInstruction( uint32_t result_type, uint32_t set, uint32_t instruction, const std::vector<uint32_t>& ext_operands) { @@ -593,6 +667,23 @@ return AddInstruction(std::move(new_inst)); } + Instruction* AddSampledImage(uint32_t sampled_image_type_id, + uint32_t image_id, uint32_t sampler_id) { + std::vector<Operand> operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {image_id}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {sampler_id}}); + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + + std::unique_ptr<Instruction> new_inst( + new Instruction(GetContext(), spv::Op::OpSampledImage, + sampled_image_type_id, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + // Inserts the new instruction before the insertion point. Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) { Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 1cf0d74..6a66d2b 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,8 +33,8 @@ constexpr int kEntryPointFunctionIdInIdx = 1; constexpr int kEntryPointExecutionModelInIdx = 0; -// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 -// extension instructions. +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo extension +// instructions. constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13; constexpr uint32_t kDebugGlobalVariableOperandVariableIndex = 11; } // namespace @@ -91,6 +93,9 @@ if (set & kAnalysisLiveness) { BuildLivenessManager(); } + if (set & kAnalysisIdToGraphMapping) { + BuildIdToGraphMapping(); + } } void IRContext::InvalidateAnalysesExceptFor( @@ -162,6 +167,9 @@ if (analyses_to_invalidate & kAnalysisDebugInfo) { debug_info_mgr_.reset(nullptr); } + if (analyses_to_invalidate & kAnalysisIdToGraphMapping) { + id_to_graph_.clear(); + } valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate); } @@ -175,6 +183,8 @@ KillOperandFromDebugInstructions(inst); + KillRelatedDebugScopes(inst); + if (AreAnalysesValid(kAnalysisDefUse)) { analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); def_use_mgr->ClearInst(inst); @@ -199,7 +209,9 @@ constant_mgr_->RemoveId(inst->result_id()); } if (inst->opcode() == spv::Op::OpCapability || - inst->opcode() == spv::Op::OpExtension) { + inst->opcode() == spv::Op::OpConditionalCapabilityINTEL || + inst->opcode() == spv::Op::OpExtension || + inst->opcode() == spv::Op::OpConditionalExtensionINTEL) { // We reset the feature manager, instead of updating it, because it is just // as much work. We would have to remove all capabilities implied by this // capability that are not also implied by the remaining OpCapability @@ -380,6 +392,7 @@ } } + return true; if (AreAnalysesValid(kAnalysisIdToFuncMapping)) { for (auto& fn : *module_) { if (id_to_func_[fn.result_id()] != &fn) { @@ -388,6 +401,14 @@ } } + if (AreAnalysesValid(kAnalysisIdToGraphMapping)) { + for (auto& g : module_->graphs()) { + if (id_to_graph_[g->DefInst().result_id()] != g.get()) { + return false; + } + } + } + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { for (auto& func : *module()) { for (auto& block : func) { @@ -396,8 +417,9 @@ return false; } return true; - })) + })) { return false; + } } } } @@ -512,6 +534,20 @@ } } +void IRContext::KillRelatedDebugScopes(Instruction* inst) { + // Extension has been fully unloaded, remove debug scope from every + // instruction. + if (inst->opcode() == spv::Op::OpExtInstImport) { + const std::string extension_name = inst->GetInOperand(0).AsString(); + if (extension_name.compare(0, 29, "NonSemantic.Shader.DebugInfo.") == 0 || + extension_name == "OpenCL.DebugInfo.100") { + module()->ForEachInst([](Instruction* child) { + child->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); + }); + } + } +} + void IRContext::AddCombinatorsForCapability(uint32_t capability) { spv::Capability cap = spv::Capability(capability); if (cap == spv::Capability::Shader) { @@ -537,11 +573,14 @@ (uint32_t)spv::Op::OpTypeAccelerationStructureKHR, (uint32_t)spv::Op::OpTypeRayQueryKHR, (uint32_t)spv::Op::OpTypeHitObjectNV, + (uint32_t)spv::Op::OpTypeHitObjectEXT, (uint32_t)spv::Op::OpTypeArray, (uint32_t)spv::Op::OpTypeRuntimeArray, + (uint32_t)spv::Op::OpTypeNodePayloadArrayAMDX, (uint32_t)spv::Op::OpTypeStruct, (uint32_t)spv::Op::OpTypeOpaque, (uint32_t)spv::Op::OpTypePointer, + (uint32_t)spv::Op::OpTypeUntypedPointerKHR, (uint32_t)spv::Op::OpTypeFunction, (uint32_t)spv::Op::OpTypeEvent, (uint32_t)spv::Op::OpTypeDeviceEvent, @@ -550,10 +589,12 @@ (uint32_t)spv::Op::OpTypePipe, (uint32_t)spv::Op::OpTypeForwardPointer, (uint32_t)spv::Op::OpVariable, + (uint32_t)spv::Op::OpUntypedVariableKHR, (uint32_t)spv::Op::OpImageTexelPointer, (uint32_t)spv::Op::OpLoad, (uint32_t)spv::Op::OpAccessChain, (uint32_t)spv::Op::OpInBoundsAccessChain, + (uint32_t)spv::Op::OpUntypedAccessChainKHR, (uint32_t)spv::Op::OpArrayLength, (uint32_t)spv::Op::OpVectorExtractDynamic, (uint32_t)spv::Op::OpVectorInsertDynamic, @@ -561,6 +602,7 @@ (uint32_t)spv::Op::OpCompositeConstruct, (uint32_t)spv::Op::OpCompositeExtract, (uint32_t)spv::Op::OpCompositeInsert, + (uint32_t)spv::Op::OpCopyLogical, (uint32_t)spv::Op::OpCopyObject, (uint32_t)spv::Op::OpTranspose, (uint32_t)spv::Op::OpSampledImage, @@ -905,11 +947,13 @@ return 0; } } + if (reg_type == nullptr) return 0; // Error + uint32_t type_id = type_mgr->GetTypeInstruction(reg_type); uint32_t varTyPtrId = type_mgr->FindPointerToType(type_id, spv::StorageClass::Input); - // TODO(1841): Handle id overflow. var_id = TakeNextId(); + if (var_id == 0) return 0; // Error std::unique_ptr<Instruction> newVarOp( new Instruction(this, spv::Op::OpVariable, varTyPtrId, var_id, {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 3857696..f4a69fc 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h
@@ -27,7 +27,6 @@ #include <vector> #include "source/assembly_grammar.h" -#include "source/enum_string_mapping.h" #include "source/opt/cfg.h" #include "source/opt/constants.h" #include "source/opt/debug_info_manager.h" @@ -44,6 +43,7 @@ #include "source/opt/struct_cfg_analysis.h" #include "source/opt/type_manager.h" #include "source/opt/value_number_table.h" +#include "source/table2.h" #include "source/util/make_unique.h" #include "source/util/string_utils.h" @@ -84,7 +84,8 @@ kAnalysisTypes = 1 << 15, kAnalysisDebugInfo = 1 << 16, kAnalysisLiveness = 1 << 17, - kAnalysisEnd = 1 << 18 + kAnalysisIdToGraphMapping = 1 << 18, + kAnalysisEnd = 1 << 19 }; using ProcessFunction = std::function<bool(Function*)>; @@ -109,7 +110,8 @@ id_to_name_(nullptr), max_id_bound_(kDefaultMaxIdBound), preserve_bindings_(false), - preserve_spec_constants_(false) { + preserve_spec_constants_(false), + id_overflow_(false) { SetContextMessageConsumer(syntax_context_, consumer_); module_->SetContext(this); } @@ -127,7 +129,8 @@ id_to_name_(nullptr), max_id_bound_(kDefaultMaxIdBound), preserve_bindings_(false), - preserve_spec_constants_(false) { + preserve_spec_constants_(false), + id_overflow_(false) { SetContextMessageConsumer(syntax_context_, consumer_); module_->SetContext(this); InitializeCombinators(); @@ -505,6 +508,9 @@ // Change operands of debug instruction to DebugInfoNone. void KillOperandFromDebugInstructions(Instruction* inst); + // Remove the debug scope from any instruction related to |inst|. + void KillRelatedDebugScopes(Instruction* inst); + // Returns the next unique id for use by an instruction. inline uint32_t TakeNextUniqueId() { assert(unique_id_ != std::numeric_limits<uint32_t>::max()); @@ -563,6 +569,7 @@ inline uint32_t TakeNextId() { uint32_t next_id = module()->TakeNextIdBound(); if (next_id == 0) { + id_overflow_ = true; if (consumer()) { std::string message = "ID overflow. Try running compact-ids."; consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); @@ -583,6 +590,13 @@ return next_id; } + // Returns true if an ID overflow has occurred since the last time the flag + // was cleared. + bool id_overflow() const { return id_overflow_; } + + // Clears the ID overflow flag. + void clear_id_overflow() { id_overflow_ = false; } + FeatureManager* get_feature_mgr() { if (!feature_mgr_.get()) { AnalyzeFeatures(); @@ -641,6 +655,23 @@ return GetFunction(inst->result_id()); } + // Returns the graph whose id is |id|, if one exists. Returns |nullptr| + // otherwise. + Graph* GetGraph(uint32_t id) { + if (!AreAnalysesValid(kAnalysisIdToGraphMapping)) { + BuildIdToGraphMapping(); + } + auto entry = id_to_graph_.find(id); + return (entry != id_to_graph_.end()) ? entry->second : nullptr; + } + + Graph* GetGraph(Instruction* inst) { + if (inst->opcode() != spv::Op::OpGraphARM) { + return nullptr; + } + return GetGraph(inst->result_id()); + } + // Add to |todo| all ids of functions called directly from |func|. void AddCalls(const Function* func, std::queue<uint32_t>* todo); @@ -719,6 +750,15 @@ valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping; } + // Builds the instruction-graph map for the whole module. + void BuildIdToGraphMapping() { + id_to_graph_.clear(); + for (auto& g : module_->graphs()) { + id_to_graph_[g->DefInst().result_id()] = g.get(); + } + valid_analyses_ = valid_analyses_ | kAnalysisIdToGraphMapping; + } + void BuildDecorationManager() { decoration_mgr_ = MakeUnique<analysis::DecorationManager>(module()); valid_analyses_ = valid_analyses_ | kAnalysisDecorations; @@ -872,6 +912,13 @@ // iterators to traverse instructions. std::unordered_map<uint32_t, Function*> id_to_func_; + // A map from ids to the graph they define. This mapping is + // built on-demand when GetGraph() is called. + // + // NOTE: Do not traverse this map. Ever. Use the graph iterators to + // traverse instructions. + std::unordered_map<uint32_t, Graph*> id_to_graph_; + // A bitset indicating which analyzes are currently valid. Analysis valid_analyses_; @@ -930,6 +977,9 @@ // Whether all specialization constants within |module_| // should be preserved. bool preserve_spec_constants_; + + // Set to true if TakeNextId() fails. + bool id_overflow_; }; inline IRContext::Analysis operator|(IRContext::Analysis lhs,
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index a785048..c0af91b 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp
@@ -46,10 +46,10 @@ 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; + const NonSemanticShaderDebugInfoInstructions ext_inst_key = + NonSemanticShaderDebugInfoInstructions(ext_inst_index); + return ext_inst_key == NonSemanticShaderDebugInfoDebugLine || + ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine; } bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { @@ -178,12 +178,48 @@ last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); last_line_inst_.reset(); dbg_line_info_.clear(); + } else if (opcode == spv::Op::OpGraphARM) { + if (graph_ != nullptr) { + Error(consumer_, src, loc, "graph inside graph"); + return false; + } + graph_ = MakeUnique<Graph>(std::move(spv_inst)); + } else if (opcode == spv::Op::OpGraphEndARM) { + if (graph_ == nullptr) { + Error(consumer_, src, loc, + "OpGraphEndARM without corresponding OpGraphARM"); + return false; + } + graph_->SetGraphEnd(std::move(spv_inst)); + module_->AddGraph(std::move(graph_)); + graph_ = nullptr; + } else if (opcode == spv::Op::OpGraphConstantARM) { + module_->AddGlobalValue(std::move(spv_inst)); + } else if (graph_ != nullptr) { + if (opcode == spv::Op::OpGraphInputARM) { + graph_->AddInput(std::move(spv_inst)); + } else if (opcode == spv::Op::OpGraphSetOutputARM) { + graph_->AddOutput(std::move(spv_inst)); + } else { + switch (opcode) { + case spv::Op::OpExtInst: + case spv::Op::OpCompositeExtract: + graph_->AddInstruction(std::move(spv_inst)); + break; + default: + Errorf(consumer_, src, loc, + "unhandled instruction (opcode %d) inside graph", opcode); + return false; + } + } } else { if (function_ == nullptr) { // Outside function definition SPIRV_ASSERT(consumer_, block_ == nullptr); - if (opcode == spv::Op::OpCapability) { + if (opcode == spv::Op::OpCapability || + opcode == spv::Op::OpConditionalCapabilityINTEL) { module_->AddCapability(std::move(spv_inst)); - } else if (opcode == spv::Op::OpExtension) { + } else if (opcode == spv::Op::OpExtension || + opcode == spv::Op::OpConditionalExtensionINTEL) { module_->AddExtension(std::move(spv_inst)); } else if (opcode == spv::Op::OpExtInstImport) { module_->AddExtInstImport(std::move(spv_inst)); @@ -193,6 +229,8 @@ module_->SetSampledImageAddressMode(std::move(spv_inst)); } else if (opcode == spv::Op::OpEntryPoint) { module_->AddEntryPoint(std::move(spv_inst)); + } else if (opcode == spv::Op::OpGraphEntryPointARM) { + module_->AddGraphEntryPoint(std::move(spv_inst)); } else if (opcode == spv::Op::OpExecutionMode || opcode == spv::Op::OpExecutionModeId) { module_->AddExecutionMode(std::move(spv_inst)); @@ -207,6 +245,7 @@ } else if (IsTypeInst(opcode)) { module_->AddType(std::move(spv_inst)); } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable || + opcode == spv::Op::OpUntypedVariableKHR || opcode == spv::Op::OpUndef) { module_->AddGlobalValue(std::move(spv_inst)); } else if (spvIsExtendedInstruction(opcode) && @@ -267,14 +306,14 @@ } } else if (inst->ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { - const NonSemanticShaderDebugInfo100Instructions ext_inst_key = - NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + const NonSemanticShaderDebugInfoInstructions ext_inst_key = + NonSemanticShaderDebugInfoInstructions(ext_inst_index); switch (ext_inst_key) { - case NonSemanticShaderDebugInfo100DebugDeclare: - case NonSemanticShaderDebugInfo100DebugValue: - case NonSemanticShaderDebugInfo100DebugScope: - case NonSemanticShaderDebugInfo100DebugNoScope: - case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { + case NonSemanticShaderDebugInfoDebugDeclare: + case NonSemanticShaderDebugInfoDebugValue: + case NonSemanticShaderDebugInfoDebugScope: + case NonSemanticShaderDebugInfoDebugNoScope: + case NonSemanticShaderDebugInfoDebugFunctionDefinition: { if (block_ == nullptr) { // Inside function but outside blocks Errorf(consumer_, src, loc, "Debug info extension instruction found inside function "
diff --git a/source/opt/ir_loader.h b/source/opt/ir_loader.h index 16bc2c7..a8d2119 100644 --- a/source/opt/ir_loader.h +++ b/source/opt/ir_loader.h
@@ -20,6 +20,7 @@ #include <vector> #include "source/opt/basic_block.h" +#include "source/opt/graph.h" #include "source/opt/instruction.h" #include "source/opt/module.h" #include "spirv-tools/libspirv.hpp" @@ -80,6 +81,8 @@ std::unique_ptr<Function> function_; // The current BasicBlock under construction. std::unique_ptr<BasicBlock> block_; + // The current Graph under construction. + std::unique_ptr<Graph> graph_; // Line related debug instructions accumulated thus far. std::vector<Instruction> dbg_line_info_; // If doing extra line tracking, this is the line instruction that should be
diff --git a/source/opt/legalize_multidim_array_pass.cpp b/source/opt/legalize_multidim_array_pass.cpp new file mode 100644 index 0000000..cd207b0 --- /dev/null +++ b/source/opt/legalize_multidim_array_pass.cpp
@@ -0,0 +1,275 @@ +// Copyright (c) 2026 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/legalize_multidim_array_pass.h" + +#include "source/opt/constants.h" +#include "source/opt/desc_sroa_util.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +Pass::Status LegalizeMultidimArrayPass::Process() { + std::vector<Instruction*> vars_to_legalize; + + for (auto& var : context()->types_values()) { + if (var.opcode() != spv::Op::OpVariable) continue; + if (!IsMultidimArrayOfResources(&var)) continue; + if (!CanLegalize(&var)) { + context()->EmitErrorMessage("Unable to legalize multidimensional array: ", + &var); + return Status::Failure; + } + vars_to_legalize.push_back(&var); + } + + if (vars_to_legalize.empty()) return Status::SuccessWithoutChange; + + for (auto* var : vars_to_legalize) { + uint32_t old_ptr_type_id = var->type_id(); + uint32_t new_ptr_type_id = FlattenArrayType(var); + if (new_ptr_type_id == 0) return Status::Failure; + if (!RewriteAccessChains(var, old_ptr_type_id)) return Status::Failure; + } + + return Status::SuccessWithChange; +} + +bool LegalizeMultidimArrayPass::IsMultidimArrayOfResources(Instruction* var) { + if (!descsroautil::IsDescriptorArray(context(), var)) return false; + + uint32_t type_id = var->type_id(); + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + uint32_t pointee_type_id = type_inst->GetSingleWordInOperand(1); + std::vector<uint32_t> dims; + uint32_t element_type_id = 0; + GetArrayDimensions(pointee_type_id, &dims, &element_type_id); + + return dims.size() > 1; +} + +void LegalizeMultidimArrayPass::GetArrayDimensions(uint32_t type_id, + std::vector<uint32_t>* dims, + uint32_t* element_type_id) { + assert(dims != nullptr && "dims cannot be null."); + dims->clear(); + + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + while (type_inst->opcode() == spv::Op::OpTypeArray) { + uint32_t length_id = type_inst->GetSingleWordInOperand(1); + Instruction* length_inst = context()->get_def_use_mgr()->GetDef(length_id); + // Assume OpConstant. According to the spec the length could also be an + // OpSpecConstantOp. However, DXC will not generate that type of code. The + // code to handle spec constants will be much more complicated. + assert(length_inst->opcode() == spv::Op::OpConstant); + uint32_t length = length_inst->GetSingleWordInOperand(0); + dims->push_back(length); + type_id = type_inst->GetSingleWordInOperand(0); + type_inst = context()->get_def_use_mgr()->GetDef(type_id); + } + *element_type_id = type_id; +} + +uint32_t LegalizeMultidimArrayPass::FlattenArrayType(Instruction* var) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* constant_mgr = context()->get_constant_mgr(); + + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = + context()->get_def_use_mgr()->GetDef(ptr_type_id); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + + std::vector<uint32_t> dims; + uint32_t element_type_id = 0; + GetArrayDimensions(pointee_type_id, &dims, &element_type_id); + + uint32_t total_elements = 1; + for (uint32_t dim : dims) { + total_elements *= dim; + } + + const analysis::Constant* total_elements_const = + constant_mgr->GetIntConst(total_elements, 32, false); + + Instruction* total_elements_inst = + constant_mgr->GetDefiningInstruction(total_elements_const); + uint32_t total_elements_id = total_elements_inst->result_id(); + + // Create new OpTypeArray. + analysis::Type* element_type = type_mgr->GetType(element_type_id); + analysis::Array::LengthInfo length_info = { + total_elements_id, + {analysis::Array::LengthInfo::kConstant, total_elements}}; + analysis::Array new_array_type(element_type, length_info); + uint32_t new_array_type_id = type_mgr->GetTypeInstruction(&new_array_type); + + // Create new OpTypePointer. + spv::StorageClass sc = + static_cast<spv::StorageClass>(ptr_type_inst->GetSingleWordInOperand(0)); + analysis::Pointer new_ptr_type(type_mgr->GetType(new_array_type_id), sc); + uint32_t new_ptr_type_id = type_mgr->GetTypeInstruction(&new_ptr_type); + + var->SetResultType(new_ptr_type_id); + context()->UpdateDefUse(var); + + // Move the var after the new pointer type to avoid a def-before-use. + var->InsertAfter(get_def_use_mgr()->GetDef(new_ptr_type_id)); + + return new_ptr_type_id; +} + +bool LegalizeMultidimArrayPass::RewriteAccessChains(Instruction* var, + uint32_t old_ptr_type_id) { + uint32_t var_id = var->result_id(); + std::vector<Instruction*> users; + // Use a worklist to handle transitive uses (e.g. through OpCopyObject) + std::vector<Instruction*> worklist; + + context()->get_def_use_mgr()->ForEachUser( + var_id, [&worklist](Instruction* user) { worklist.push_back(user); }); + + Instruction* old_ptr_type_inst = + context()->get_def_use_mgr()->GetDef(old_ptr_type_id); + uint32_t old_pointee_type_id = old_ptr_type_inst->GetSingleWordInOperand(1); + std::vector<uint32_t> dims; + uint32_t element_type_id = 0; + GetArrayDimensions(old_pointee_type_id, &dims, &element_type_id); + assert(dims.size() != 0 && + "This variable should have been rejected earlier."); + + // Calculate strides once + std::vector<uint32_t> strides(dims.size()); + strides[dims.size() - 1] = 1; + for (int i = static_cast<int>(dims.size()) - 2; i >= 0; --i) { + strides[i] = strides[i + 1] * dims[i + 1]; + } + + // Pre-calculate uint type id + uint32_t uint_type_id = context()->get_type_mgr()->GetUIntTypeId(); + if (uint_type_id == 0) return false; + + while (!worklist.empty()) { + Instruction* user = worklist.back(); + worklist.pop_back(); + + if (user->opcode() == spv::Op::OpAccessChain || + user->opcode() == spv::Op::OpInBoundsAccessChain) { + uint32_t num_indices = user->NumInOperands() - 1; + assert(num_indices >= dims.size()); + + InstructionBuilder builder(context(), user, IRContext::kAnalysisDefUse); + + uint32_t linearized_idx_id = 0; + for (uint32_t i = 0; i < dims.size(); ++i) { + uint32_t idx_id = user->GetSingleWordInOperand(i + 1); + + uint32_t term_id = idx_id; + if (strides[i] != 1) { + const analysis::Constant* stride_const = + context()->get_constant_mgr()->GetConstant( + context()->get_type_mgr()->GetType(uint_type_id), + {strides[i]}); + Instruction* stride_inst = + context()->get_constant_mgr()->GetDefiningInstruction( + stride_const); + + Instruction* mul_inst = builder.AddBinaryOp( + uint_type_id, spv::Op::OpIMul, idx_id, stride_inst->result_id()); + if (mul_inst == nullptr) return false; + term_id = mul_inst->result_id(); + } + + if (linearized_idx_id == 0) { + linearized_idx_id = term_id; + } else { + Instruction* add_inst = builder.AddBinaryOp( + uint_type_id, spv::Op::OpIAdd, linearized_idx_id, term_id); + if (add_inst == nullptr) return false; + linearized_idx_id = add_inst->result_id(); + } + } + + // Create new AccessChain. + Instruction::OperandList new_operands; + new_operands.push_back(user->GetInOperand(0)); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {linearized_idx_id}}); + for (uint32_t i = static_cast<uint32_t>(dims.size()); i < num_indices; + ++i) { + new_operands.push_back(user->GetInOperand(i + 1)); + } + user->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(user); + } else if (user->opcode() == spv::Op::OpCopyObject) { + // The type of the variable has changed so the result type of the + // OpCopyObject will change as well. + + uint32_t operand_id = user->GetSingleWordInOperand(0); + Instruction* operand_inst = + context()->get_def_use_mgr()->GetDef(operand_id); + user->SetResultType(operand_inst->type_id()); + context()->UpdateDefUse(user); + + // Add users of this copy to worklist + context()->get_def_use_mgr()->ForEachUser( + user->result_id(), + [&worklist](Instruction* u) { worklist.push_back(u); }); + } + } + return true; +} + +bool LegalizeMultidimArrayPass::CheckUse(Instruction* inst, + uint32_t max_depth) { + if (inst->opcode() == spv::Op::OpAccessChain || + inst->opcode() == spv::Op::OpInBoundsAccessChain) { + uint32_t num_indices = inst->NumInOperands() - 1; + return num_indices >= max_depth; + } else if (inst->opcode() == spv::Op::OpCopyObject) { + bool ok = true; + return !context()->get_def_use_mgr()->WhileEachUser( + inst->result_id(), + [&](Instruction* u) { return !CheckUse(u, max_depth); }); + return ok; + } else if (inst->IsDecoration() || inst->opcode() == spv::Op::OpName || + inst->opcode() == spv::Op::OpMemberName) { + // Metadata is fine. + return true; + } + + // Direct use of array or partial array without AccessChain is not allowed. + return false; +} + +bool LegalizeMultidimArrayPass::CanLegalize(Instruction* var) { + bool ok = true; + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = + context()->get_def_use_mgr()->GetDef(ptr_type_id); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + std::vector<uint32_t> dims; + uint32_t element_type_id = 0; + GetArrayDimensions(pointee_type_id, &dims, &element_type_id); + + context()->get_def_use_mgr()->ForEachUser( + var->result_id(), [&](Instruction* u) { + if (!CheckUse(u, static_cast<uint32_t>(dims.size()))) ok = false; + }); + return ok; +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/legalize_multidim_array_pass.h b/source/opt/legalize_multidim_array_pass.h new file mode 100644 index 0000000..61c912d --- /dev/null +++ b/source/opt/legalize_multidim_array_pass.h
@@ -0,0 +1,55 @@ +// Copyright (c) 2026 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_LEGALIZE_MULTIDIM_ARRAY_PASS_H_ +#define SOURCE_OPT_LEGALIZE_MULTIDIM_ARRAY_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Pass to legalize multidimensional arrays of resources for Vulkan. +// It transforms multidimensional arrays into single-dimensional ones. +class LegalizeMultidimArrayPass : public Pass { + public: + const char* name() const override { return "legalize-multidim-array"; } + Status Process() override; + + private: + // Returns true if |var| is a multidimensional array of resources. + bool IsMultidimArrayOfResources(Instruction* var); + + // Flattens the multidimensional array type of |var| and returns the new type + // id. + uint32_t FlattenArrayType(Instruction* var); + + // Rewrites all access chains that use |var|. + bool RewriteAccessChains(Instruction* var, uint32_t old_ptr_type_id); + + // Returns true if all uses of |var| can be legalized. + bool CanLegalize(Instruction* var); + + // Recursively checks if the uses of |inst| can be legalized. + bool CheckUse(Instruction* inst, uint32_t max_depth); + + // Returns the dimensions of the array type |type_id|. + void GetArrayDimensions(uint32_t type_id, std::vector<uint32_t>* dims, + uint32_t* element_type_id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LEGALIZE_MULTIDIM_ARRAY_PASS_H_
diff --git a/source/opt/licm_pass.cpp b/source/opt/licm_pass.cpp index f2a6e4d..996762b 100644 --- a/source/opt/licm_pass.cpp +++ b/source/opt/licm_pass.cpp
@@ -118,7 +118,6 @@ } bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) { - // TODO(1841): Handle failure to create pre-header. BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock(); if (!pre_header_bb) { return false;
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 91ea7c6..44e3645 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -1,6 +1,8 @@ // Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -352,15 +354,15 @@ 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 + // Only allow NonSemantic.Shader.DebugInfo (any version); we cannot safely + // optimise around unknown extended instruction sets even if non-semantic. for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == spv::Op::OpExtInstImport && "Expecting an import of an extension's instruction set."); const std::string extension_name = inst.GetInOperand(0).AsString(); if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && - extension_name != "NonSemantic.Shader.DebugInfo.100") { + !spvtools::utils::starts_with(extension_name, + "NonSemantic.Shader.DebugInfo.")) { return false; } } @@ -397,41 +399,84 @@ void LocalAccessChainConvertPass::InitExtensions() { extensions_allowlist_.clear(); - extensions_allowlist_.insert( - {"SPV_AMD_shader_explicit_vertex_parameter", - "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader", - "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot", - "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters", - "SPV_KHR_subgroup_vote", "SPV_KHR_8bit_storage", "SPV_KHR_16bit_storage", - "SPV_KHR_device_group", "SPV_KHR_multiview", - "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2", - "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage", - "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod", - "SPV_KHR_storage_buffer_storage_class", - // SPV_KHR_variable_pointers - // Currently do not support extended pointer expressions - "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage", - "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export", - "SPV_EXT_shader_viewport_index_layer", - "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask", - "SPV_EXT_fragment_fully_covered", "SPV_AMD_gpu_shader_half_float_fetch", - "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", - "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", - "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", - "SPV_NV_fragment_shader_barycentric", - "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint", - "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader", - "SPV_NV_ray_tracing", "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", - "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation", - "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", - "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", - "SPV_KHR_uniform_group_instructions", - "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_vulkan_memory_model", - "SPV_NV_bindless_texture", "SPV_EXT_shader_atomic_float_add", - "SPV_EXT_fragment_shader_interlock", - "SPV_KHR_compute_shader_derivatives", "SPV_NV_cooperative_matrix", - "SPV_KHR_cooperative_matrix", "SPV_KHR_ray_tracing_position_fetch", - "SPV_KHR_fragment_shading_rate"}); + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + // SPV_KHR_variable_pointers + // Currently do not support extended pointer expressions + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_EXT_descriptor_heap", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_KHR_terminate_invocation", + "SPV_KHR_shader_clock", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", + "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", + "SPV_KHR_ray_tracing_position_fetch", + "SPV_AMDX_shader_enqueue", + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control", + "SPV_NV_shader_invocation_reorder", + "SPV_NV_cluster_acceleration_structure", + "SPV_NV_linear_swept_spheres", + "SPV_KHR_maximal_reconvergence", + "SPV_NV_push_constant_bank", + "SPV_EXT_opacity_micromap", + "SPV_EXT_shader_invocation_reorder", + "SPV_EXT_shader_atomic_float16_add", + "SPV_KHR_abort", + "SPV_KHR_constant_data", + }); } bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index f9c5a79..9dcef9e 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp
@@ -1,6 +1,8 @@ // Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -185,15 +187,15 @@ 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 + // Only allow NonSemantic.Shader.DebugInfo (any version); we cannot safely + // optimise around unknown extended instruction sets even if non-semantic. for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == spv::Op::OpExtInstImport && "Expecting an import of an extension's instruction set."); const std::string extension_name = inst.GetInOperand(0).AsString(); if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && - extension_name != "NonSemantic.Shader.DebugInfo.100") { + !spvtools::utils::starts_with(extension_name, + "NonSemantic.Shader.DebugInfo.")) { return false; } } @@ -233,69 +235,85 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { extensions_allowlist_.clear(); - extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter", - "SPV_AMD_shader_trinary_minmax", - "SPV_AMD_gcn_shader", - "SPV_KHR_shader_ballot", - "SPV_AMD_shader_ballot", - "SPV_AMD_gpu_shader_half_float", - "SPV_KHR_shader_draw_parameters", - "SPV_KHR_subgroup_vote", - "SPV_KHR_8bit_storage", - "SPV_KHR_16bit_storage", - "SPV_KHR_device_group", - "SPV_KHR_multiview", - "SPV_NVX_multiview_per_view_attributes", - "SPV_NV_viewport_array2", - "SPV_NV_stereo_view_rendering", - "SPV_NV_sample_mask_override_coverage", - "SPV_NV_geometry_shader_passthrough", - "SPV_AMD_texture_gather_bias_lod", - "SPV_KHR_storage_buffer_storage_class", - "SPV_KHR_variable_pointers", - "SPV_AMD_gpu_shader_int16", - "SPV_KHR_post_depth_coverage", - "SPV_KHR_shader_atomic_counter_ops", - "SPV_EXT_shader_stencil_export", - "SPV_EXT_shader_viewport_index_layer", - "SPV_AMD_shader_image_load_store_lod", - "SPV_AMD_shader_fragment_mask", - "SPV_EXT_fragment_fully_covered", - "SPV_AMD_gpu_shader_half_float_fetch", - "SPV_GOOGLE_decorate_string", - "SPV_GOOGLE_hlsl_functionality1", - "SPV_GOOGLE_user_type", - "SPV_NV_shader_subgroup_partitioned", - "SPV_EXT_demote_to_helper_invocation", - "SPV_EXT_descriptor_indexing", - "SPV_NV_fragment_shader_barycentric", - "SPV_NV_compute_shader_derivatives", - "SPV_NV_shader_image_footprint", - "SPV_NV_shading_rate", - "SPV_NV_mesh_shader", - "SPV_EXT_mesh_shader", - "SPV_NV_ray_tracing", - "SPV_KHR_ray_tracing", - "SPV_KHR_ray_query", - "SPV_EXT_fragment_invocation_density", - "SPV_EXT_physical_storage_buffer", - "SPV_KHR_physical_storage_buffer", - "SPV_KHR_terminate_invocation", - "SPV_KHR_subgroup_uniform_control_flow", - "SPV_KHR_integer_dot_product", - "SPV_EXT_shader_image_int64", - "SPV_KHR_non_semantic_info", - "SPV_KHR_uniform_group_instructions", - "SPV_KHR_fragment_shader_barycentric", - "SPV_KHR_vulkan_memory_model", - "SPV_NV_bindless_texture", - "SPV_EXT_shader_atomic_float_add", - "SPV_EXT_fragment_shader_interlock", - "SPV_KHR_compute_shader_derivatives", - "SPV_NV_cooperative_matrix", - "SPV_KHR_cooperative_matrix", - "SPV_KHR_ray_tracing_position_fetch", - "SPV_KHR_fragment_shading_rate"}); + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_EXT_descriptor_heap", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + "SPV_KHR_shader_clock", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", + "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", + "SPV_KHR_ray_tracing_position_fetch", + "SPV_AMDX_shader_enqueue", + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control", + "SPV_NV_shader_invocation_reorder", + "SPV_NV_cluster_acceleration_structure", + "SPV_NV_linear_swept_spheres", + "SPV_KHR_maximal_reconvergence", + "SPV_NV_push_constant_bank", + "SPV_EXT_opacity_micromap", + "SPV_EXT_shader_invocation_reorder", + "SPV_EXT_shader_atomic_float16_add", + "SPV_KHR_abort", + "SPV_KHR_constant_data", + }); } } // namespace opt
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index 38fa14e..a3b2949 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp
@@ -1,6 +1,8 @@ // Copyright (c) 2017 The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -48,15 +50,15 @@ 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 + // Only allow NonSemantic.Shader.DebugInfo (any version); we cannot safely + // optimise around unknown extended instruction sets even if non-semantic. for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == spv::Op::OpExtInstImport && "Expecting an import of an extension's instruction set."); const std::string extension_name = inst.GetInOperand(0).AsString(); if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && - extension_name != "NonSemantic.Shader.DebugInfo.100") { + !spvtools::utils::starts_with(extension_name, + "NonSemantic.Shader.DebugInfo.")) { return false; } } @@ -86,66 +88,84 @@ } void LocalSingleStoreElimPass::InitExtensionAllowList() { - extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter", - "SPV_AMD_shader_trinary_minmax", - "SPV_AMD_gcn_shader", - "SPV_KHR_shader_ballot", - "SPV_AMD_shader_ballot", - "SPV_AMD_gpu_shader_half_float", - "SPV_KHR_shader_draw_parameters", - "SPV_KHR_subgroup_vote", - "SPV_KHR_8bit_storage", - "SPV_KHR_16bit_storage", - "SPV_KHR_device_group", - "SPV_KHR_multiview", - "SPV_NVX_multiview_per_view_attributes", - "SPV_NV_viewport_array2", - "SPV_NV_stereo_view_rendering", - "SPV_NV_sample_mask_override_coverage", - "SPV_NV_geometry_shader_passthrough", - "SPV_AMD_texture_gather_bias_lod", - "SPV_KHR_storage_buffer_storage_class", - "SPV_KHR_variable_pointers", - "SPV_AMD_gpu_shader_int16", - "SPV_KHR_post_depth_coverage", - "SPV_KHR_shader_atomic_counter_ops", - "SPV_EXT_shader_stencil_export", - "SPV_EXT_shader_viewport_index_layer", - "SPV_AMD_shader_image_load_store_lod", - "SPV_AMD_shader_fragment_mask", - "SPV_EXT_fragment_fully_covered", - "SPV_AMD_gpu_shader_half_float_fetch", - "SPV_GOOGLE_decorate_string", - "SPV_GOOGLE_hlsl_functionality1", - "SPV_NV_shader_subgroup_partitioned", - "SPV_EXT_descriptor_indexing", - "SPV_NV_fragment_shader_barycentric", - "SPV_NV_compute_shader_derivatives", - "SPV_NV_shader_image_footprint", - "SPV_NV_shading_rate", - "SPV_NV_mesh_shader", - "SPV_EXT_mesh_shader", - "SPV_NV_ray_tracing", - "SPV_KHR_ray_query", - "SPV_EXT_fragment_invocation_density", - "SPV_EXT_physical_storage_buffer", - "SPV_KHR_physical_storage_buffer", - "SPV_KHR_terminate_invocation", - "SPV_KHR_subgroup_uniform_control_flow", - "SPV_KHR_integer_dot_product", - "SPV_EXT_shader_image_int64", - "SPV_KHR_non_semantic_info", - "SPV_KHR_uniform_group_instructions", - "SPV_KHR_fragment_shader_barycentric", - "SPV_KHR_vulkan_memory_model", - "SPV_NV_bindless_texture", - "SPV_EXT_shader_atomic_float_add", - "SPV_EXT_fragment_shader_interlock", - "SPV_KHR_compute_shader_derivatives", - "SPV_NV_cooperative_matrix", - "SPV_KHR_cooperative_matrix", - "SPV_KHR_ray_tracing_position_fetch", - "SPV_KHR_fragment_shading_rate"}); + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_descriptor_indexing", + "SPV_EXT_descriptor_heap", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + "SPV_KHR_shader_clock", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", + "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", + "SPV_KHR_ray_tracing_position_fetch", + "SPV_AMDX_shader_enqueue", + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control", + "SPV_NV_shader_invocation_reorder", + "SPV_NV_cluster_acceleration_structure", + "SPV_NV_linear_swept_spheres", + "SPV_KHR_maximal_reconvergence", + "SPV_NV_push_constant_bank", + "SPV_EXT_opacity_micromap", + "SPV_EXT_shader_invocation_reorder", + "SPV_EXT_shader_atomic_float16_add", + "SPV_KHR_abort", + "SPV_KHR_constant_data", + }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { std::vector<Instruction*> users;
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp index cbfc2e7..b0064b4 100644 --- a/source/opt/loop_descriptor.cpp +++ b/source/opt/loop_descriptor.cpp
@@ -25,6 +25,7 @@ #include "source/opt/dominator_tree.h" #include "source/opt/ir_context.h" #include "source/opt/iterator.h" +#include "source/opt/pass.h" #include "source/opt/tree_iterator.h" #include "source/util/make_unique.h" @@ -278,6 +279,9 @@ CFG* cfg = context_->cfg(); loop_header_ = cfg->SplitLoopHeader(loop_header_); + if (loop_header_ == nullptr) { + return nullptr; + } return loop_preheader_; } @@ -920,18 +924,19 @@ return induction; } -bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() { - auto modified = false; +LoopDescriptor::Status LoopDescriptor::CreatePreHeaderBlocksIfMissing() { + bool modified = false; for (auto& loop : *this) { if (!loop.GetPreHeaderBlock()) { + if (!loop.GetOrCreatePreHeaderBlock()) { + return Status::Failure; + } modified = true; - // TODO(1841): Handle failure to create pre-header. - loop.GetOrCreatePreHeaderBlock(); } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } // Add and remove loops which have been marked for addition and removal to
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h index d451496..bd93bfa 100644 --- a/source/opt/loop_descriptor.h +++ b/source/opt/loop_descriptor.h
@@ -28,6 +28,7 @@ #include "source/opt/dominator_analysis.h" #include "source/opt/module.h" #include "source/opt/tree_iterator.h" +#include "source/util/status.h" namespace spvtools { namespace opt { @@ -425,6 +426,9 @@ using pre_iterator = TreeDFIterator<Loop>; using const_pre_iterator = TreeDFIterator<const Loop>; + // The status of processing a module. + using Status = utils::Status; + // Creates a loop object for all loops found in |f|. LoopDescriptor(IRContext* context, const Function* f); @@ -506,9 +510,11 @@ loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add))); } - // Checks all loops in |this| and will create pre-headers for all loops - // that don't have one. Returns |true| if any blocks were created. - bool CreatePreHeaderBlocksIfMissing(); + // Creates pre-header blocks for all loops in the function that do not have + // one. Returns `LoopDescriptor::Status::kSuccessWithChange` if any change is + // made, `LoopDescriptor::Status::kSuccessWithoutChange` if no change is made, + // and `LoopDescriptor::Status::kFailure` if it fails to create a pre-header. + Status CreatePreHeaderBlocksIfMissing(); // Should be called to preserve the LoopAnalysis after loops have been marked // for addition with AddLoop or MarkLoopForRemoval.
diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp index 1bbe4ba..fbbee11 100644 --- a/source/opt/loop_fission.cpp +++ b/source/opt/loop_fission.cpp
@@ -362,14 +362,19 @@ LoopUtils util{context_, loop_}; LoopUtils::LoopCloningResult clone_results; Loop* cloned_loop = util.CloneAndAttachLoopToHeader(&clone_results); + if (!cloned_loop) { + return nullptr; + } // Update the OpLoopMerge in the cloned loop. cloned_loop->UpdateLoopMergeInst(); // Add the loop_ to the module. - // TODO(1841): Handle failure to create pre-header. - Function::iterator it = - util.GetFunction()->FindBlock(loop_->GetOrCreatePreHeaderBlock()->id()); + BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock(); + if (!pre_header) { + return nullptr; + } + Function::iterator it = util.GetFunction()->FindBlock(pre_header->id()); util.GetFunction()->AddBasicBlocks(clone_results.cloned_bb_.begin(), clone_results.cloned_bb_.end(), ++it); loop_->SetPreHeaderBlock(cloned_loop->GetMergeBlock()); @@ -478,6 +483,9 @@ if (impl.CanPerformSplit()) { Loop* second_loop = impl.SplitLoop(); + if (!second_loop) { + return Status::Failure; + } changed = true; context()->InvalidateAnalysesExceptFor( IRContext::kAnalysisLoopAnalysis);
diff --git a/source/opt/loop_fusion_pass.cpp b/source/opt/loop_fusion_pass.cpp index 097430f..c8e74c8 100644 --- a/source/opt/loop_fusion_pass.cpp +++ b/source/opt/loop_fusion_pass.cpp
@@ -22,23 +22,27 @@ namespace opt { Pass::Status LoopFusionPass::Process() { - bool modified = false; + Status status = Status::SuccessWithoutChange; Module* module = context()->module(); // Process each function in the module for (Function& f : *module) { - modified |= ProcessFunction(&f); + status = CombineStatus(status, ProcessFunction(&f)); + if (status == Status::Failure) return Status::Failure; } - return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; + return status; } -bool LoopFusionPass::ProcessFunction(Function* function) { +Pass::Status LoopFusionPass::ProcessFunction(Function* function) { LoopDescriptor& ld = *context()->GetLoopDescriptor(function); // If a loop doesn't have a preheader needs then it needs to be created. Make // sure to return Status::SuccessWithChange in that case. - auto modified = ld.CreatePreHeaderBlocksIfMissing(); + bool modified = false; + auto status = ld.CreatePreHeaderBlocksIfMissing(); + if (status == LoopDescriptor::Status::Failure) return Status::Failure; + modified = status == LoopDescriptor::Status::SuccessWithChange; // TODO(tremmelg): Could the only loop that |loop| could possibly be fused be // picked out so don't have to check every loop @@ -55,13 +59,13 @@ fusion.Fuse(); // Recurse, as the current iterators will have been invalidated. ProcessFunction(function); - return true; + return Status::SuccessWithChange; } } } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } } // namespace opt
diff --git a/source/opt/loop_fusion_pass.h b/source/opt/loop_fusion_pass.h index 9d5b7cc..9c1f567 100644 --- a/source/opt/loop_fusion_pass.h +++ b/source/opt/loop_fusion_pass.h
@@ -39,7 +39,7 @@ private: // Fuse loops in |function| if compatible, legal and the fused loop won't use // too many registers. - bool ProcessFunction(Function* function); + Status ProcessFunction(Function* function); // The maximum number of registers a fused loop is allowed to use. size_t max_registers_per_loop_;
diff --git a/source/opt/loop_peeling.cpp b/source/opt/loop_peeling.cpp index 25c6db1..e7f3876 100644 --- a/source/opt/loop_peeling.cpp +++ b/source/opt/loop_peeling.cpp
@@ -45,7 +45,7 @@ size_t LoopPeelingPass::code_grow_threshold_ = 1000; -void LoopPeeling::DuplicateAndConnectLoop( +bool LoopPeeling::DuplicateAndConnectLoop( LoopUtils::LoopCloningResult* clone_results) { CFG& cfg = *context_->cfg(); analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); @@ -53,12 +53,17 @@ assert(CanPeelLoop() && "Cannot peel loop!"); std::vector<BasicBlock*> ordered_loop_blocks; - // TODO(1841): Handle failure to create pre-header. BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock(); + if (!pre_header) { + return false; + } loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks); cloned_loop_ = loop_utils_.CloneLoop(clone_results, ordered_loop_blocks); + if (!cloned_loop_) { + return false; + } // Add the basic block to the function. Function::iterator it = @@ -146,17 +151,21 @@ // Force the creation of a new preheader for the original loop and set it as // the merge block for the cloned loop. - // TODO(1841): Handle failure to create pre-header. - cloned_loop_->SetMergeBlock(loop_->GetOrCreatePreHeaderBlock()); + BasicBlock* new_pre_header = loop_->GetOrCreatePreHeaderBlock(); + if (!new_pre_header) { + return false; + } + cloned_loop_->SetMergeBlock(new_pre_header); + return true; } -void LoopPeeling::InsertCanonicalInductionVariable( +bool LoopPeeling::InsertCanonicalInductionVariable( LoopUtils::LoopCloningResult* clone_results) { if (original_loop_canonical_induction_variable_) { canonical_induction_variable_ = context_->get_def_use_mgr()->GetDef(clone_results->value_map_.at( original_loop_canonical_induction_variable_->result_id())); - return; + return true; } BasicBlock::iterator insert_point = GetClonedLoop()->GetLatchBlock()->tail(); @@ -168,19 +177,25 @@ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* uint_1_cst = builder.GetIntConstant<uint32_t>(1, int_type_->IsSigned()); + if (!uint_1_cst) return false; // Create the increment. // Note that we do "1 + 1" here, one of the operand should the phi // value but we don't have it yet. The operand will be set latter. Instruction* iv_inc = builder.AddIAdd( uint_1_cst->type_id(), uint_1_cst->result_id(), uint_1_cst->result_id()); + if (!iv_inc) return false; builder.SetInsertPoint(&*GetClonedLoop()->GetHeaderBlock()->begin()); + Instruction* initial_value = + builder.GetIntConstant<uint32_t>(0, int_type_->IsSigned()); + if (!initial_value) return false; + canonical_induction_variable_ = builder.AddPhi( uint_1_cst->type_id(), - {builder.GetIntConstant<uint32_t>(0, int_type_->IsSigned())->result_id(), - GetClonedLoop()->GetPreHeaderBlock()->id(), iv_inc->result_id(), - GetClonedLoop()->GetLatchBlock()->id()}); + {initial_value->result_id(), GetClonedLoop()->GetPreHeaderBlock()->id(), + iv_inc->result_id(), GetClonedLoop()->GetLatchBlock()->id()}); + if (!canonical_induction_variable_) return false; // Connect everything. iv_inc->SetInOperand(0, {canonical_induction_variable_->result_id()}); @@ -191,6 +206,7 @@ if (do_while_form_) { canonical_induction_variable_ = iv_inc; } + return true; } void LoopPeeling::GetIteratorUpdateOperations( @@ -308,7 +324,7 @@ } } -void LoopPeeling::FixExitCondition( +bool LoopPeeling::FixExitCondition( const std::function<uint32_t(Instruction*)>& condition_builder) { CFG& cfg = *context_->cfg(); @@ -329,7 +345,11 @@ --insert_point; } - exit_condition->SetInOperand(0, {condition_builder(&*insert_point)}); + uint32_t new_cond_id = condition_builder(&*insert_point); + if (new_cond_id == 0) { + return false; + } + exit_condition->SetInOperand(0, {new_cond_id}); uint32_t to_continue_block_idx = GetClonedLoop()->IsInsideLoop(exit_condition->GetSingleWordInOperand(1)) @@ -341,6 +361,7 @@ // Update def/use manager. context_->get_def_use_mgr()->AnalyzeInstUse(exit_condition); + return true; } BasicBlock* LoopPeeling::CreateBlockBefore(BasicBlock* bb) { @@ -348,10 +369,13 @@ CFG& cfg = *context_->cfg(); assert(cfg.preds(bb->id()).size() == 1 && "More than one predecessor"); - // TODO(1841): Handle id overflow. + uint32_t new_id = context_->TakeNextId(); + if (new_id == 0) { + return nullptr; + } std::unique_ptr<BasicBlock> new_bb = - MakeUnique<BasicBlock>(std::unique_ptr<Instruction>(new Instruction( - context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {}))); + MakeUnique<BasicBlock>(std::unique_ptr<Instruction>( + new Instruction(context_, spv::Op::OpLabel, 0, new_id, {}))); // Update the loop descriptor. Loop* in_loop = (*loop_utils_.GetLoopDescriptor())[bb]; if (in_loop) { @@ -394,8 +418,10 @@ BasicBlock* LoopPeeling::ProtectLoop(Loop* loop, Instruction* condition, BasicBlock* if_merge) { - // TODO(1841): Handle failure to create pre-header. BasicBlock* if_block = loop->GetOrCreatePreHeaderBlock(); + if (!if_block) { + return nullptr; + } // Will no longer be a pre-header because of the if. loop->SetPreHeaderBlock(nullptr); // Kill the branch to the header. @@ -411,48 +437,63 @@ return if_block; } -void LoopPeeling::PeelBefore(uint32_t peel_factor) { +bool LoopPeeling::PeelBefore(uint32_t peel_factor) { assert(CanPeelLoop() && "Cannot peel loop"); LoopUtils::LoopCloningResult clone_results; // Clone the loop and insert the cloned one before the loop. - DuplicateAndConnectLoop(&clone_results); + if (!DuplicateAndConnectLoop(&clone_results)) { + return false; + } // Add a canonical induction variable "canonical_induction_variable_". - InsertCanonicalInductionVariable(&clone_results); + if (!InsertCanonicalInductionVariable(&clone_results)) { + return false; + } InstructionBuilder builder( context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* factor = builder.GetIntConstant(peel_factor, int_type_->IsSigned()); + if (!factor) return false; Instruction* has_remaining_iteration = builder.AddLessThan( factor->result_id(), loop_iteration_count_->result_id()); + if (!has_remaining_iteration) return false; Instruction* max_iteration = builder.AddSelect( factor->type_id(), has_remaining_iteration->result_id(), factor->result_id(), loop_iteration_count_->result_id()); + if (!max_iteration) return false; // Change the exit condition of the cloned loop to be (exit when become // false): // "canonical_induction_variable_" < min("factor", "loop_iteration_count_") - FixExitCondition([max_iteration, this](Instruction* insert_before_point) { - return InstructionBuilder(context_, insert_before_point, - IRContext::kAnalysisDefUse | - IRContext::kAnalysisInstrToBlockMapping) - .AddLessThan(canonical_induction_variable_->result_id(), - max_iteration->result_id()) - ->result_id(); - }); + if (!FixExitCondition( + [max_iteration, this](Instruction* insert_before_point) { + Instruction* new_cond = + InstructionBuilder(context_, insert_before_point, + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping) + .AddLessThan(canonical_induction_variable_->result_id(), + max_iteration->result_id()); + return new_cond ? new_cond->result_id() : 0; + })) { + return false; + } // "Protect" the second loop: the second loop can only be executed if // |has_remaining_iteration| is true (i.e. factor < loop_iteration_count_). BasicBlock* if_merge_block = loop_->GetMergeBlock(); - loop_->SetMergeBlock(CreateBlockBefore(loop_->GetMergeBlock())); + BasicBlock* new_merge_block = CreateBlockBefore(loop_->GetMergeBlock()); + if (!new_merge_block) return false; + loop_->SetMergeBlock(new_merge_block); // Prevent the second loop from being executed if we already executed all the // required iterations. BasicBlock* if_block = ProtectLoop(loop_, has_remaining_iteration, if_merge_block); + if (!if_block) return false; + // Patch the phi of the merge block. if_merge_block->ForEachPhiInst( [&clone_results, if_block, this](Instruction* phi) { @@ -471,14 +512,17 @@ context_->InvalidateAnalysesExceptFor( IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG); + return true; } -void LoopPeeling::PeelAfter(uint32_t peel_factor) { +bool LoopPeeling::PeelAfter(uint32_t peel_factor) { assert(CanPeelLoop() && "Cannot peel loop"); LoopUtils::LoopCloningResult clone_results; // Clone the loop and insert the cloned one before the loop. - DuplicateAndConnectLoop(&clone_results); + if (!DuplicateAndConnectLoop(&clone_results)) { + return false; + } // Add a canonical induction variable "canonical_induction_variable_". InsertCanonicalInductionVariable(&clone_results); @@ -488,40 +532,55 @@ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); Instruction* factor = builder.GetIntConstant(peel_factor, int_type_->IsSigned()); + if (!factor) return false; Instruction* has_remaining_iteration = builder.AddLessThan( factor->result_id(), loop_iteration_count_->result_id()); + if (!has_remaining_iteration) return false; // Change the exit condition of the cloned loop to be (exit when become // false): // "canonical_induction_variable_" + "factor" < "loop_iteration_count_" - FixExitCondition([factor, this](Instruction* insert_before_point) { - InstructionBuilder cond_builder( - context_, insert_before_point, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - // Build the following check: canonical_induction_variable_ + factor < - // iteration_count - return cond_builder - .AddLessThan(cond_builder - .AddIAdd(canonical_induction_variable_->type_id(), - canonical_induction_variable_->result_id(), - factor->result_id()) - ->result_id(), - loop_iteration_count_->result_id()) - ->result_id(); - }); + if (!FixExitCondition([factor, + this](Instruction* insert_before_point) -> uint32_t { + InstructionBuilder cond_builder( + context_, insert_before_point, + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping); + // Build the following check: canonical_induction_variable_ + factor < + // iteration_count + Instruction* add = cond_builder.AddIAdd( + canonical_induction_variable_->type_id(), + canonical_induction_variable_->result_id(), factor->result_id()); + if (!add) return 0; + Instruction* new_cond = cond_builder.AddLessThan( + add->result_id(), loop_iteration_count_->result_id()); + return new_cond ? new_cond->result_id() : 0; + })) { + return false; + } // "Protect" the first loop: the first loop can only be executed if // factor < loop_iteration_count_. // The original loop's pre-header was the cloned loop merge block. - GetClonedLoop()->SetMergeBlock( - CreateBlockBefore(GetOriginalLoop()->GetPreHeaderBlock())); + BasicBlock* pre_header = GetOriginalLoop()->GetPreHeaderBlock(); + if (!pre_header) { + return false; + } + BasicBlock* new_merge_block = CreateBlockBefore(pre_header); + if (!new_merge_block) { + return false; + } + GetClonedLoop()->SetMergeBlock(new_merge_block); // Use the second loop preheader as if merge block. // Prevent the first loop if only the peeled loop needs it. BasicBlock* if_block = ProtectLoop(cloned_loop_, has_remaining_iteration, GetOriginalLoop()->GetPreHeaderBlock()); + if (!if_block) { + return false; + } // Patch the phi of the header block. // We added an if to enclose the first loop and because the phi node are @@ -529,8 +588,10 @@ // dominate the preheader. // We had to the preheader (our if merge block) the required phi instruction // and patch the header phi. + bool ok = true; GetOriginalLoop()->GetHeaderBlock()->ForEachPhiInst( - [&clone_results, if_block, this](Instruction* phi) { + [&clone_results, if_block, &ok, this](Instruction* phi) { + if (!ok) return; analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); auto find_value_idx = [](Instruction* phi_inst, Loop* loop) { @@ -554,15 +615,21 @@ find_value_idx(phi, GetOriginalLoop())), GetClonedLoop()->GetMergeBlock()->id(), cloned_preheader_value, if_block->id()}); + if (!new_phi) { + ok = false; + return; + } phi->SetInOperand(find_value_idx(phi, GetOriginalLoop()), {new_phi->result_id()}); def_use_mgr->AnalyzeInstUse(phi); }); + if (!ok) return false; context_->InvalidateAnalysesExceptFor( IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG); + return true; } Pass::Status LoopPeelingPass::Process() { @@ -571,13 +638,19 @@ // Process each function in the module for (Function& f : *module) { - modified |= ProcessFunction(&f); + Pass::Status status = ProcessFunction(&f); + if (status == Status::Failure) { + return Status::Failure; + } + if (status == Status::SuccessWithChange) { + modified = true; + } } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -bool LoopPeelingPass::ProcessFunction(Function* f) { +Pass::Status LoopPeelingPass::ProcessFunction(Function* f) { bool modified = false; LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(f); @@ -593,41 +666,56 @@ CodeMetrics loop_size; loop_size.Analyze(*loop); - auto try_peel = [&loop_size, &modified, this](Loop* loop_to_peel) -> Loop* { + auto try_peel = [&loop_size, &modified, this]( + Loop* loop_to_peel) -> std::pair<Pass::Status, Loop*> { if (!loop_to_peel->IsLCSSA()) { - LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA(); + if (!LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA()) { + return {Pass::Status::Failure, nullptr}; + } } - bool peeled_loop; + Pass::Status status; Loop* still_peelable_loop; - std::tie(peeled_loop, still_peelable_loop) = + std::tie(status, still_peelable_loop) = ProcessLoop(loop_to_peel, &loop_size); - if (peeled_loop) { + if (status == Pass::Status::SuccessWithChange) { modified = true; } - return still_peelable_loop; + return {status, still_peelable_loop}; }; - Loop* still_peelable_loop = try_peel(loop); + Pass::Status status; + Loop* still_peelable_loop; + std::tie(status, still_peelable_loop) = try_peel(loop); + + if (status == Pass::Status::Failure) { + return Pass::Status::Failure; + } + // The pass is working out the maximum factor by which a loop can be peeled. // If the loop can potentially be peeled again, then there is only one // possible direction, so only one call is still needed. if (still_peelable_loop) { - try_peel(loop); + std::tie(status, still_peelable_loop) = try_peel(still_peelable_loop); + if (status == Pass::Status::Failure) { + return Pass::Status::Failure; + } } } - return modified; + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; } -std::pair<bool, Loop*> LoopPeelingPass::ProcessLoop(Loop* loop, - CodeMetrics* loop_size) { +std::tuple<Pass::Status, Loop*> LoopPeelingPass::ProcessLoop( + Loop* loop, CodeMetrics* loop_size) { ScalarEvolutionAnalysis* scev_analysis = context()->GetScalarEvolutionAnalysis(); // Default values for bailing out. - std::pair<bool, Loop*> bail_out{false, nullptr}; + std::tuple<Pass::Status, Loop*> bail_out{Pass::Status::SuccessWithoutChange, + nullptr}; BasicBlock* exit_block = loop->FindConditionBlock(); if (!exit_block) { @@ -744,7 +832,9 @@ Loop* extra_opportunity = nullptr; if (direction == PeelDirection::kBefore) { - peeler.PeelBefore(factor); + if (!peeler.PeelBefore(factor)) { + return {Pass::Status::Failure, nullptr}; + } if (stats_) { stats_->peeled_loops_.emplace_back(loop, PeelDirection::kBefore, factor); } @@ -753,7 +843,9 @@ extra_opportunity = peeler.GetOriginalLoop(); } } else { - peeler.PeelAfter(factor); + if (!peeler.PeelAfter(factor)) { + return {Pass::Status::Failure, nullptr}; + } if (stats_) { stats_->peeled_loops_.emplace_back(loop, PeelDirection::kAfter, factor); } @@ -763,7 +855,7 @@ } } - return {true, extra_opportunity}; + return {Pass::Status::SuccessWithChange, extra_opportunity}; } uint32_t LoopPeelingPass::LoopPeelingInfo::GetFirstLoopInvariantOperand(
diff --git a/source/opt/loop_peeling.h b/source/opt/loop_peeling.h index 2a55fe4..a794793 100644 --- a/source/opt/loop_peeling.h +++ b/source/opt/loop_peeling.h
@@ -148,11 +148,11 @@ // Moves the execution of the |factor| first iterations of the loop into a // dedicated loop. - void PeelBefore(uint32_t factor); + bool PeelBefore(uint32_t factor); // Moves the execution of the |factor| last iterations of the loop into a // dedicated loop. - void PeelAfter(uint32_t factor); + bool PeelAfter(uint32_t factor); // Returns the cloned loop. Loop* GetClonedLoop() { return cloned_loop_; } @@ -184,19 +184,19 @@ // Duplicate |loop_| and place the new loop before the cloned loop. Iterating // values from the cloned loop are then connected to the original loop as // initializer. - void DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results); + bool DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results); // Insert the canonical induction variable into the first loop as a simplified - // counter. - void InsertCanonicalInductionVariable( + // counter. Returns true on success. + bool InsertCanonicalInductionVariable( LoopUtils::LoopCloningResult* clone_results); // Fixes the exit condition of the before loop. The function calls // |condition_builder| to get the condition to use in the conditional branch // of the loop exit. The loop will be exited if the condition evaluate to // true. |condition_builder| takes an Instruction* that represent the - // insertion point. - void FixExitCondition( + // insertion point. Returns true on success. + bool FixExitCondition( const std::function<uint32_t(Instruction*)>& condition_builder); // Gathers all operations involved in the update of |iterator| into @@ -321,10 +321,14 @@ ScalarEvolutionAnalysis* scev_analysis_; size_t loop_max_iterations_; }; - // Peel profitable loops in |f|. - bool ProcessFunction(Function* f); - // Peel |loop| if profitable. - std::pair<bool, Loop*> ProcessLoop(Loop* loop, CodeMetrics* loop_size); + // Peel profitable loops in |f|. Returns Pass::Status::Failure if an error + // occurs. + Pass::Status ProcessFunction(Function* f); + // Peel |loop| if profitable. Returns Pass::Status::Failure if an error + // occurs. Returns {Pass::Status::SuccessWithChange, Loop*} if the loop is + // peeled and there is another peeling opportunity. + std::tuple<Pass::Status, Loop*> ProcessLoop(Loop* loop, + CodeMetrics* loop_size); static size_t code_grow_threshold_; LoopPeelingStats* stats_;
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index d9e34f2..8765b16 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp
@@ -178,7 +178,7 @@ // Unroll the |loop| by given |factor| by copying the whole body |factor| // times. The resulting basicblock structure will remain a loop. - void PartiallyUnroll(Loop*, size_t factor); + bool PartiallyUnroll(Loop*, size_t factor); // If partially unrolling the |loop| would leave the loop with too many bodies // for its number of iterations then this method should be used. This method @@ -186,12 +186,12 @@ // successor of the original's merge block. The original loop will have its // condition changed to loop over the residual part and the duplicate will be // partially unrolled. The resulting structure will be two loops. - void PartiallyUnrollResidualFactor(Loop* loop, size_t factor); + bool PartiallyUnrollResidualFactor(Loop* loop, size_t factor); // Fully unroll the |loop| by copying the full body by the total number of // loop iterations, folding all conditions, and removing the backedge from the // continue block to the header. - void FullyUnroll(Loop* loop); + bool FullyUnroll(Loop* loop); // Get the ID of the variable in the |phi| paired with |label|. uint32_t GetPhiDefID(const Instruction* phi, uint32_t label) const; @@ -203,7 +203,7 @@ // Remove the OpConditionalBranch instruction inside |conditional_block| used // to branch to either exit or continue the loop and replace it with an // unconditional OpBranch to block |new_target|. - void FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target); + bool FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target); // Add all blocks_to_add_ to function_ at the |insert_point|. void AddBlocksToFunction(const BasicBlock* insert_point); @@ -211,7 +211,7 @@ // Duplicates the |old_loop|, cloning each body and remapping the ids without // removing instructions or changing relative structure. Result will be stored // in |new_loop|. - void DuplicateLoop(Loop* old_loop, Loop* new_loop); + bool DuplicateLoop(Loop* old_loop, Loop* new_loop); inline size_t GetLoopIterationCount() const { return number_of_loop_iterations_; @@ -241,7 +241,7 @@ // to old // ids. |loop| is used to identify special loop blocks (header, continue, // etc). - void AssignNewResultIds(BasicBlock* basic_block); + bool AssignNewResultIds(BasicBlock* basic_block); // Using the map built by AssignNewResultIds, replace the uses in |inst| // by the id that the use maps to. @@ -258,18 +258,18 @@ // the old |loop| continue block and the new body will link to the |loop| // header via the new continue block. |eliminate_conditions| is used to decide // whether or not to fold all the condition blocks other than the last one. - void CopyBody(Loop* loop, bool eliminate_conditions); + bool CopyBody(Loop* loop, bool eliminate_conditions); // Copy a given |block_to_copy| in the |loop| and record the mapping of the // old/new ids. |preserve_instructions| determines whether or not the method // will modify (other than result_id) instructions which are copied. - void CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy, + bool CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy, bool preserve_instructions); // The actual implementation of the unroll step. Unrolls |loop| by given // |factor| by copying the body by |factor| times. Also propagates the // induction variable value throughout the copies. - void Unroll(Loop* loop, size_t factor); + bool Unroll(Loop* loop, size_t factor); // Fills the loop_blocks_inorder_ field with the ordered list of basic blocks // as computed by the method ComputeLoopOrderedBlocks. @@ -376,11 +376,12 @@ // loop it creates two loops and unrolls one and adjusts the condition on the // other. The end result being that the new loop pair iterates over the correct // number of bodies. -void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop, +bool LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop, size_t factor) { - // TODO(1841): Handle id overflow. - std::unique_ptr<Instruction> new_label{new Instruction( - context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})}; + uint32_t new_label_id = context_->TakeNextId(); + if (new_label_id == 0) return false; + std::unique_ptr<Instruction> new_label{ + new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})}; std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))}; new_exit_bb->SetParent(&function_); @@ -401,7 +402,9 @@ // Clear the basic blocks of the new loop. new_loop->ClearBlocks(); - DuplicateLoop(loop, new_loop.get()); + if (!DuplicateLoop(loop, new_loop.get())) { + return false; + } // Add the blocks to the function. AddBlocksToFunction(loop->GetMergeBlock()); @@ -416,7 +419,9 @@ loop_induction_variable_ = state_.new_phi; // Unroll the new loop by the factor with the usual -1 to account for the // existing block iteration. - Unroll(new_loop.get(), factor); + if (!Unroll(new_loop.get(), factor)) { + return false; + } LinkLastPhisToStart(new_loop.get()); AddBlocksToLoop(new_loop.get()); @@ -460,6 +465,10 @@ new_constant = builder.GetUintConstant(static_cast<int32_t>(remainder)); } + if (!new_constant) { + return false; + } + uint32_t constant_id = new_constant->result_id(); // Update the condition check. @@ -477,6 +486,9 @@ for (size_t index = 0; index < new_inductions.size(); ++index) { Instruction* new_induction = new_inductions[index]; Instruction* old_induction = old_inductions[index]; + if (!new_induction || !old_induction) { + return false; + } // Get the index of the loop initalizer, the value coming in from the // preheader. uint32_t initalizer_index = @@ -512,6 +524,7 @@ loop_descriptor.AddLoop(std::move(new_loop), loop->GetParent()); RemoveDeadInstructions(); + return true; } // Mark this loop as DontUnroll as it will already be unrolled and it may not @@ -528,7 +541,7 @@ // Duplicate the |loop| body |factor| - 1 number of times while keeping the loop // backedge intact. This will leave the loop with |factor| number of bodies // after accounting for the initial body. -void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) { +bool LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) { // If we unroll a loop partially it will not be safe to unroll it further. // This is due to the current method of calculating the number of loop // iterations. @@ -539,8 +552,11 @@ state_ = LoopUnrollState{loop_induction_variable_, loop->GetLatchBlock(), loop_condition_block_, std::move(inductions)}; for (size_t i = 0; i < factor - 1; ++i) { - CopyBody(loop, true); + if (!CopyBody(loop, true)) { + return false; + } } + return true; } void LoopUnrollerUtilsImpl::RemoveDeadInstructions() { @@ -560,6 +576,10 @@ loop->GetInductionVariables(inductions); for (size_t index = 0; index < inductions.size(); ++index) { + // We don't want the decorations that applied to the induction variable + // to be applied to the value that replace it. + context_->KillNamesAndDecorates(state_.previous_phis_[index]); + uint32_t trip_step_id = GetPhiDefID(state_.previous_phis_[index], state_.previous_latch_block_->id()); context_->ReplaceAllUsesWith(inductions[index]->result_id(), trip_step_id); @@ -569,12 +589,16 @@ // Fully unroll the loop by partially unrolling it by the number of loop // iterations minus one for the body already accounted for. -void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) { +bool LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) { // We unroll the loop by number of iterations in the loop. - Unroll(loop, number_of_loop_iterations_); + if (!Unroll(loop, number_of_loop_iterations_)) { + return false; + } // The first condition block is preserved until now so it can be copied. - FoldConditionBlock(loop_condition_block_, 1); + if (!FoldConditionBlock(loop_condition_block_, 1)) { + return false; + } // Delete the OpLoopMerge and remove the backedge to the header. CloseUnrolledLoop(loop); @@ -598,6 +622,7 @@ context_->InvalidateAnalysesExceptFor( IRContext::Analysis::kAnalysisLoopAnalysis | IRContext::Analysis::kAnalysisDefUse); + return true; } void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) { @@ -618,10 +643,11 @@ // and the id mapping in the state. |preserve_instructions| is used to determine // whether or not this function should edit instructions other than the // |result_id|. -void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr, +bool LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr, bool preserve_instructions) { // Clone the block exactly, including the IDs. BasicBlock* basic_block = itr->Clone(context_); + if (!basic_block) return false; basic_block->SetParent(itr->GetParent()); // We do not want to duplicate DebugDeclare. @@ -629,7 +655,9 @@ // Assign each result a new unique ID and keep a mapping of the old ids to // the new ones. - AssignNewResultIds(basic_block); + if (!AssignNewResultIds(basic_block)) { + return false; + } // If this is the continue block we are copying. if (itr == loop->GetContinueBlock()) { @@ -668,13 +696,16 @@ // Keep tracking the old block via a map. state_.new_blocks[itr->id()] = basic_block; + return true; } -void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) { +bool LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) { // Copy each basic block in the loop, give them new ids, and save state // information. for (const BasicBlock* itr : loop_blocks_inorder_) { - CopyBasicBlock(loop, itr, false); + if (!CopyBasicBlock(loop, itr, false)) { + return false; + } } // Set the previous latch block to point to the new header. @@ -713,7 +744,9 @@ if (eliminate_conditions && state_.new_condition_block != loop_condition_block_) { - FoldConditionBlock(state_.new_condition_block, 1); + if (!FoldConditionBlock(state_.new_condition_block, 1)) { + return false; + } } // Only reference to the header block is the backedge in the latch block, @@ -729,6 +762,7 @@ // Swap the state so the new is now the previous. state_.NextIterationState(); + return true; } uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi, @@ -742,7 +776,7 @@ return 0; } -void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, +bool LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, uint32_t operand_label) { // Remove the old conditional branch to the merge and continue blocks. Instruction& old_branch = *condition_block->tail(); @@ -759,8 +793,13 @@ IRContext::Analysis::kAnalysisInstrToBlockMapping); Instruction* new_branch = builder.AddBranch(new_target); - if (!lines.empty()) new_branch->AddDebugLine(&lines.back()); + if (!lines.empty()) { + if (!new_branch->AddDebugLine(&lines.back())) { + return false; + } + } new_branch->SetDebugScope(scope); + return true; } void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) { @@ -807,19 +846,24 @@ } // Uses the first loop to create a copy of the loop with new IDs. -void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { +bool LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { std::vector<BasicBlock*> new_block_order; // Copy every block in the old loop. for (const BasicBlock* itr : loop_blocks_inorder_) { - CopyBasicBlock(old_loop, itr, true); + if (!CopyBasicBlock(old_loop, itr, true)) { + return false; + } new_block_order.push_back(blocks_to_add_.back().get()); } // Clone the merge block, give it a new id and record it in the state. BasicBlock* new_merge = old_loop->GetMergeBlock()->Clone(context_); + if (!new_merge) return false; new_merge->SetParent(old_loop->GetMergeBlock()->GetParent()); - AssignNewResultIds(new_merge); + if (!AssignNewResultIds(new_merge)) { + return false; + } state_.new_blocks[old_loop->GetMergeBlock()->id()] = new_merge; // Remap the operands of every instruction in the loop to point to the new @@ -836,6 +880,7 @@ new_loop->SetContinueBlock(state_.new_continue_block); new_loop->SetLatchBlock(state_.new_latch_block); new_loop->SetMergeBlock(new_merge); + return true; } // Whenever the utility copies a block it stores it in a temporary buffer, this @@ -858,13 +903,15 @@ // Assign all result_ids in |basic_block| instructions to new IDs and preserve // the mapping of new ids to old ones. -void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { +bool LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); // Label instructions aren't covered by normal traversal of the // instructions. - // TODO(1841): Handle id overflow. uint32_t new_label_id = context_->TakeNextId(); + if (new_label_id == 0) { + return false; + } // Assign a new id to the label. state_.new_inst[basic_block->GetLabelInst()->result_id()] = new_label_id; @@ -884,10 +931,18 @@ } // Give the instruction a new id. - // TODO(1841): Handle id overflow. - inst.SetResultId(context_->TakeNextId()); + uint32_t new_id = context_->TakeNextId(); + if (new_id == 0) { + return false; + } + inst.SetResultId(new_id); def_use_mgr->AnalyzeInstDef(&inst); + // All decorations that can apply to an instruction in a function body + // modify the behaviour of the instruction, and should be on the + // new instruction to keep the same results. + context_->get_decoration_mgr()->CloneDecorations(old_id, new_id); + // Save the mapping of old_id -> new_id. state_.new_inst[old_id] = inst.result_id(); // Check if this instruction is the induction variable. @@ -897,6 +952,7 @@ } state_.ids_to_new_inst[inst.result_id()] = &inst; } + return true; } void LoopUnrollerUtilsImpl::RemapOperands(Instruction* inst) { @@ -957,12 +1013,13 @@ // Duplicate the |loop| body |factor| number of times while keeping the loop // backedge intact. -void LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) { - Unroll(loop, factor); +bool LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) { + if (!Unroll(loop, factor)) return false; LinkLastPhisToStart(loop); AddBlocksToLoop(loop); AddBlocksToFunction(loop->GetMergeBlock()); RemoveDeadInstructions(); + return true; } /* @@ -1067,7 +1124,9 @@ // If the unrolling factor is larger than or the same size as the loop just // fully unroll the loop. if (factor >= unroller.GetLoopIterationCount()) { - unroller.FullyUnroll(loop_); + if (!unroller.FullyUnroll(loop_)) { + return false; + } return true; } @@ -1076,9 +1135,13 @@ // remaining part. We add one when calucating the remainder to take into // account the one iteration already in the loop. if (unroller.GetLoopIterationCount() % factor != 0) { - unroller.PartiallyUnrollResidualFactor(loop_, factor); + if (!unroller.PartiallyUnrollResidualFactor(loop_, factor)) { + return false; + } } else { - unroller.PartiallyUnroll(loop_, factor); + if (!unroller.PartiallyUnroll(loop_, factor)) { + return false; + } } return true; @@ -1094,7 +1157,9 @@ loop_->GetHeaderBlock()->GetParent()}; unroller.Init(loop_); - unroller.FullyUnroll(loop_); + if (!unroller.FullyUnroll(loop_)) { + return false; + } return true; } @@ -1127,15 +1192,25 @@ } if (fully_unroll_) { - loop_utils.FullyUnroll(); + if (!loop_utils.FullyUnroll()) { + return Status::Failure; + } + changed = true; } else { - loop_utils.PartiallyUnroll(unroll_factor_); + if (!loop_utils.PartiallyUnroll(unroll_factor_)) { + return Status::Failure; + } + changed = true; } - changed = true; } LD->PostModificationCleanup(); } + if (changed) { + context()->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + } + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; }
diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp index 41f1a80..b49f243 100644 --- a/source/opt/loop_unswitch_pass.cpp +++ b/source/opt/loop_unswitch_pass.cpp
@@ -92,12 +92,16 @@ // position |ip|. This function preserves the def/use and instr to block // managers. BasicBlock* CreateBasicBlock(Function::iterator ip) { + uint32_t new_label_id = TakeNextId(); + if (new_label_id == 0) { + return nullptr; + } + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); - // TODO(1841): Handle id overflow. BasicBlock* bb = &*ip.InsertBefore(std::unique_ptr<BasicBlock>( new BasicBlock(std::unique_ptr<Instruction>(new Instruction( - context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {}))))); + context_, spv::Op::OpLabel, 0, new_label_id, {}))))); bb->SetParent(function_); def_use_mgr->AnalyzeInstDef(bb->GetLabelInst()); context_->set_instr_block(bb->GetLabelInst(), bb); @@ -135,7 +139,7 @@ } // Unswitches |loop_|. - void PerformUnswitch() { + bool PerformUnswitch() { assert(CanUnswitchLoop() && "Cannot unswitch if there is not constant condition"); assert(loop_->GetPreHeaderBlock() && "This loop has no pre-header block"); @@ -165,6 +169,9 @@ if_merge_block ? CreateBasicBlock(FindBasicBlockPosition(if_merge_block)) : nullptr; + if (if_merge_block && !loop_merge_block) { + return false; + } if (loop_merge_block) { // Add the instruction and update managers. InstructionBuilder builder( @@ -174,17 +181,24 @@ builder.SetInsertPoint(&*loop_merge_block->begin()); cfg.RegisterBlock(loop_merge_block); def_use_mgr->AnalyzeInstDef(loop_merge_block->GetLabelInst()); - // Update CFG. + bool ok = true; if_merge_block->ForEachPhiInst( - [loop_merge_block, &builder, this](Instruction* phi) { + [loop_merge_block, &ok, &builder, this](Instruction* phi) -> bool { Instruction* cloned = phi->Clone(context_); - cloned->SetResultId(TakeNextId()); + uint32_t new_id = TakeNextId(); + if (new_id == 0) { + ok = false; + return false; + } + cloned->SetResultId(new_id); builder.AddInstruction(std::unique_ptr<Instruction>(cloned)); phi->SetInOperand(0, {cloned->result_id()}); phi->SetInOperand(1, {loop_merge_block->id()}); for (uint32_t j = phi->NumInOperands() - 1; j > 1; j--) phi->RemoveInOperand(j); + return true; }); + if (!ok) return false; // Copy the predecessor list (will get invalidated otherwise). std::vector<uint32_t> preds = cfg.preds(if_merge_block->id()); for (uint32_t pid : preds) { @@ -227,6 +241,9 @@ // we need to create a dedicated block for the if. BasicBlock* loop_pre_header = CreateBasicBlock(++FindBasicBlockPosition(if_block)); + if (!loop_pre_header) { + return false; + } InstructionBuilder( context_, loop_pre_header, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) @@ -308,6 +325,12 @@ // specific value. original_loop_constant_value = GetValueForDefaultPathForSwitch(iv_condition); + if (!original_loop_constant_value) { + return false; + } + if (!original_loop_constant_value) { + return false; + } for (uint32_t i = 2; i < iv_condition->NumInOperands(); i += 2) { constant_branch.emplace_back( @@ -341,6 +364,9 @@ Loop* cloned_loop = loop_utils.CloneLoop(&clone_result, ordered_loop_blocks_); + if (!cloned_loop) { + return false; + } specialisation_pair.second = cloned_loop->GetPreHeaderBlock(); //////////////////////////////////// @@ -416,6 +442,7 @@ context_->InvalidateAnalysesExceptFor( IRContext::Analysis::kAnalysisLoopAnalysis); + return true; } private: @@ -434,10 +461,7 @@ std::vector<BasicBlock*> ordered_loop_blocks_; // Returns the next usable id for the context. - uint32_t TakeNextId() { - // TODO(1841): Handle id overflow. - return context_->TakeNextId(); - } + uint32_t TakeNextId() { return context_->TakeNextId(); } // Simplifies |loop| assuming the instruction |to_version_insn| takes the // value |cst_value|. |block_range| is an iterator range returning the loop @@ -573,13 +597,15 @@ // Process each function in the module for (Function& f : *module) { - modified |= ProcessFunction(&f); + Pass::Status status = ProcessFunction(&f); + if (status == Status::Failure) return Status::Failure; + if (status == Status::SuccessWithChange) modified = true; } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -bool LoopUnswitchPass::ProcessFunction(Function* f) { +Pass::Status LoopUnswitchPass::ProcessFunction(Function* f) { bool modified = false; std::unordered_set<Loop*> processed_loop; @@ -597,17 +623,21 @@ LoopUnswitch unswitcher(context(), f, &loop, &loop_descriptor); while (unswitcher.CanUnswitchLoop()) { if (!loop.IsLCSSA()) { - LoopUtils(context(), &loop).MakeLoopClosedSSA(); + if (!LoopUtils(context(), &loop).MakeLoopClosedSSA()) { + return Status::Failure; + } + } + if (!unswitcher.PerformUnswitch()) { + return Status::Failure; } modified = true; loop_changed = true; - unswitcher.PerformUnswitch(); } if (loop_changed) break; } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } } // namespace opt
diff --git a/source/opt/loop_unswitch_pass.h b/source/opt/loop_unswitch_pass.h index 4f7295d..66a757f 100644 --- a/source/opt/loop_unswitch_pass.h +++ b/source/opt/loop_unswitch_pass.h
@@ -34,7 +34,8 @@ Pass::Status Process() override; private: - bool ProcessFunction(Function* f); + // Process the given function. + Pass::Status ProcessFunction(Function* f); }; } // namespace opt
diff --git a/source/opt/loop_utils.cpp b/source/opt/loop_utils.cpp index 20494e1..ced68de 100644 --- a/source/opt/loop_utils.cpp +++ b/source/opt/loop_utils.cpp
@@ -68,7 +68,7 @@ // block. This operation does not update the def/use manager, instead it // records what needs to be updated. The actual update is performed by // UpdateManagers. - void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) { + bool RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) { assert( (user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) && "The root basic block must be the incoming edge if |user| is a phi " @@ -79,9 +79,13 @@ "phi instruction"); Instruction* new_def = GetOrBuildIncoming(bb->id()); + if (!new_def) { + return false; + } user->SetOperand(operand_index, {new_def->result_id()}); rewritten_.insert(user); + return true; } // In-place update of some managers (avoid full invalidation). @@ -120,6 +124,9 @@ IRContext::kAnalysisInstrToBlockMapping); Instruction* incoming_phi = builder.AddPhi(def_insn_.type_id(), incomings); + if (!incoming_phi) { + return nullptr; + } rewritten_.insert(incoming_phi); return incoming_phi; @@ -139,6 +146,9 @@ IRContext::kAnalysisInstrToBlockMapping); Instruction* incoming_phi = builder.AddPhi(def_insn_.type_id(), incomings); + if (!incoming_phi) { + return nullptr; + } rewritten_.insert(incoming_phi); return incoming_phi; @@ -270,7 +280,7 @@ // Make the set |blocks| closed SSA. The set is closed SSA if all the uses // outside the set are phi instructions in exiting basic block set (hold by // |lcssa_rewriter|). -inline void MakeSetClosedSSA(IRContext* context, Function* function, +inline bool MakeSetClosedSSA(IRContext* context, Function* function, const std::unordered_set<uint32_t>& blocks, const std::unordered_set<BasicBlock*>& exit_bb, LCSSARewriter* lcssa_rewriter) { @@ -285,18 +295,18 @@ if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue; for (Instruction& inst : *bb) { LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst); - def_use_manager->ForEachUse( + bool success = def_use_manager->WhileEachUse( &inst, [&blocks, &rewriter, &exit_bb, context]( Instruction* use, uint32_t operand_index) { BasicBlock* use_parent = context->get_instr_block(use); assert(use_parent); - if (blocks.count(use_parent->id())) return; + if (blocks.count(use_parent->id())) return true; if (use->opcode() == spv::Op::OpPhi) { // If the use is a Phi instruction and the incoming block is // coming from the loop, then that's consistent with LCSSA form. if (exit_bb.count(use_parent)) { - return; + return true; } else { // That's not an exit block, but the user is a phi instruction. // Consider the incoming branch only. @@ -306,16 +316,20 @@ } // Rewrite the use. Note that this call does not invalidate the // def/use manager. So this operation is safe. - rewriter.RewriteUse(use_parent, use, operand_index); + return rewriter.RewriteUse(use_parent, use, operand_index); }); + if (!success) { + return false; + } rewriter.UpdateManagers(); } } + return true; } } // namespace -void LoopUtils::CreateLoopDedicatedExits() { +bool LoopUtils::CreateLoopDedicatedExits() { Function* function = loop_->GetHeaderBlock()->GetParent(); LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function); CFG& cfg = *context_->cfg(); @@ -351,10 +365,13 @@ assert(insert_pt != function->end() && "Basic Block not found"); // Create the dedicate exit basic block. - // TODO(1841): Handle id overflow. - BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr<BasicBlock>( - new BasicBlock(std::unique_ptr<Instruction>(new Instruction( - context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {}))))); + uint32_t exit_id = context_->TakeNextId(); + if (exit_id == 0) { + return false; + } + BasicBlock& exit = *insert_pt.InsertBefore( + std::unique_ptr<BasicBlock>(new BasicBlock(std::unique_ptr<Instruction>( + new Instruction(context_, spv::Op::OpLabel, 0, exit_id, {}))))); exit.SetParent(function); // Redirect in loop predecessors to |exit| block. @@ -380,7 +397,7 @@ // We also reset the insert point so all instructions are inserted before // the branch. builder.SetInsertPoint(builder.AddBranch(non_dedicate->id())); - non_dedicate->ForEachPhiInst( + bool succeeded = non_dedicate->WhileEachPhiInst( [&builder, &exit, def_use_mgr, this](Instruction* phi) { // New phi operands for this instruction. std::vector<uint32_t> new_phi_op; @@ -400,6 +417,9 @@ // Build the new phi instruction dedicated exit block. Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op); + if (!exit_phi) { + return false; + } // Build the new incoming branch. new_phi_op.push_back(exit_phi->result_id()); new_phi_op.push_back(exit.id()); @@ -412,7 +432,9 @@ phi->RemoveInOperand(j); // Update the def/use manager for this |phi|. def_use_mgr->AnalyzeInstUse(phi); + return true; }); + if (!succeeded) return false; // Update the CFG. cfg.RegisterBlock(&exit); cfg.RemoveNonExistingEdges(non_dedicate->id()); @@ -431,10 +453,13 @@ PreservedAnalyses | IRContext::kAnalysisCFG | IRContext::Analysis::kAnalysisLoopAnalysis); } + return true; } -void LoopUtils::MakeLoopClosedSSA() { - CreateLoopDedicatedExits(); +bool LoopUtils::MakeLoopClosedSSA() { + if (!CreateLoopDedicatedExits()) { + return false; + } Function* function = loop_->GetHeaderBlock()->GetParent(); CFG& cfg = *context_->cfg(); @@ -452,8 +477,10 @@ LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb, loop_->GetMergeBlock()); - MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb, - &lcssa_rewriter); + if (!MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb, + &lcssa_rewriter)) { + return false; + } // Make sure all defs post-dominated by the merge block have their last use no // further than the merge block. @@ -466,14 +493,17 @@ exit_bb.insert(loop_->GetMergeBlock()); // LCSSARewriter is reusable here only because it forces the creation of a // phi instruction in the merge block. - MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb, - &lcssa_rewriter); + if (!MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb, + &lcssa_rewriter)) { + return false; + } } context_->InvalidateAnalysesExceptFor( IRContext::Analysis::kAnalysisCFG | IRContext::Analysis::kAnalysisDominatorAnalysis | IRContext::Analysis::kAnalysisLoopAnalysis); + return true; } Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const { @@ -488,12 +518,18 @@ Loop* LoopUtils::CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result) { // Clone the loop. - Loop* new_loop = CloneLoop(cloning_result); + Loop* cloned_loop = CloneLoop(cloning_result); + if (!cloned_loop) { + return nullptr; + } // Create a new exit block/label for the new loop. - // TODO(1841): Handle id overflow. - std::unique_ptr<Instruction> new_label{new Instruction( - context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})}; + uint32_t new_label_id = context_->TakeNextId(); + if (new_label_id == 0) { + return nullptr; + } + std::unique_ptr<Instruction> new_label{ + new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})}; std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))}; new_exit_bb->SetParent(loop_->GetMergeBlock()->GetParent()); @@ -520,7 +556,7 @@ } const uint32_t old_header = loop_->GetHeaderBlock()->id(); - const uint32_t new_header = new_loop->GetHeaderBlock()->id(); + const uint32_t new_header = cloned_loop->GetHeaderBlock()->id(); analysis::DefUseManager* def_use = context_->get_def_use_mgr(); def_use->ForEachUse(old_header, @@ -529,22 +565,24 @@ inst->SetOperand(operand, {new_header}); }); - // TODO(1841): Handle failure to create pre-header. + BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock(); + if (!pre_header) { + return nullptr; + } def_use->ForEachUse( - loop_->GetOrCreatePreHeaderBlock()->id(), + pre_header->id(), [new_merge_block, this](Instruction* inst, uint32_t operand) { if (this->loop_->IsInsideLoop(inst)) inst->SetOperand(operand, {new_merge_block}); - }); - new_loop->SetMergeBlock(new_exit_bb.get()); + cloned_loop->SetMergeBlock(new_exit_bb.get()); - new_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock()); + cloned_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock()); // Add the new block into the cloned instructions. cloning_result->cloned_bb_.push_back(std::move(new_exit_bb)); - return new_loop; + return cloned_loop; } Loop* LoopUtils::CloneLoop( @@ -561,9 +599,13 @@ // For each basic block in the loop, we clone it and register the mapping // between old and new ids. BasicBlock* new_bb = old_bb->Clone(context_); + if (!new_bb) return nullptr; new_bb->SetParent(&function_); - // TODO(1841): Handle id overflow. - new_bb->GetLabelInst()->SetResultId(context_->TakeNextId()); + uint32_t new_label_id = context_->TakeNextId(); + if (new_label_id == 0) { + return nullptr; + } + new_bb->GetLabelInst()->SetResultId(new_label_id); def_use_mgr->AnalyzeInstDef(new_bb->GetLabelInst()); context_->set_instr_block(new_bb->GetLabelInst(), new_bb); cloning_result->cloned_bb_.emplace_back(new_bb); @@ -578,8 +620,11 @@ new_inst != new_bb->end(); ++new_inst, ++old_inst) { cloning_result->ptr_map_[&*new_inst] = &*old_inst; if (new_inst->HasResultId()) { - // TODO(1841): Handle id overflow. - new_inst->SetResultId(context_->TakeNextId()); + uint32_t new_result_id = context_->TakeNextId(); + if (new_result_id == 0) { + return nullptr; + } + new_inst->SetResultId(new_result_id); cloning_result->value_map_[old_inst->result_id()] = new_inst->result_id();
diff --git a/source/opt/loop_utils.h b/source/opt/loop_utils.h index 70060fc..2c96e9e 100644 --- a/source/opt/loop_utils.h +++ b/source/opt/loop_utils.h
@@ -95,14 +95,14 @@ // // This makes some loop transformations (such as loop unswitch) simpler // (removes the needs to take care of exiting variables). - void MakeLoopClosedSSA(); + bool MakeLoopClosedSSA(); // Create dedicate exit basic block. This ensure all exit basic blocks has the // loop as sole predecessors. // By construction, structured control flow already has a dedicated exit // block. // Preserves: CFG, def/use and instruction to block mapping. - void CreateLoopDedicatedExits(); + bool CreateLoopDedicatedExits(); // Clone |loop_| and remap its instructions. Newly created blocks // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to @@ -114,6 +114,7 @@ // The function preserves the def/use, cfg and instr to block analyses. // The cloned loop nest will be added to the loop descriptor and will have // ownership. + // Returns the cloned loop, or nullptr if the loop could not be cloned. Loop* CloneLoop(LoopCloningResult* cloning_result, const std::vector<BasicBlock*>& ordered_loop_blocks) const; // Clone |loop_| and remap its instructions, as above. Overload to compute @@ -121,6 +122,7 @@ Loop* CloneLoop(LoopCloningResult* cloning_result) const; // Clone the |loop_| and make the new loop branch to the second loop on exit. + // Returns the cloned loop, or nullptr if the loop could not be cloned. Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); // Perform a partial unroll of |loop| by given |factor|. This will copy the
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp index 65f45ec..4d061ff 100644 --- a/source/opt/mem_pass.cpp +++ b/source/opt/mem_pass.cpp
@@ -72,7 +72,8 @@ bool MemPass::IsNonPtrAccessChain(const spv::Op opcode) const { return opcode == spv::Op::OpAccessChain || - opcode == spv::Op::OpInBoundsAccessChain; + opcode == spv::Op::OpInBoundsAccessChain || + opcode == spv::Op::OpUntypedAccessChainKHR; } bool MemPass::IsPtr(uint32_t ptrId) { @@ -88,11 +89,14 @@ ptrInst = get_def_use_mgr()->GetDef(varId); } const spv::Op op = ptrInst->opcode(); - if (op == spv::Op::OpVariable || IsNonPtrAccessChain(op)) return true; + if (op == spv::Op::OpVariable || op == spv::Op::OpUntypedVariableKHR || + IsNonPtrAccessChain(op)) + return true; const uint32_t varTypeId = ptrInst->type_id(); if (varTypeId == 0) return false; const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); - return varTypeInst->opcode() == spv::Op::OpTypePointer; + return varTypeInst->opcode() == spv::Op::OpTypePointer || + varTypeInst->opcode() == spv::Op::OpTypeUntypedPointerKHR; } Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) { @@ -100,18 +104,29 @@ Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId); Instruction* varInst; - if (ptrInst->opcode() == spv::Op::OpConstantNull) { - *varId = 0; - return ptrInst; + switch (ptrInst->opcode()) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: + case spv::Op::OpFunctionParameter: + varInst = ptrInst; + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpCopyObject: + varInst = ptrInst->GetBaseAddress(); + break; + default: + *varId = 0; + return ptrInst; + break; } - if (ptrInst->opcode() != spv::Op::OpVariable && - ptrInst->opcode() != spv::Op::OpFunctionParameter) { - varInst = ptrInst->GetBaseAddress(); - } else { - varInst = ptrInst; - } - if (varInst->opcode() == spv::Op::OpVariable) { + if (varInst->opcode() == spv::Op::OpVariable || + varInst->opcode() == spv::Op::OpUntypedVariableKHR) { *varId = varInst->result_id(); } else { *varId = 0; @@ -332,7 +347,7 @@ // %50 = OpUndef %int // [ ... ] // %30 = OpPhi %int %int_42 %13 %50 %14 %50 %15 -void MemPass::RemovePhiOperands( +bool MemPass::RemovePhiOperands( Instruction* phi, const std::unordered_set<BasicBlock*>& reachable_blocks) { std::vector<Operand> keep_operands; uint32_t type_id = 0; @@ -374,6 +389,7 @@ if (!undef_id) { type_id = arg_def_instr->type_id(); undef_id = Type2Undef(type_id); + if (undef_id == 0) return false; } keep_operands.push_back( Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {undef_id})); @@ -392,6 +408,7 @@ context()->ForgetUses(phi); phi->ReplaceOperands(keep_operands); context()->AnalyzeUses(phi); + return true; } void MemPass::RemoveBlock(Function::iterator* bi) { @@ -414,8 +431,8 @@ *bi = bi->Erase(); } -bool MemPass::RemoveUnreachableBlocks(Function* func) { - if (func->IsDeclaration()) return false; +Pass::Status MemPass::RemoveUnreachableBlocks(Function* func) { + if (func->IsDeclaration()) return Status::SuccessWithoutChange; bool modified = false; // Mark reachable all blocks reachable from the function's entry block. @@ -461,9 +478,11 @@ // If the block is reachable and has Phi instructions, remove all // operands from its Phi instructions that reference unreachable blocks. // If the block has no Phi instructions, this is a no-op. - block.ForEachPhiInst([&reachable_blocks, this](Instruction* phi) { - RemovePhiOperands(phi, reachable_blocks); - }); + bool success = + block.WhileEachPhiInst([&reachable_blocks, this](Instruction* phi) { + return RemovePhiOperands(phi, reachable_blocks); + }); + if (!success) return Status::Failure; } // Erase unreachable blocks. @@ -476,13 +495,11 @@ } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -bool MemPass::CFGCleanup(Function* func) { - bool modified = false; - modified |= RemoveUnreachableBlocks(func); - return modified; +Pass::Status MemPass::CFGCleanup(Function* func) { + return RemoveUnreachableBlocks(func); } void MemPass::CollectTargetVars(Function* func) {
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h index aef9e5f..496286b 100644 --- a/source/opt/mem_pass.h +++ b/source/opt/mem_pass.h
@@ -114,7 +114,7 @@ void DCEInst(Instruction* inst, const std::function<void(Instruction*)>&); // Call all the cleanup helper functions on |func|. - bool CFGCleanup(Function* func); + Status CFGCleanup(Function* func); // Return true if |op| is supported decorate. inline bool IsNonTypeDecorate(spv::Op op) const { @@ -142,15 +142,15 @@ bool HasOnlySupportedRefs(uint32_t varId); // Remove all the unreachable basic blocks in |func|. - bool RemoveUnreachableBlocks(Function* func); + Status RemoveUnreachableBlocks(Function* func); // Remove the block pointed by the iterator |*bi|. This also removes // all the instructions in the pointed-to block. void RemoveBlock(Function::iterator* bi); // Remove Phi operands in |phi| that are coming from blocks not in - // |reachable_blocks|. - void RemovePhiOperands( + // |reachable_blocks|. Returns false if it fails. + bool RemovePhiOperands( Instruction* phi, const std::unordered_set<BasicBlock*>& reachable_blocks);
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp index c262ea0..0ec7c37 100644 --- a/source/opt/merge_return_pass.cpp +++ b/source/opt/merge_return_pass.cpp
@@ -58,7 +58,9 @@ failed = true; } } else { - MergeReturnBlocks(function, return_blocks); + if (!MergeReturnBlocks(function, return_blocks)) { + failed = true; + } } return true; }; @@ -132,7 +134,9 @@ state_.pop_back(); } - ProcessStructuredBlock(block); + if (!ProcessStructuredBlock(block)) { + return false; + } // Generate state for next block if warranted GenerateState(block); @@ -167,14 +171,20 @@ // We have not kept the dominator tree up-to-date. // Invalidate it at this point to make sure it will be rebuilt. context()->RemoveDominatorAnalysis(function); - AddNewPhiNodes(); + if (!AddNewPhiNodes()) { + return false; + } return true; } -void MergeReturnPass::CreateReturnBlock() { +bool MergeReturnPass::CreateReturnBlock() { // Create a label for the new return block + uint32_t label_id = TakeNextId(); + if (label_id == 0) { + return false; + } std::unique_ptr<Instruction> return_label( - new Instruction(context(), spv::Op::OpLabel, 0u, TakeNextId(), {})); + new Instruction(context(), spv::Op::OpLabel, 0u, label_id, {})); // Create the new basic block std::unique_ptr<BasicBlock> return_block( @@ -186,14 +196,20 @@ final_return_block_); assert(final_return_block_->GetParent() == function_ && "The function should have been set when the block was created."); + return true; } -void MergeReturnPass::CreateReturn(BasicBlock* block) { - AddReturnValue(); +bool MergeReturnPass::CreateReturn(BasicBlock* block) { + if (!AddReturnValue()) { + return false; + } if (return_value_) { // Load and return the final return value uint32_t loadId = TakeNextId(); + if (loadId == 0) { + return false; + } block->AddInstruction(MakeUnique<Instruction>( context(), spv::Op::OpLoad, function_->type_id(), loadId, std::initializer_list<Operand>{ @@ -216,14 +232,21 @@ context()->AnalyzeDefUse(block->terminator()); context()->set_instr_block(block->terminator(), block); } + return true; } -void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) { +bool MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) { + if (block->tail() == block->end()) { + return true; + } + spv::Op tail_opcode = block->tail()->opcode(); if (tail_opcode == spv::Op::OpReturn || tail_opcode == spv::Op::OpReturnValue) { if (!return_flag_) { - AddReturnFlag(); + if (!AddReturnFlag()) { + return false; + } } } @@ -232,15 +255,20 @@ tail_opcode == spv::Op::OpUnreachable) { assert(CurrentState().InBreakable() && "Should be in the placeholder construct."); - BranchToBlock(block, CurrentState().BreakMergeId()); + if (!BranchToBlock(block, CurrentState().BreakMergeId())) { + return false; + } return_blocks_.insert(block->id()); } + return true; } -void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) { +bool MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) { if (block->tail()->opcode() == spv::Op::OpReturn || block->tail()->opcode() == spv::Op::OpReturnValue) { - RecordReturned(block); + if (!RecordReturned(block)) { + return false; + } RecordReturnValue(block); } @@ -248,7 +276,9 @@ if (target_block->GetLoopMergeInst()) { cfg()->SplitLoopHeader(target_block); } - UpdatePhiNodes(block, target_block); + if (!UpdatePhiNodes(block, target_block)) { + return false; + } Instruction* return_inst = block->terminator(); return_inst->SetOpcode(spv::Op::OpBranch); @@ -256,19 +286,26 @@ context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst); new_edges_[target_block].insert(block->id()); cfg()->AddEdge(block->id(), target); + return true; } -void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source, +bool MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target) { - target->ForEachPhiInst([this, new_source](Instruction* inst) { + bool succeeded = true; + target->ForEachPhiInst([this, new_source, &succeeded](Instruction* inst) { uint32_t undefId = Type2Undef(inst->type_id()); + if (undefId == 0) { + succeeded = false; + return; + } inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}}); inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}}); context()->UpdateDefUse(inst); }); + return succeeded; } -void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block, +bool MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst) { DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(merge_block->GetParent()); @@ -302,7 +339,7 @@ }); if (users_to_update.empty()) { - return; + return true; } // There is at least one values that needs to be replaced. @@ -346,6 +383,9 @@ if (regenerateInstruction) { std::unique_ptr<Instruction> regen_inst(inst.Clone(context())); uint32_t new_id = TakeNextId(); + if (new_id == 0) { + return false; + } regen_inst->SetResultId(new_id); Instruction* insert_pos = &*merge_block->begin(); while (insert_pos->opcode() == spv::Op::OpPhi) { @@ -355,18 +395,31 @@ get_def_use_mgr()->AnalyzeInstDefUse(new_phi); context()->set_instr_block(new_phi, merge_block); - new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) { + bool succeeded = true; + new_phi->ForEachInId([dom_tree, merge_block, this, + &succeeded](uint32_t* use_id) { + if (!succeeded) { + return; + } Instruction* use = get_def_use_mgr()->GetDef(*use_id); BasicBlock* use_bb = context()->get_instr_block(use); if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) { - CreatePhiNodesForInst(merge_block, *use); + if (!CreatePhiNodesForInst(merge_block, *use)) { + succeeded = false; + } } }); + if (!succeeded) { + return false; + } } else { InstructionBuilder builder( context(), &*merge_block->begin(), IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); new_phi = builder.AddPhi(inst.type_id(), phi_operands); + if (new_phi == nullptr) { + return false; + } } uint32_t result_of_phi = new_phi->result_id(); @@ -380,6 +433,7 @@ context()->AnalyzeUses(user); } } + return true; } bool MergeReturnPass::PredicateBlocks( @@ -472,6 +526,9 @@ cfg()->RemoveSuccessorEdges(block); auto old_body_id = TakeNextId(); + if (old_body_id == 0) { + return false; + } BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter); predicated->insert(old_body); @@ -508,8 +565,11 @@ analysis::Bool bool_type; uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type); assert(bool_id != 0); - uint32_t load_id = - builder.AddLoad(bool_id, return_flag_->result_id())->result_id(); + Instruction* load_inst = builder.AddLoad(bool_id, return_flag_->result_id()); + if (load_inst == nullptr) { + return false; + } + uint32_t load_id = load_inst->result_id(); // 2. Branch to |merge_block| (true) or |old_body| (false) builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(), @@ -522,7 +582,9 @@ } // 3. Update OpPhi instructions in |merge_block|. - UpdatePhiNodes(block, merge_block); + if (!UpdatePhiNodes(block, merge_block)) { + return false; + } // 4. Update the CFG. We do this after updating the OpPhi instructions // because |UpdatePhiNodes| assumes the edge from |block| has not been added @@ -535,10 +597,10 @@ return true; } -void MergeReturnPass::RecordReturned(BasicBlock* block) { +bool MergeReturnPass::RecordReturned(BasicBlock* block) { if (block->tail()->opcode() != spv::Op::OpReturn && block->tail()->opcode() != spv::Op::OpReturnValue) - return; + return true; assert(return_flag_ && "Did not generate the return flag variable."); @@ -551,6 +613,9 @@ const analysis::Constant* true_const = const_mgr->GetConstant(bool_type, {true}); constant_true_ = const_mgr->GetDefiningInstruction(true_const); + if (!constant_true_) { + return false; + } context()->UpdateDefUse(constant_true_); } @@ -564,6 +629,7 @@ &*block->tail().InsertBefore(std::move(return_store)); context()->set_instr_block(store_inst, block); context()->AnalyzeDefUse(store_inst); + return true; } void MergeReturnPass::RecordReturnValue(BasicBlock* block) { @@ -587,18 +653,21 @@ context()->AnalyzeDefUse(store_inst); } -void MergeReturnPass::AddReturnValue() { - if (return_value_) return; +bool MergeReturnPass::AddReturnValue() { + if (return_value_) return true; uint32_t return_type_id = function_->type_id(); if (get_def_use_mgr()->GetDef(return_type_id)->opcode() == spv::Op::OpTypeVoid) - return; + return true; uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType( return_type_id, spv::StorageClass::Function); uint32_t var_id = TakeNextId(); + if (var_id == 0) { + return false; + } std::unique_ptr<Instruction> returnValue( new Instruction(context(), spv::Op::OpVariable, return_ptr_type, var_id, std::initializer_list<Operand>{ @@ -614,27 +683,44 @@ context()->get_decoration_mgr()->CloneDecorations( function_->result_id(), var_id, {spv::Decoration::RelaxedPrecision}); + return true; } -void MergeReturnPass::AddReturnFlag() { - if (return_flag_) return; +bool MergeReturnPass::AddReturnFlag() { + if (return_flag_) return true; analysis::TypeManager* type_mgr = context()->get_type_mgr(); analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); analysis::Bool temp; uint32_t bool_id = type_mgr->GetTypeInstruction(&temp); + if (bool_id == 0) { + return false; + } analysis::Bool* bool_type = type_mgr->GetType(bool_id)->AsBool(); const analysis::Constant* false_const = const_mgr->GetConstant(bool_type, {false}); - uint32_t const_false_id = - const_mgr->GetDefiningInstruction(false_const)->result_id(); + Instruction* false_inst = const_mgr->GetDefiningInstruction(false_const); + if (false_inst == nullptr) { + return false; + } + uint32_t const_false_id = false_inst->result_id(); uint32_t bool_ptr_id = type_mgr->FindPointerToType(bool_id, spv::StorageClass::Function); + if (bool_ptr_id == 0) { + return false; + ; + } + uint32_t var_id = TakeNextId(); + + if (var_id == 0) { + return false; + } + std::unique_ptr<Instruction> returnFlag(new Instruction( context(), spv::Op::OpVariable, bool_ptr_id, var_id, std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS, @@ -648,6 +734,7 @@ return_flag_ = &*entry_block->begin(); context()->AnalyzeDefUse(return_flag_); context()->set_instr_block(return_flag_, entry_block); + return true; } std::vector<BasicBlock*> MergeReturnPass::CollectReturnBlocks( @@ -663,14 +750,16 @@ return return_blocks; } -void MergeReturnPass::MergeReturnBlocks( +bool MergeReturnPass::MergeReturnBlocks( Function* function, const std::vector<BasicBlock*>& return_blocks) { if (return_blocks.size() <= 1) { // No work to do. - return; + return true; } - CreateReturnBlock(); + if (!CreateReturnBlock()) { + return false; + } uint32_t return_id = final_return_block_->id(); auto ret_block_iter = --function->end(); // Create the PHI for the merged block (if necessary). @@ -687,6 +776,9 @@ if (!phi_ops.empty()) { // Need a PHI node to select the correct return value. uint32_t phi_result_id = TakeNextId(); + if (phi_result_id == 0) { + return false; + } uint32_t phi_type_id = function->type_id(); std::unique_ptr<Instruction> phi_inst(new Instruction( context(), spv::Op::OpPhi, phi_type_id, phi_result_id, phi_ops)); @@ -718,18 +810,22 @@ } get_def_use_mgr()->AnalyzeInstDefUse(ret_block_iter->GetLabelInst()); + return true; } -void MergeReturnPass::AddNewPhiNodes() { +bool MergeReturnPass::AddNewPhiNodes() { std::list<BasicBlock*> order; cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order); for (BasicBlock* bb : order) { - AddNewPhiNodes(bb); + if (!AddNewPhiNodes(bb)) { + return false; + } } + return true; } -void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) { +bool MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) { // New phi nodes are needed for any id whose definition used to dominate |bb|, // but no longer dominates |bb|. These are found by walking the dominator // tree starting at the original immediate dominator of |bb| and ending at its @@ -747,16 +843,19 @@ BasicBlock* dominator = dom_tree->ImmediateDominator(bb); if (dominator == nullptr) { - return; + return true; } BasicBlock* current_bb = context()->get_instr_block(original_dominator_[bb]); while (current_bb != nullptr && current_bb != dominator) { for (Instruction& inst : *current_bb) { - CreatePhiNodesForInst(bb, inst); + if (!CreatePhiNodesForInst(bb, inst)) { + return false; + } } current_bb = dom_tree->ImmediateDominator(current_bb); } + return true; } void MergeReturnPass::RecordImmediateDominators(Function* function) { @@ -781,8 +880,12 @@ } bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() { - CreateReturnBlock(); - CreateReturn(final_return_block_); + if (!CreateReturnBlock()) { + return false; + } + if (!CreateReturn(final_return_block_)) { + return false; + } if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { cfg()->RegisterBlock(final_return_block_); @@ -828,15 +931,32 @@ 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. + // block to make sure the OpVariable instructions and DebugFunctionDefinition + // instructions remain in the entry block. BasicBlock* start_block = &*function_->begin(); auto split_pos = start_block->begin(); while (split_pos->opcode() == spv::Op::OpVariable) { ++split_pos; } + uint32_t new_block_id = TakeNextId(); + if (new_block_id == 0) { + return false; + } BasicBlock* old_block = - start_block->SplitBasicBlock(context(), TakeNextId(), split_pos); + start_block->SplitBasicBlock(context(), new_block_id, split_pos); + + // Find DebugFunctionDefinition inst in the old block, and if we can find it, + // move it to the entry block. Since DebugFunctionDefinition is not necessary + // after OpVariable inst, we have to traverse the whole block to find it. + for (auto pos = old_block->begin(); pos != old_block->end(); ++pos) { + if (pos->GetShaderDebugOpcode() == + NonSemanticShaderDebugInfoDebugFunctionDefinition) { + start_block->AddInstruction(MakeUnique<Instruction>(*pos)); + pos.Erase(); + break; + } + } // Add the switch to the end of the entry block. InstructionBuilder builder(
diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h index d15db2f..d83ffc3 100644 --- a/source/opt/merge_return_pass.h +++ b/source/opt/merge_return_pass.h
@@ -149,8 +149,9 @@ // Creates a new basic block with a single return. If |function| returns a // value, a phi node is created to select the correct value to return. - // Replaces old returns with an unconditional branch to the new block. - void MergeReturnBlocks(Function* function, + // Replaces old returns with an unconditional branch to the new block. Returns + // true if successful. + bool MergeReturnBlocks(Function* function, const std::vector<BasicBlock*>& returnBlocks); // Generate and push new control flow state if |block| contains a merge. @@ -172,21 +173,22 @@ // // Note this will break the semantics. To fix this, PredicateBlock will have // to be called on the merge block the branch targets. - void ProcessStructuredBlock(BasicBlock* block); + bool ProcessStructuredBlock(BasicBlock* block); // Creates a variable used to store whether or not the control flow has // traversed a block that used to have a return. A pointer to the instruction - // declaring the variable is stored in |return_flag_|. - void AddReturnFlag(); + // declaring the variable is stored in |return_flag_|. Returns true if it + // succeeds. + bool AddReturnFlag(); // Creates the variable used to store the return value when passing through - // a block that use to contain an OpReturnValue. - void AddReturnValue(); + // a block that use to contain an OpReturnValue. Returns true if it succeeds. + bool AddReturnValue(); - // Adds a store that stores true to |return_flag_| immediately before the - // terminator of |block|. It is assumed that |AddReturnFlag| has already been - // called. - void RecordReturned(BasicBlock* block); + // Records that |block| used to be a return. This is done by adding an + // instruction to store true to the |return_flag_|. Returns true if it + // succeeds. + bool RecordReturned(BasicBlock* block); // Adds an instruction that stores the value being returned in the // OpReturnValue in |block|. The value is stored to |return_value_|, and the @@ -197,10 +199,10 @@ // have already been called to create the variable to store to. void RecordReturnValue(BasicBlock* block); - // Adds an unconditional branch in |block| that branches to |target|. It also - // adds stores to |return_flag_| and |return_value_| as needed. - // |AddReturnFlag| and |AddReturnValue| must have already been called. - void BranchToBlock(BasicBlock* block, uint32_t target); + // Replaces the terminator of |block| with a branch to |target|. If the + // terminator was a return, it will first call RecordReturned and + // RecordReturnValue. Returns true if it succeeds. + bool BranchToBlock(BasicBlock* block, uint32_t target); // For every basic block that is reachable from |return_block|, extra code is // added to jump around any code that should not be executed because the @@ -231,38 +233,35 @@ // Add an |OpReturn| or |OpReturnValue| to the end of |block|. If an // |OpReturnValue| is needed, the return value is loaded from |return_value_|. - void CreateReturn(BasicBlock* block); + // Returns true if successful. + bool CreateReturn(BasicBlock* block); // Creates a block at the end of the function that will become the single // return block at the end of the pass. - void CreateReturnBlock(); + bool CreateReturnBlock(); - // Creates a Phi node in |merge_block| for the result of |inst|. - // Any uses of the result of |inst| that are no longer - // dominated by |inst|, are replaced with the result of the new |OpPhi| - // instruction. - void CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst); + // For each use of |inst| that is no longer dominated by |inst|, a phi node + // is created in |merge_block|. The original use is replaced by the result + // of the phi node. Returns true if it succeeds. + bool CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst); - // Add new phi nodes for any id that no longer dominate all of it uses. A phi - // node is added to a block |bb| for an id if the id is defined between the - // original immediate dominator of |bb| and its new immediate dominator. It - // is assumed that at this point there are no unreachable blocks in the - // control flow graph. - void AddNewPhiNodes(); + // Adds new phi nodes as needed to the function. This is necessary because + // adding the predication code can change the dominator tree. Returns false + // if there is a failure. + bool AddNewPhiNodes(); - // Creates any new phi nodes that are needed in |bb|. |AddNewPhiNodes| must - // have already been called on the original dominators of |bb|. - void AddNewPhiNodes(BasicBlock* bb); + // Adds new phi nodes to |bb| as needed. This is necessary because adding + // the predication code can change the dominator tree. Returns false if + // there is a failure. + bool AddNewPhiNodes(BasicBlock* bb); // Records the terminator of immediate dominator for every basic block in // |function|. void RecordImmediateDominators(Function* function); - // Modifies existing OpPhi instruction in |target| block to account for the - // new edge from |new_source|. The value for that edge will be an Undef. - // - // The CFG must not include the edge from |new_source| to |target| yet. - void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target); + // For each OpPhi instruction in |target|, this function adds an operand for + // |new_source|. The value will be OpUndef. Returns true if it succeeds. + bool UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target); StructuredControlState& CurrentState() { return state_.back(); }
diff --git a/source/opt/module.cpp b/source/opt/module.cpp index a9710c6..86f6a28 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp
@@ -93,6 +93,7 @@ if (sampled_image_address_mode_) sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts); DELEGATE(entry_points_); + DELEGATE(graph_entry_points_); DELEGATE(execution_modes_); DELEGATE(debugs1_); DELEGATE(debugs2_); @@ -104,6 +105,10 @@ i->ForEachInst(f, run_on_debug_line_insts, /* run_on_non_semantic_insts = */ true); } + for (auto& g : graphs_) { + g->ForEachInst(f, run_on_debug_line_insts, + /* run_on_non_semantic_insts = */ true); + } #undef DELEGATE } @@ -132,6 +137,12 @@ f, run_on_debug_line_insts, /* run_on_non_semantic_insts = */ true); } + for (auto& i : graph_entry_points_) DELEGATE(i); + for (auto& i : graphs_) { + static_cast<const Graph*>(i.get())->ForEachInst( + f, run_on_debug_line_insts, + /* run_on_non_semantic_insts = */ true); + } if (run_on_debug_line_insts) { for (auto& i : trailing_dbg_line_info_) DELEGATE(i); } @@ -176,16 +187,15 @@ // If the current instruction does not have the line information, // the last line information is not effective any more. Emit OpNoLine // or DebugNoLine to specify it. - uint32_t shader_set_id = context() - ->get_feature_mgr() - ->GetExtInstImportId_Shader100DebugInfo(); + uint32_t shader_set_id = + context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo(); if (shader_set_id != 0) { binary->push_back((5 << 16) | static_cast<uint16_t>(spv::Op::OpExtInst)); binary->push_back(context()->get_type_mgr()->GetVoidTypeId()); binary->push_back(context()->TakeNextId()); binary->push_back(shader_set_id); - binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine); + binary->push_back(NonSemanticShaderDebugInfoDebugNoLine); } else { binary->push_back((1 << 16) | static_cast<uint16_t>(spv::Op::OpNoLine)); @@ -206,7 +216,7 @@ if (scope != last_scope && !between_merge_and_branch) { // Can only emit nonsemantic instructions after all phi instructions // in a block so don't emit scope instructions before phi instructions - // for NonSemantic.Shader.DebugInfo.100. + // for NonSemantic.Shader.DebugInfo. if (!between_label_and_phi_var || context() ->get_feature_mgr()
diff --git a/source/opt/module.h b/source/opt/module.h index 98c16dc..0f32995 100644 --- a/source/opt/module.h +++ b/source/opt/module.h
@@ -23,6 +23,7 @@ #include <vector> #include "source/opt/function.h" +#include "source/opt/graph.h" #include "source/opt/instruction.h" #include "source/opt/iterator.h" @@ -48,6 +49,8 @@ using const_iterator = UptrVectorIterator<Function, true>; using inst_iterator = InstructionList::iterator; using const_inst_iterator = InstructionList::const_iterator; + using graph_iterator = UptrVectorIterator<Graph>; + using const_graph_iterator = UptrVectorIterator<Graph, true>; // Creates an empty module with zero'd header. Module() : header_({}), contains_debug_info_(false) {} @@ -90,6 +93,9 @@ // Appends an entry point instruction to this module. inline void AddEntryPoint(std::unique_ptr<Instruction> e); + // Appends a graph entry point instruction to this module. + inline void AddGraphEntryPoint(std::unique_ptr<Instruction> e); + // Appends an execution mode instruction to this module. inline void AddExecutionMode(std::unique_ptr<Instruction> e); @@ -108,7 +114,7 @@ inline void AddDebug3Inst(std::unique_ptr<Instruction> d); // Appends a debug info extension (OpenCL.DebugInfo.100, - // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module. + // NonSemantic.Shader.DebugInfo, or DebugInfo) instruction to this module. inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d); // Appends an annotation instruction to this module. @@ -126,6 +132,9 @@ // Appends a function to this module. inline void AddFunction(std::unique_ptr<Function> f); + // Appends a graph to this module. + inline void AddGraph(std::unique_ptr<Graph> g); + // Sets |contains_debug_info_| as true. inline void SetContainsDebugInfo(); inline bool ContainsDebugInfo() { return contains_debug_info_; } @@ -220,6 +229,10 @@ inline IteratorRange<inst_iterator> entry_points(); inline IteratorRange<const_inst_iterator> entry_points() const; + // Iterators for graph entry point instructions contained in this module + inline IteratorRange<inst_iterator> graph_entry_points(); + inline IteratorRange<const_inst_iterator> graph_entry_points() const; + // Iterators for execution_modes instructions contained in this module. inline inst_iterator execution_mode_begin(); inline inst_iterator execution_mode_end(); @@ -252,6 +265,9 @@ inline const_iterator cbegin() const; inline const_iterator cend() const; + // Iterators for graphs contained in this module. + inline const std::vector<std::unique_ptr<Graph>>& graphs() const; + // Invokes function |f| on all instructions in this module, and optionally on // the debug line instructions that precede them. void ForEachInst(const std::function<void(Instruction*)>& f, @@ -306,6 +322,7 @@ // A module can only have one optional sampled image addressing mode std::unique_ptr<Instruction> sampled_image_address_mode_; InstructionList entry_points_; + InstructionList graph_entry_points_; InstructionList execution_modes_; InstructionList debugs1_; InstructionList debugs2_; @@ -315,6 +332,7 @@ // Type declarations, constants, and global variable declarations. InstructionList types_values_; std::vector<std::unique_ptr<Function>> functions_; + std::vector<std::unique_ptr<Graph>> graphs_; // If the module ends with Op*Line instruction, they will not be attached to // any instruction. We record them here, so they will not be lost. @@ -351,6 +369,10 @@ entry_points_.push_back(std::move(e)); } +inline void Module::AddGraphEntryPoint(std::unique_ptr<Instruction> e) { + graph_entry_points_.push_back(std::move(e)); +} + inline void Module::AddExecutionMode(std::unique_ptr<Instruction> e) { execution_modes_.push_back(std::move(e)); } @@ -392,6 +414,10 @@ functions_.emplace_back(std::move(f)); } +inline void Module::AddGraph(std::unique_ptr<Graph> g) { + graphs_.emplace_back(std::move(g)); +} + inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; } inline Module::inst_iterator Module::capability_begin() { @@ -482,6 +508,15 @@ return make_range(entry_points_.begin(), entry_points_.end()); } +inline IteratorRange<Module::inst_iterator> Module::graph_entry_points() { + return make_range(graph_entry_points_.begin(), graph_entry_points_.end()); +} + +inline IteratorRange<Module::const_inst_iterator> Module::graph_entry_points() + const { + return make_range(graph_entry_points_.begin(), graph_entry_points_.end()); +} + inline Module::inst_iterator Module::execution_mode_begin() { return execution_modes_.begin(); } @@ -544,6 +579,10 @@ return make_range(types_values_.begin(), types_values_.end()); } +inline const std::vector<std::unique_ptr<Graph>>& Module::graphs() const { + return graphs_; +} + inline Module::const_iterator Module::cbegin() const { return const_iterator(&functions_, functions_.cbegin()); }
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 94b15c6..6986501 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp
@@ -18,6 +18,7 @@ #include <charconv> #include <memory> #include <string> +#include <system_error> #include <unordered_map> #include <utility> #include <vector> @@ -148,7 +149,9 @@ .RegisterPass(CreateLocalSingleStoreElimPass()) .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateCombineAccessChainsPass()) .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateLegalizeMultidimArrayPass()) // Propagate constants to get as many constant conditions on branches // as possible. .RegisterPass(CreateCCPPass()) @@ -188,7 +191,7 @@ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) - .RegisterPass(CreateScalarReplacementPass()) + .RegisterPass(CreateScalarReplacementPass(0)) .RegisterPass(CreateLocalAccessChainConvertPass()) .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) @@ -202,7 +205,7 @@ .RegisterPass(CreateRedundancyEliminationPass()) .RegisterPass(CreateCombineAccessChainsPass()) .RegisterPass(CreateSimplificationPass()) - .RegisterPass(CreateScalarReplacementPass()) + .RegisterPass(CreateScalarReplacementPass(0)) .RegisterPass(CreateLocalAccessChainConvertPass()) .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) @@ -398,9 +401,11 @@ RegisterPass(CreateFoldSpecConstantOpAndCompositePass()); } else if (pass_name == "loop-unswitch") { RegisterPass(CreateLoopUnswitchPass()); + } else if (pass_name == "legalize-multidim-array") { + RegisterPass(CreateLegalizeMultidimArrayPass()); } else if (pass_name == "scalar-replacement") { if (pass_args.size() == 0) { - RegisterPass(CreateScalarReplacementPass()); + RegisterPass(CreateScalarReplacementPass(0)); } else { int limit = -1; if (pass_args.find_first_not_of("0123456789") == std::string::npos) { @@ -636,6 +641,12 @@ } } else if (pass_name == "trim-capabilities") { RegisterPass(CreateTrimCapabilitiesPass()); + } else if (pass_name == "split-combined-image-sampler") { + RegisterPass(CreateSplitCombinedImageSamplerPass()); + } else if (pass_name == "resolve-binding-conflicts") { + RegisterPass(CreateResolveBindingConflictsPass()); + } else if (pass_name == "canonicalize-ids") { + RegisterPass(CreateCanonicalizeIdsPass()); } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -954,6 +965,11 @@ MakeUnique<opt::LoopUnswitchPass>()); } +Optimizer::PassToken CreateLegalizeMultidimArrayPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::LegalizeMultidimArrayPass>()); +} + Optimizer::PassToken CreateRedundancyEliminationPass() { return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::RedundancyEliminationPass>()); @@ -1187,6 +1203,21 @@ MakeUnique<opt::OpExtInstWithForwardReferenceFixupPass>()); } +Optimizer::PassToken CreateSplitCombinedImageSamplerPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::SplitCombinedImageSamplerPass>()); +} + +Optimizer::PassToken CreateResolveBindingConflictsPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::ResolveBindingConflictsPass>()); +} + +Optimizer::PassToken CreateCanonicalizeIdsPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::CanonicalizeIdsPass>()); +} + } // namespace spvtools extern "C" {
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp index 0f260e2..ce37f36 100644 --- a/source/opt/pass.cpp +++ b/source/opt/pass.cpp
@@ -114,8 +114,12 @@ assert(length_const->AsIntConstant()); uint32_t array_length = length_const->AsIntConstant()->GetU32(); for (uint32_t i = 0; i < array_length; i++) { + // TODO(1841): Handle id overflow. Instruction* extract = ir_builder.AddCompositeExtract( original_element_type_id, object_to_copy->result_id(), {i}); + if (extract == nullptr) { + return 0; + } uint32_t new_id = GenerateCopy(extract, new_element_type_id, insertion_position); if (new_id == 0) { @@ -124,16 +128,24 @@ element_ids.push_back(new_id); } - return ir_builder.AddCompositeConstruct(new_type_id, element_ids) - ->result_id(); + Instruction* construct = + ir_builder.AddCompositeConstruct(new_type_id, element_ids); + if (construct == nullptr) { + return 0; + } + return construct->result_id(); } case spv::Op::OpTypeStruct: { std::vector<uint32_t> element_ids; for (uint32_t i = 0; i < original_type->NumInOperands(); i++) { uint32_t orig_member_type_id = original_type->GetSingleWordInOperand(i); uint32_t new_member_type_id = new_type->GetSingleWordInOperand(i); + // TODO(1841): Handle id overflow. Instruction* extract = ir_builder.AddCompositeExtract( orig_member_type_id, object_to_copy->result_id(), {i}); + if (extract == nullptr) { + return 0; + } uint32_t new_id = GenerateCopy(extract, new_member_type_id, insertion_position); if (new_id == 0) { @@ -141,8 +153,12 @@ } element_ids.push_back(new_id); } - return ir_builder.AddCompositeConstruct(new_type_id, element_ids) - ->result_id(); + Instruction* construct = + ir_builder.AddCompositeConstruct(new_type_id, element_ids); + if (construct == nullptr) { + return 0; + } + return construct->result_id(); } default: // If we do not have an aggregate type, then we have a problem. Either we
diff --git a/source/opt/pass.h b/source/opt/pass.h index 3e6c4d0..73773e3 100644 --- a/source/opt/pass.h +++ b/source/opt/pass.h
@@ -25,6 +25,7 @@ #include "source/opt/def_use_manager.h" #include "source/opt/ir_context.h" #include "source/opt/module.h" +#include "source/util/status.h" #include "spirv-tools/libspirv.hpp" #include "types.h" @@ -46,11 +47,7 @@ // // The numbers for the cases are assigned to make sure that Failure & anything // is Failure, SuccessWithChange & any success is SuccessWithChange. - enum class Status { - Failure = 0x00, - SuccessWithChange = 0x10, - SuccessWithoutChange = 0x11, - }; + using Status = utils::Status; using ProcessFunction = std::function<bool(Function*)>;
diff --git a/source/opt/passes.h b/source/opt/passes.h index 3311529..533fc21 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h
@@ -21,6 +21,7 @@ #include "source/opt/amd_ext_to_khr.h" #include "source/opt/analyze_live_input_pass.h" #include "source/opt/block_merge_pass.h" +#include "source/opt/canonicalize_ids_pass.h" #include "source/opt/ccp_pass.h" #include "source/opt/cfg_cleanup_pass.h" #include "source/opt/code_sink.h" @@ -51,6 +52,7 @@ #include "source/opt/interface_var_sroa.h" #include "source/opt/interp_fixup_pass.h" #include "source/opt/invocation_interlock_placement_pass.h" +#include "source/opt/legalize_multidim_array_pass.h" #include "source/opt/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" #include "source/opt/local_redundancy_elimination.h" @@ -74,9 +76,11 @@ #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/resolve_binding_conflicts_pass.h" #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" #include "source/opt/simplification_pass.h" +#include "source/opt/split_combined_image_sampler_pass.h" #include "source/opt/spread_volatile_semantics.h" #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h"
diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp index 4904e05..ac33dfb 100644 --- a/source/opt/private_to_local_pass.cpp +++ b/source/opt/private_to_local_pass.cpp
@@ -90,13 +90,13 @@ Function* target_function = nullptr; context()->get_def_use_mgr()->ForEachUser( inst.result_id(), - [&target_function, &found_first_use, this](Instruction* use) { + [&target_function, &found_first_use, inst, this](Instruction* use) { BasicBlock* current_block = context()->get_instr_block(use); if (current_block == nullptr) { return; } - if (!IsValidUse(use)) { + if (!IsValidUse(use, inst.result_id())) { found_first_use = true; target_function = nullptr; return; @@ -153,7 +153,8 @@ return new_type_id; } -bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const { +bool PrivateToLocalPass::IsValidUse(const Instruction* inst, + uint32_t private_variable_id) const { // The cases in this switch have to match the cases in |UpdateUse|. // If we don't know how to update it, it is not valid. if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { @@ -161,13 +162,14 @@ } switch (inst->opcode()) { case spv::Op::OpLoad: - case spv::Op::OpStore: case spv::Op::OpImageTexelPointer: // Treat like a load return true; + case spv::Op::OpStore: + return inst->GetOperand(1).AsId() != private_variable_id; case spv::Op::OpAccessChain: return context()->get_def_use_mgr()->WhileEachUser( - inst, [this](const Instruction* user) { - if (!IsValidUse(user)) return false; + inst, [this, inst](const Instruction* user) { + if (!IsValidUse(user, inst->result_id())) return false; return true; }); case spv::Op::OpName:
diff --git a/source/opt/private_to_local_pass.h b/source/opt/private_to_local_pass.h index e96a965..93c1c50 100644 --- a/source/opt/private_to_local_pass.h +++ b/source/opt/private_to_local_pass.h
@@ -53,7 +53,7 @@ // Returns true is |inst| is a valid use of a pointer. In this case, a // valid use is one where the transformation is able to rewrite the type to // match a change in storage class of the original variable. - bool IsValidUse(const Instruction* inst) const; + bool IsValidUse(const Instruction* inst, uint32_t private_variable_id) const; // Given the result id of a pointer type, |old_type_id|, this function // returns the id of a the same pointer type except the storage class has
diff --git a/source/opt/propagator.cpp b/source/opt/propagator.cpp index 9cd6174..67f9132 100644 --- a/source/opt/propagator.cpp +++ b/source/opt/propagator.cpp
@@ -84,16 +84,19 @@ return status_changed; } -bool SSAPropagator::Simulate(Instruction* instr) { +Pass::Status SSAPropagator::Simulate(Instruction* instr) { bool changed = false; // Don't bother visiting instructions that should not be simulated again. if (!ShouldSimulateAgain(instr)) { - return changed; + return Pass::Status::SuccessWithoutChange; } BasicBlock* dest_bb = nullptr; PropStatus status = visit_fn_(instr, &dest_bb); + if (status == SSAPropagator::kFailed) { + return Pass::Status::Failure; + } bool status_changed = SetStatus(instr, status); if (status == kVarying) { @@ -112,7 +115,7 @@ AddControlEdge(e); } } - return false; + return Pass::Status::SuccessWithoutChange; } else if (status == kInteresting) { // Add the SSA edges coming out of this instruction if the propagation // status has changed. @@ -169,12 +172,13 @@ DontSimulateAgain(instr); } - return changed; + return changed ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; } -bool SSAPropagator::Simulate(BasicBlock* block) { +Pass::Status SSAPropagator::Simulate(BasicBlock* block) { if (block == ctx_->cfg()->pseudo_exit_block()) { - return false; + return Pass::Status::SuccessWithoutChange; } // Always simulate Phi instructions, even if we have simulated this block @@ -182,17 +186,29 @@ // incoming edges. When those edges are marked executable, the corresponding // operand can be simulated. bool changed = false; - block->ForEachPhiInst( - [&changed, this](Instruction* instr) { changed |= Simulate(instr); }); + bool succeeded = + block->WhileEachPhiInst([&changed, this](Instruction* instr) { + auto Status = Simulate(instr); + if (Status == Pass::Status::Failure) return false; + changed |= Status == Pass::Status::SuccessWithChange; + return true; + }); + if (!succeeded) { + return Pass::Status::Failure; + } // If this is the first time this block is being simulated, simulate every // statement in it. if (!BlockHasBeenSimulated(block)) { - block->ForEachInst([this, &changed](Instruction* instr) { - if (instr->opcode() != spv::Op::OpPhi) { - changed |= Simulate(instr); - } + succeeded = block->WhileEachInst([&changed, this](Instruction* instr) { + auto Status = Simulate(instr); + if (Status == Pass::Status::Failure) return false; + changed |= Status == Pass::Status::SuccessWithChange; + return true; }); + if (!succeeded) { + return Pass::Status::Failure; + } MarkBlockSimulated(block); @@ -203,7 +219,8 @@ } } - return changed; + return changed ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; } void SSAPropagator::Initialize(Function* fn) { @@ -245,7 +262,11 @@ // follow after all the blocks have been simulated. if (!blocks_.empty()) { auto block = blocks_.front(); - changed |= Simulate(block); + Pass::Status status = Simulate(block); + if (status == Pass::Status::Failure) { + return false; + } + changed |= status == Pass::Status::SuccessWithChange; blocks_.pop(); continue; } @@ -253,7 +274,11 @@ // Simulate edges from the SSA queue. if (!ssa_edge_uses_.empty()) { Instruction* instr = ssa_edge_uses_.front(); - changed |= Simulate(instr); + Pass::Status status = Simulate(instr); + if (status == Pass::Status::Failure) { + return changed; + } + changed |= status == Pass::Status::SuccessWithChange; ssa_edge_uses_.pop(); } }
diff --git a/source/opt/propagator.h b/source/opt/propagator.h index 71212c9..2727f24 100644 --- a/source/opt/propagator.h +++ b/source/opt/propagator.h
@@ -25,6 +25,7 @@ #include "source/opt/ir_context.h" #include "source/opt/module.h" +#include "source/opt/pass.h" namespace spvtools { namespace opt { @@ -182,7 +183,7 @@ public: // Lattice values used for propagation. See class documentation for // a description. - enum PropStatus { kNotInteresting, kInteresting, kVarying }; + enum PropStatus { kNotInteresting, kInteresting, kVarying, kFailed }; using VisitFunction = std::function<PropStatus(Instruction*, BasicBlock**)>; @@ -190,7 +191,9 @@ : ctx_(context), visit_fn_(visit_fn) {} // Runs the propagator on function |fn|. Returns true if changes were made to - // the function. Otherwise, it returns false. + // the function. Otherwise, it returns false. The user should check + // IRContext::id_overflow() to see if there was an error caused by reaching + // the max id. bool Run(Function* fn); // Returns true if the |i|th argument for |phi| comes through a CFG edge that @@ -218,13 +221,13 @@ // Simulate the execution |block| by calling |visit_fn_| on every instruction // in it. - bool Simulate(BasicBlock* block); + Pass::Status Simulate(BasicBlock* block); // Simulate the execution of |instr| by replacing all the known values in // every operand and determining whether the result is interesting for // propagation. This invokes the callback function |visit_fn_| to determine // the value computed by |instr|. - bool Simulate(Instruction* instr); + Pass::Status Simulate(Instruction* instr); // Returns true if |instr| should be simulated again. bool ShouldSimulateAgain(Instruction* instr) const {
diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp index 73a90f0..3162d79 100644 --- a/source/opt/reduce_load_size.cpp +++ b/source/opt/reduce_load_size.cpp
@@ -108,6 +108,7 @@ Instruction* new_access_chain = ir_builder.AddAccessChain( pointer_to_result_type_id, composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids); + // TODO(1841): Handle id overflow. Instruction* new_load = ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
diff --git a/source/opt/redundancy_elimination.cpp b/source/opt/redundancy_elimination.cpp index 398225b..61234fa 100644 --- a/source/opt/redundancy_elimination.cpp +++ b/source/opt/redundancy_elimination.cpp
@@ -33,12 +33,7 @@ DominatorTree& dom_tree = context()->GetDominatorAnalysis(&func)->GetDomTree(); - // Keeps track of all ids that contain a given value number. We keep - // track of multiple values because they could have the same value, but - // different decorations. - std::map<uint32_t, uint32_t> value_to_ids; - - if (EliminateRedundanciesFrom(dom_tree.GetRoot(), vnTable, value_to_ids)) { + if (EliminateRedundanciesFrom(dom_tree.GetRoot(), vnTable)) { modified = true; } } @@ -46,14 +41,21 @@ } bool RedundancyEliminationPass::EliminateRedundanciesFrom( - DominatorTreeNode* bb, const ValueNumberTable& vnTable, - std::map<uint32_t, uint32_t> value_to_ids) { - bool modified = EliminateRedundanciesInBB(bb->bb_, vnTable, &value_to_ids); - - for (auto dominated_bb : bb->children_) { - modified |= EliminateRedundanciesFrom(dominated_bb, vnTable, value_to_ids); + DominatorTreeNode* bb, const ValueNumberTable& vnTable) { + struct State { + DominatorTreeNode* node; + std::map<uint32_t, uint32_t> value_to_id_map; + }; + std::vector<State> todo; + todo.push_back({bb, std::map<uint32_t, uint32_t>()}); + bool modified = false; + for (size_t next_node = 0; next_node < todo.size(); next_node++) { + modified |= EliminateRedundanciesInBB(todo[next_node].node->bb_, vnTable, + &todo[next_node].value_to_id_map); + for (DominatorTreeNode* child : todo[next_node].node->children_) { + todo.push_back({child, todo[next_node].value_to_id_map}); + } } - return modified; } } // namespace opt
diff --git a/source/opt/redundancy_elimination.h b/source/opt/redundancy_elimination.h index 40451f4..8c6e16a 100644 --- a/source/opt/redundancy_elimination.h +++ b/source/opt/redundancy_elimination.h
@@ -46,8 +46,7 @@ // // Returns true if at least one instruction is deleted. bool EliminateRedundanciesFrom(DominatorTreeNode* bb, - const ValueNumberTable& vnTable, - std::map<uint32_t, uint32_t> value_to_ids); + const ValueNumberTable& vnTable); }; } // namespace opt
diff --git a/source/opt/reflect.h b/source/opt/reflect.h index ec7c2dd..db43d02 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h
@@ -44,7 +44,8 @@ opcode <= spv::Op::OpGroupMemberDecorate) || opcode == spv::Op::OpDecorateId || opcode == spv::Op::OpDecorateStringGOOGLE || - opcode == spv::Op::OpMemberDecorateStringGOOGLE; + opcode == spv::Op::OpMemberDecorateStringGOOGLE || + opcode == spv::Op::OpMemberDecorateIdEXT; } inline bool IsTypeInst(spv::Op opcode) { return spvOpcodeGeneratesType(opcode) ||
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp index 0df559b..a388155 100644 --- a/source/opt/remove_duplicates_pass.cpp +++ b/source/opt/remove_duplicates_pass.cpp
@@ -29,6 +29,7 @@ Pass::Status RemoveDuplicatesPass::Process() { bool modified = RemoveDuplicateCapabilities(); + modified |= RemoveDuplicateExtensions(); modified |= RemoveDuplicatesExtInstImports(); modified |= RemoveDuplicateTypes(); modified |= RemoveDuplicateDecorations(); @@ -36,6 +37,41 @@ return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } +bool RemoveDuplicatesPass::RemoveDuplicateExtensions() const { + bool modified = false; + + if (context()->extensions().empty()) { + return modified; + } + + // set of {condition ID, extension name} + // ID 0 means unconditional extension, ie., OpExtension, otherwise the ID is + // the condition operand of OpConditionalExtensionINTEL. + std::set<std::pair<uint32_t, std::string>> extensions; + for (auto* inst = &*context()->extension_begin(); inst;) { + uint32_t cond_id = 0; + uint32_t i_name = 0; + if (inst->opcode() == spv::Op::OpConditionalExtensionINTEL) { + cond_id = inst->GetOperand(0).AsId(); + i_name = 1; + } + + auto res = + extensions.insert({cond_id, inst->GetOperand(i_name).AsString()}); + + if (res.second) { + // Never seen before, keep it. + inst = inst->NextNode(); + } else { + // It's a duplicate, remove it. + inst = context()->KillInst(inst); + modified = true; + } + } + + return modified; +} + bool RemoveDuplicatesPass::RemoveDuplicateCapabilities() const { bool modified = false; @@ -43,16 +79,27 @@ return modified; } - std::unordered_set<uint32_t> capabilities; - for (auto* i = &*context()->capability_begin(); i;) { - auto res = capabilities.insert(i->GetSingleWordOperand(0u)); + // set of {condition ID, capability} + // ID 0 means unconditional capability, ie., OpCapability, otherwise the ID is + // the condition operand of OpConditionalCapabilityINTEL. + std::set<std::pair<uint32_t, uint32_t>> capabilities; + for (auto* inst = &*context()->capability_begin(); inst;) { + uint32_t cond_id = 0; + uint32_t i_cap = 0; + if (inst->opcode() == spv::Op::OpConditionalCapabilityINTEL) { + cond_id = inst->GetOperand(0).AsId(); + i_cap = 1; + } + + auto res = + capabilities.insert({cond_id, inst->GetSingleWordOperand(i_cap)}); if (res.second) { // Never seen before, keep it. - i = i->NextNode(); + inst = inst->NextNode(); } else { // It's a duplicate, remove it. - i = context()->KillInst(i); + inst = context()->KillInst(inst); modified = true; } }
diff --git a/source/opt/remove_duplicates_pass.h b/source/opt/remove_duplicates_pass.h index 038caa8..e5b3925 100644 --- a/source/opt/remove_duplicates_pass.h +++ b/source/opt/remove_duplicates_pass.h
@@ -37,6 +37,10 @@ Status Process() override; private: + // Remove duplicate extensions from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateExtensions() const; // Remove duplicate capabilities from the module // // Returns true if the module was modified, false otherwise.
diff --git a/source/opt/remove_unused_interface_variables_pass.cpp b/source/opt/remove_unused_interface_variables_pass.cpp index c3a4b77..61e6eac 100644 --- a/source/opt/remove_unused_interface_variables_pass.cpp +++ b/source/opt/remove_unused_interface_variables_pass.cpp
@@ -13,6 +13,7 @@ // limitations under the License. #include "remove_unused_interface_variables_pass.h" + #include "source/spirv_constant.h" namespace spvtools { namespace opt { @@ -33,7 +34,9 @@ instruction.ForEachInId([&](const uint32_t* id) { if (used_variables_.count(*id)) return; auto* var = parent_.get_def_use_mgr()->GetDef(*id); - if (!var || var->opcode() != spv::Op::OpVariable) return; + if (!var || (var->opcode() != spv::Op::OpVariable && + var->opcode() != spv::Op::OpUntypedVariableKHR)) + return; auto storage_class = spv::StorageClass(var->GetSingleWordInOperand(0)); if (storage_class != spv::StorageClass::Function && @@ -55,7 +58,9 @@ void CollectUsedVariables() { std::queue<uint32_t> roots; - roots.push(entry_.GetSingleWordInOperand(1)); + const int op_i = + entry_.opcode() == spv::Op::OpConditionalEntryPointINTEL ? 2 : 1; + roots.push(entry_.GetSingleWordInOperand(op_i)); parent_.context()->ProcessCallTreeFromRoots(pfn_, &roots); } @@ -73,7 +78,9 @@ } void Modify() { - for (int i = entry_.NumInOperands() - 1; i >= 3; --i) + const int min_num_operands = + entry_.opcode() == spv::Op::OpConditionalEntryPointINTEL ? 4 : 3; + for (int i = entry_.NumInOperands() - 1; i >= min_num_operands; --i) entry_.RemoveInOperand(i); for (auto id : operands_to_add_) { entry_.AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id}));
diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp index 59745e1..619fa93 100644 --- a/source/opt/replace_desc_array_access_using_var_index.cpp +++ b/source/opt/replace_desc_array_access_using_var_index.cpp
@@ -41,14 +41,15 @@ Status status = Status::SuccessWithoutChange; for (Instruction& var : context()->types_values()) { if (descsroautil::IsDescriptorArray(context(), &var)) { - if (ReplaceVariableAccessesWithConstantElements(&var)) - status = Status::SuccessWithChange; + Status s = ReplaceVariableAccessesWithConstantElements(&var); + if (s == Status::Failure) return Status::Failure; + if (s == Status::SuccessWithChange) status = Status::SuccessWithChange; } } return status; } -bool ReplaceDescArrayAccessUsingVarIndex:: +Pass::Status ReplaceDescArrayAccessUsingVarIndex:: ReplaceVariableAccessesWithConstantElements(Instruction* var) const { std::vector<Instruction*> work_list; get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) { @@ -66,16 +67,16 @@ for (Instruction* access_chain : work_list) { if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) == nullptr) { - ReplaceAccessChain(var, access_chain); + if (!ReplaceAccessChain(var, access_chain)) return Status::Failure; updated = true; } } // Note that we do not consider OpLoad and OpCompositeExtract because // OpCompositeExtract always has constant literals for indices. - return updated; + return updated ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain( +bool ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain( Instruction* var, Instruction* access_chain) const { uint32_t number_of_elements = descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); @@ -83,21 +84,23 @@ if (number_of_elements == 1) { UseConstIndexForAccessChain(access_chain, 0); get_def_use_mgr()->AnalyzeInstUse(access_chain); - return; + return true; } - ReplaceUsersOfAccessChain(access_chain, number_of_elements); + return ReplaceUsersOfAccessChain(access_chain, number_of_elements); } -void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain( +bool 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 = CollectRequiredImageAndAccessInsts(inst); - ReplaceNonUniformAccessWithSwitchCase( - inst, access_chain, number_of_elements, insts_to_be_cloned); + if (!ReplaceNonUniformAccessWithSwitchCase( + inst, access_chain, number_of_elements, insts_to_be_cloned)) + return false; } + return true; } void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType( @@ -208,17 +211,23 @@ 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; + std::unique_ptr<BasicBlock> case_block(CreateNewBlock()); + if (!case_block) return nullptr; + + if (!AddConstElementAccessToCaseBlock(case_block.get(), access_chain, + element_index, old_ids_to_new_ids)) { + return nullptr; + } + if (!CloneInstsToBlock(case_block.get(), access_chain, insts_to_be_cloned, + old_ids_to_new_ids)) { + return nullptr; + } + AddBranchToBlock(case_block.get(), branch_target_id); + UseNewIdsInBlock(case_block.get(), *old_ids_to_new_ids); + return case_block.release(); } -void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock( +bool 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 { @@ -227,6 +236,7 @@ std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context())); if (inst_to_be_cloned->HasResultId()) { uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) return false; clone->SetResultId(new_id); (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id; } @@ -234,6 +244,7 @@ context()->set_instr_block(clone.get(), block); block->AddInstruction(std::move(clone)); } + return true; } void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock( @@ -250,18 +261,19 @@ } } -void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( +bool ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( Instruction* access_chain_final_user, Instruction* access_chain, uint32_t number_of_elements, const std::deque<Instruction*>& insts_to_be_cloned) const { auto* block = context()->get_instr_block(access_chain_final_user); // If the instruction does not belong to a block (i.e. in the case of // OpDecorate), no replacement is needed. - if (!block) return; + if (!block) return true; // Create merge block and add terminator auto* merge_block = SeparateInstructionsIntoNewBlock( block, access_chain_final_user->NextNode()); + if (!merge_block) return false; auto* function = block->GetParent(); @@ -273,6 +285,7 @@ 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)); + if (!case_block) return false; case_block_ids.push_back(case_block->id()); function->InsertBasicBlockBefore(std::move(case_block), merge_block); @@ -288,6 +301,7 @@ std::unique_ptr<BasicBlock> default_block( CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands, merge_block->id())); + if (!default_block) return false; uint32_t default_block_id = default_block->id(); function->InsertBasicBlockBefore(std::move(default_block), merge_block); @@ -301,11 +315,13 @@ if (!phi_operands.empty()) { uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands, case_block_ids, default_block_id); + if (phi_id == 0) return false; 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()); + return true; } BasicBlock* @@ -316,13 +332,16 @@ &*separation_begin != separation_begin_inst) { ++separation_begin; } - return block->SplitBasicBlock(context(), context()->TakeNextId(), - separation_begin); + uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) return nullptr; + return block->SplitBasicBlock(context(), new_id, separation_begin); } BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const { - auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(new Instruction( - context(), spv::Op::OpLabel, 0, context()->TakeNextId(), {}))); + uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) return nullptr; + auto* new_block = new BasicBlock(std::unique_ptr<Instruction>( + new Instruction(context(), spv::Op::OpLabel, 0, new_id, {}))); get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst()); context()->set_instr_block(new_block->GetLabelInst(), new_block); return new_block; @@ -336,7 +355,7 @@ {const_element_idx_id}); } -void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock( +bool 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 { @@ -344,12 +363,14 @@ UseConstIndexForAccessChain(access_clone.get(), const_element_idx); uint32_t new_access_id = context()->TakeNextId(); + if (new_access_id == 0) return false; (*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)); + return true; } void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock( @@ -363,6 +384,7 @@ bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands, uint32_t merge_block_id) const { auto* default_block = CreateNewBlock(); + if (!default_block) return nullptr; AddBranchToBlock(default_block, merge_block_id); if (!null_const_for_phi_is_needed) return default_block; @@ -413,7 +435,11 @@ 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); + Instruction* phi = builder.AddPhi(phi_result_type_id, incomings); + if (!phi) { + return 0; + } + context()->get_def_use_mgr()->AnalyzeInstDefUse(phi); return phi->result_id(); }
diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h index 51817c1..b385532 100644 --- a/source/opt/replace_desc_array_access_using_var_index.h +++ b/source/opt/replace_desc_array_access_using_var_index.h
@@ -49,14 +49,13 @@ private: // Replaces all accesses to |var| using variable indices with constant // elements of the array |var|. Creates switch-case statements to determine - // the value of the variable index for all the possible cases. Returns - // whether replacement is done or not. - bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const; + // the value of the variable index for all the possible cases. + Status 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; + bool 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 @@ -68,7 +67,7 @@ // |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, + bool ReplaceUsersOfAccessChain(Instruction* access_chain, uint32_t number_of_elements) const; // Puts all the recursive users of |access_chain| with concrete result types @@ -102,7 +101,7 @@ // 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( + bool ReplaceNonUniformAccessWithSwitchCase( Instruction* access_chain_final_user, Instruction* access_chain, uint32_t number_of_elements, const std::deque<Instruction*>& non_uniform_accesses_to_clone) const; @@ -124,7 +123,7 @@ // |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( + bool 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; @@ -132,7 +131,7 @@ // 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( + bool 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; @@ -183,7 +182,8 @@ // |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|. + // adds the phi instruction to the beginning of |parent_block|. Returns 0 if + // it fails to create the Phi instruction. uint32_t CreatePhiInstruction(BasicBlock* parent_block, const std::vector<uint32_t>& phi_operands, const std::vector<uint32_t>& case_block_ids,
diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp index 1b97c0e..a2a2aee 100644 --- a/source/opt/replace_invalid_opc.cpp +++ b/source/opt/replace_invalid_opc.cpp
@@ -17,6 +17,8 @@ #include <bitset> #include <vector> +#include "source/table2.h" + namespace spvtools { namespace opt { @@ -104,7 +106,7 @@ uint32_t file_name_id = 0; if (last_line_dbg_inst->opcode() == spv::Op::OpLine) { file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0); - } else { // Shader100::DebugLine + } else { // NSDI::DebugLine uint32_t debug_source_id = last_line_dbg_inst->GetSingleWordInOperand(2); Instruction* debug_source_inst = @@ -207,10 +209,10 @@ } std::string ReplaceInvalidOpcodePass::BuildWarningMessage(spv::Op opcode) { - spv_opcode_desc opcode_info; - context()->grammar().lookupOpcode(opcode, &opcode_info); + const spvtools::InstructionDesc* opcode_desc = nullptr; + spvtools::LookupOpcode(opcode, &opcode_desc); std::string message = "Removing "; - message += opcode_info->name; + message += opcode_desc->name().data(); message += " instruction because of incompatible execution model."; return message; }
diff --git a/source/opt/resolve_binding_conflicts_pass.cpp b/source/opt/resolve_binding_conflicts_pass.cpp new file mode 100644 index 0000000..18ea2e5 --- /dev/null +++ b/source/opt/resolve_binding_conflicts_pass.cpp
@@ -0,0 +1,328 @@ +// Copyright (c) 2025 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/resolve_binding_conflicts_pass.h" + +#include <algorithm> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace opt { + +// A VarBindingInfo contains the binding information for a single resource +// variable. +// +// Exactly one such object is created per resource variable in the +// module. In particular, when a resource variable is statically used by +// more than one entry point, those entry points share the same VarBindingInfo +// object for that variable. +struct VarBindingInfo { + const Instruction* const var; + const uint32_t descriptor_set; + Instruction* const binding_decoration; + + // Returns the binding number. + uint32_t binding() const { + return binding_decoration->GetSingleWordInOperand(2); + } + // Sets the binding number to 'b'. + void updateBinding(uint32_t b) { binding_decoration->SetOperand(2, {b}); } +}; + +// The bindings in the same descriptor set that are used by an entry point. +using BindingList = std::vector<VarBindingInfo*>; +// A map from descriptor set number to the list of bindings in that descriptor +// set, as used by a particular entry point. +using DescriptorSets = std::unordered_map<uint32_t, BindingList>; + +IRContext::Analysis ResolveBindingConflictsPass::GetPreservedAnalyses() { + // All analyses are kept up to date. + // At most this modifies the Binding numbers on variables. + return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes | IRContext::kAnalysisDebugInfo | + IRContext::kAnalysisLiveness; +} + +// Orders variable binding info objects. +// * The binding number is most signficant; +// * Then a sampler-like object compares greater than non-sampler like object. +// * Otherwise compare based on variable ID. +// This provides a total order among bindings in a descriptor set for a valid +// Vulkan module. +bool Less(const VarBindingInfo* const lhs, const VarBindingInfo* const rhs) { + if (lhs->binding() < rhs->binding()) return true; + if (lhs->binding() > rhs->binding()) return false; + + // Examine types. + // In valid Vulkan the only conflict can occur between + // images and samplers. We only care about a specific + // comparison when one is a image-like thing and the other + // is a sampler-like thing of the same shape. So unwrap + // types until we hit one of those two. + + auto* def_use_mgr = lhs->var->context()->get_def_use_mgr(); + + // Returns the type found by iteratively following pointer pointee type, + // or array element type. + auto unwrap = [&def_use_mgr](Instruction* ty) { + bool keep_going = true; + do { + switch (ty->opcode()) { + case spv::Op::OpTypePointer: + ty = def_use_mgr->GetDef(ty->GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + ty = def_use_mgr->GetDef(ty->GetSingleWordInOperand(0)); + break; + default: + keep_going = false; + break; + } + } while (keep_going); + return ty; + }; + + auto* lhs_ty = unwrap(def_use_mgr->GetDef(lhs->var->type_id())); + auto* rhs_ty = unwrap(def_use_mgr->GetDef(rhs->var->type_id())); + if (lhs_ty->opcode() == rhs_ty->opcode()) { + // Pick based on variable ID. + return lhs->var->result_id() < rhs->var->result_id(); + } + // A sampler is always greater than an image. + if (lhs_ty->opcode() == spv::Op::OpTypeSampler) { + return false; + } + if (rhs_ty->opcode() == spv::Op::OpTypeSampler) { + return true; + } + // Pick based on variable ID. + return lhs->var->result_id() < rhs->var->result_id(); +} + +// Summarizes the caller-callee relationships between functions in a module. +class CallGraph { + public: + // Returns the list of all functions statically reachable from entry points, + // where callees precede callers. + const std::vector<uint32_t>& CalleesBeforeCallers() const { + return visit_order_; + } + // Returns the list functions called from a given function. + const std::unordered_set<uint32_t>& Callees(uint32_t caller) { + return calls_[caller]; + } + + CallGraph(IRContext& context) { + // Populate calls_. + std::queue<uint32_t> callee_queue; + for (const auto& fn : *context.module()) { + auto& callees = calls_[fn.result_id()]; + context.AddCalls(&fn, &callee_queue); + while (!callee_queue.empty()) { + callees.insert(callee_queue.front()); + callee_queue.pop(); + } + } + + // Perform depth-first search, starting from each entry point. + // Populates visit_order_. + for (const auto& ep : context.module()->entry_points()) { + Visit(ep.GetSingleWordInOperand(1)); + } + } + + private: + // Visits a function, recursively visiting its callees. Adds this ID + // to the visit_order after all callees have been visited. + void Visit(uint32_t func_id) { + if (visited_.count(func_id)) { + return; + } + visited_.insert(func_id); + for (auto callee_id : calls_[func_id]) { + Visit(callee_id); + } + visit_order_.push_back(func_id); + } + + // Maps the ID of a function to the IDs of functions it calls. + std::unordered_map<uint32_t, std::unordered_set<uint32_t>> calls_; + + // IDs of visited functions; + std::unordered_set<uint32_t> visited_; + // IDs of functions, where callees precede callers. + std::vector<uint32_t> visit_order_; +}; + +// Returns vector binding info for all resource variables in the module. +auto GetVarBindings(IRContext& context) { + std::vector<VarBindingInfo> vars; + auto* deco_mgr = context.get_decoration_mgr(); + for (auto& inst : context.module()->types_values()) { + if (inst.opcode() == spv::Op::OpVariable) { + Instruction* descriptor_set_deco = nullptr; + Instruction* binding_deco = nullptr; + for (auto* deco : deco_mgr->GetDecorationsFor(inst.result_id(), false)) { + switch (static_cast<spv::Decoration>(deco->GetSingleWordInOperand(1))) { + case spv::Decoration::DescriptorSet: + assert(!descriptor_set_deco); + descriptor_set_deco = deco; + break; + case spv::Decoration::Binding: + assert(!binding_deco); + binding_deco = deco; + break; + default: + break; + } + } + if (descriptor_set_deco && binding_deco) { + vars.push_back({&inst, descriptor_set_deco->GetSingleWordInOperand(2), + binding_deco}); + } + } + } + return vars; +} + +// Merges the bindings from source into sink. Maintains order and uniqueness +// within a list of bindings. +void Merge(DescriptorSets& sink, const DescriptorSets& source) { + for (auto index_and_bindings : source) { + const uint32_t index = index_and_bindings.first; + const BindingList& src1 = index_and_bindings.second; + const BindingList& src2 = sink[index]; + BindingList merged; + merged.resize(src1.size() + src2.size()); + auto merged_end = std::merge(src1.begin(), src1.end(), src2.begin(), + src2.end(), merged.begin(), Less); + auto unique_end = std::unique(merged.begin(), merged_end); + merged.resize(unique_end - merged.begin()); + sink[index] = std::move(merged); + } +} + +// Resolves conflicts within this binding list, so the binding number on an +// item is at least one more than the binding number on the previous item. +// When this does not yet hold, increase the binding number on the second +// item in the pair. Returns true if any changes were applied. +bool ResolveConflicts(BindingList& bl) { + bool changed = false; + for (size_t i = 1; i < bl.size(); i++) { + const auto prev_num = bl[i - 1]->binding(); + if (prev_num >= bl[i]->binding()) { + bl[i]->updateBinding(prev_num + 1); + changed = true; + } + } + return changed; +} + +Pass::Status ResolveBindingConflictsPass::Process() { + // Assumes the descriptor set and binding decorations are not provided + // via decoration groups. Decoration groups were deprecated in SPIR-V 1.3 + // Revision 6. I have not seen any compiler generate them. --dneto + + auto vars = GetVarBindings(*context()); + + // Maps a function ID to the variables used directly or indirectly by the + // function, organized into descriptor sets. Each descriptor set + // consists of a BindingList of distinct variables. + std::unordered_map<uint32_t, DescriptorSets> used_vars; + + // Determine variables directly used by functions. + auto* def_use_mgr = context()->get_def_use_mgr(); + for (auto& var : vars) { + std::unordered_set<uint32_t> visited_functions_for_var; + def_use_mgr->ForEachUser(var.var, [&](Instruction* user) { + if (auto* block = context()->get_instr_block(user)) { + auto* fn = block->GetParent(); + assert(fn); + const auto fn_id = fn->result_id(); + if (visited_functions_for_var.insert(fn_id).second) { + used_vars[fn_id][var.descriptor_set].push_back(&var); + } + } + }); + } + + // Sort within a descriptor set by binding number. + for (auto& sets_for_fn : used_vars) { + for (auto& ds : sets_for_fn.second) { + BindingList& bindings = ds.second; + std::stable_sort(bindings.begin(), bindings.end(), Less); + } + } + + // Propagate from callees to callers. + CallGraph call_graph(*context()); + for (const uint32_t caller : call_graph.CalleesBeforeCallers()) { + DescriptorSets& caller_ds = used_vars[caller]; + for (const uint32_t callee : call_graph.Callees(caller)) { + Merge(caller_ds, used_vars[callee]); + } + } + + // At this point, the descriptor sets associated with each entry point + // capture exactly the set of resource variables statically used + // by the static call tree of that entry point. + + // Resolve conflicts. + // VarBindingInfo objects may be shared between the bindings lists. + // Updating a binding in one list can require updating another list later. + // So repeat updates until settling. + + // The union of BindingLists across all entry points. + std::vector<BindingList*> ep_bindings; + + for (auto& ep : context()->module()->entry_points()) { + for (auto& ds : used_vars[ep.GetSingleWordInOperand(1)]) { + BindingList& bindings = ds.second; + ep_bindings.push_back(&bindings); + } + } + bool modified = false; + bool found_conflict; + do { + found_conflict = false; + for (BindingList* bl : ep_bindings) { + found_conflict |= ResolveConflicts(*bl); + } + modified |= found_conflict; + } while (found_conflict); + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/resolve_binding_conflicts_pass.h b/source/opt/resolve_binding_conflicts_pass.h new file mode 100644 index 0000000..d1ab433 --- /dev/null +++ b/source/opt/resolve_binding_conflicts_pass.h
@@ -0,0 +1,38 @@ +// Copyright (c) 2025 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 LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_ +#define LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_ + +#include <unordered_map> +#include <utility> +#include <vector> + +#include "source/diagnostic.h" +#include "source/opt/pass.h" +#include "source/util/small_vector.h" + +namespace spvtools { +namespace opt { +class ResolveBindingConflictsPass : public Pass { + public: + virtual ~ResolveBindingConflictsPass() override = default; + const char* name() const override { return "resolve-binding-conflicts"; } + IRContext::Analysis GetPreservedAnalyses() override; + Status Process() override; +}; +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 38c8aec..cdda380 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -184,7 +186,7 @@ Instruction* added_dbg_value = context()->get_debug_info_mgr()->AddDebugValueForDecl( dbg_decl, /*value_id=*/var->result_id(), - /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl); + /*insert_before=*/insert_before, /*line=*/dbg_decl); if (added_dbg_value == nullptr) return false; added_dbg_value->AddOperand( @@ -473,6 +475,7 @@ if (id == 0) { replacements->push_back(nullptr); + return; } std::unique_ptr<Instruction> variable( @@ -486,7 +489,10 @@ Instruction* inst = &*block->begin(); // If varInst was initialized, make sure to initialize its replacement. - GetOrCreateInitialValue(var_inst, index, inst); + if (!GetOrCreateInitialValue(var_inst, index, inst)) { + replacements->push_back(nullptr); + return; + } get_def_use_mgr()->AnalyzeInstDefUse(inst); context()->set_instr_block(inst, block); @@ -507,11 +513,11 @@ return ptr_type_id; } -void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source, +bool ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source, uint32_t index, Instruction* newVar) { assert(source->opcode() == spv::Op::OpVariable); - if (source->NumInOperands() < 2) return; + if (source->NumInOperands() < 2) return true; uint32_t initId = source->GetSingleWordInOperand(1u); uint32_t storageId = GetStorageType(newVar)->result_id(); @@ -523,6 +529,7 @@ auto iter = type_to_null_.find(storageId); if (iter == type_to_null_.end()) { newInitId = TakeNextId(); + if (newInitId == 0) return false; type_to_null_[storageId] = newInitId; context()->AddGlobalValue( MakeUnique<Instruction>(context(), spv::Op::OpConstantNull, storageId, @@ -535,6 +542,7 @@ } else if (IsSpecConstantInst(init->opcode())) { // Create a new constant extract. newInitId = TakeNextId(); + if (newInitId == 0) return false; context()->AddGlobalValue(MakeUnique<Instruction>( context(), spv::Op::OpSpecConstantOp, storageId, newInitId, std::initializer_list<Operand>{ @@ -559,6 +567,7 @@ if (newInitId != 0) { newVar->AddOperand({SPV_OPERAND_TYPE_ID, {newInitId}}); } + return true; } uint64_t ScalarReplacementPass::GetArrayLength( @@ -671,7 +680,8 @@ for (auto inst : get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) { uint32_t decoration; - if (inst->opcode() == spv::Op::OpDecorate) { + if (inst->opcode() == spv::Op::OpDecorate || + inst->opcode() == spv::Op::OpDecorateId) { decoration = inst->GetSingleWordInOperand(1u); } else { assert(inst->opcode() == spv::Op::OpMemberDecorate);
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h index c73ecfd..77d5bd5 100644 --- a/source/opt/scalar_replacement_pass.h +++ b/source/opt/scalar_replacement_pass.h
@@ -33,7 +33,7 @@ // Documented in optimizer.hpp class ScalarReplacementPass : public MemPass { private: - static constexpr uint32_t kDefaultLimit = 100; + static constexpr uint32_t kDefaultLimit = 0; public: ScalarReplacementPass(uint32_t limit = kDefaultLimit) @@ -199,7 +199,9 @@ // If there is an initial value for |source| for element |index|, it is // appended as an operand on |newVar|. If the initial value is OpUndef, no // initial value is added to |newVar|. - void GetOrCreateInitialValue(Instruction* source, uint32_t index, + // + // Returns true if the value was successfully created. + bool GetOrCreateInitialValue(Instruction* source, uint32_t index, Instruction* newVar); // Replaces the load to the entire composite.
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp index d2aa9b1..523253f 100644 --- a/source/opt/set_spec_constant_default_value_pass.cpp +++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -16,8 +16,9 @@ #include <algorithm> #include <cctype> +#include <cstdint> #include <cstring> -#include <tuple> +#include <initializer_list> #include <vector> #include "source/opt/def_use_manager.h" @@ -127,6 +128,11 @@ } return result; } + } else if (type->AsArray()) { + // This is only for OpSpecConstantDataKHR + // Since the length can be a spec constant as well, + // we just pass through the bit pattern + return std::vector<uint32_t>(input_bit_pattern); } result.clear(); return result; @@ -139,6 +145,7 @@ case spv::Op::OpSpecConstant: case spv::Op::OpSpecConstantFalse: case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantDataKHR: return true; default: return false; @@ -224,6 +231,9 @@ constexpr uint32_t kOpDecorateSpecIdNumOperands = 3; // The in-operand index of the default value in a OpSpecConstant instruction. constexpr uint32_t kOpSpecConstantLiteralInOperandIndex = 0; + // The in-operand index of the default value in a OpSpecConstantData + // instruction. + constexpr uint32_t kOpSpecConstantDataLiteralInOperandIndex = 0; bool modified = false; // Scan through all the annotation instructions to find 'OpDecorate SpecId' @@ -324,6 +334,23 @@ modified = true; } break; + case spv::Op::OpSpecConstantDataKHR: { + if (spec_inst->GetInOperand(kOpSpecConstantDataLiteralInOperandIndex) + .words != bit_pattern) { + std::vector<Operand> operands; + // keep the result/type + operands.push_back(spec_inst->GetOperand(0u)); + operands.push_back(spec_inst->GetOperand(1u)); + // the validator is in charge of making sure the length matches + for (uint32_t word : bit_pattern) { + operands.emplace_back(SPV_OPERAND_TYPE_LITERAL_INTEGER, + std::initializer_list<uint32_t>{word}); + } + spec_inst->ReplaceOperands(operands); + modified = true; + } + break; + } default: break; }
diff --git a/source/opt/split_combined_image_sampler_pass.cpp b/source/opt/split_combined_image_sampler_pass.cpp new file mode 100644 index 0000000..b5d3d65 --- /dev/null +++ b/source/opt/split_combined_image_sampler_pass.cpp
@@ -0,0 +1,694 @@ +// Copyright (c) 2025 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/split_combined_image_sampler_pass.h" + +#include <algorithm> +#include <cassert> +#include <memory> + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/type_manager.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace opt { + +#define CHECK(cond) \ + { \ + if ((cond) != SPV_SUCCESS) return Pass::Status::Failure; \ + } + +#define CHECK_STATUS(cond) \ + { \ + if (auto c = (cond); c != SPV_SUCCESS) return c; \ + } + +IRContext::Analysis SplitCombinedImageSamplerPass::GetPreservedAnalyses() { + return + // def use manager is updated + IRContext::kAnalysisDefUse + + // decorations are updated + | IRContext::kAnalysisDecorations + + // control flow is not changed + | IRContext::kAnalysisCFG // + | IRContext::kAnalysisLoopAnalysis // + | IRContext::kAnalysisStructuredCFG + + // type manager is updated + | IRContext::kAnalysisTypes; +} + +Pass::Status SplitCombinedImageSamplerPass::Process() { + def_use_mgr_ = context()->get_def_use_mgr(); + type_mgr_ = context()->get_type_mgr(); + + FindCombinedTextureSamplers(); + if (combined_types_to_remove_.empty() && !sampled_image_used_as_param_) { + return Ok(); + } + + CHECK(RemapFunctions()); + CHECK(RemapVars()); + CHECK(RemoveDeadTypes()); + + def_use_mgr_ = nullptr; + type_mgr_ = nullptr; + + return Ok(); +} + +spvtools::DiagnosticStream SplitCombinedImageSamplerPass::Fail() { + return std::move( + spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY) + << "split-combined-image-sampler: "); +} + +void SplitCombinedImageSamplerPass::FindCombinedTextureSamplers() { + for (auto& inst : context()->types_values()) { + RegisterGlobal(inst.result_id()); + switch (inst.opcode()) { + case spv::Op::OpTypeSampler: + // Modules can't have duplicate sampler types. + assert(!sampler_type_); + sampler_type_ = &inst; + break; + + case spv::Op::OpTypeSampledImage: + if (!first_sampled_image_type_) { + first_sampled_image_type_ = &inst; + } + combined_types_.insert(inst.result_id()); + def_use_mgr_->WhileEachUser(inst.result_id(), [&](Instruction* i) { + sampled_image_used_as_param_ |= + i->opcode() == spv::Op::OpTypeFunction; + return !sampled_image_used_as_param_; + }); + break; + + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: { + auto pointee_id = inst.GetSingleWordInOperand(0); + if (combined_types_.find(pointee_id) != combined_types_.end()) { + combined_types_.insert(inst.result_id()); + combined_types_to_remove_.push_back(inst.result_id()); + } + } break; + + case spv::Op::OpTypePointer: { + auto sc = + static_cast<spv::StorageClass>(inst.GetSingleWordInOperand(0)); + if (sc == spv::StorageClass::UniformConstant) { + auto pointee_id = inst.GetSingleWordInOperand(1); + if (combined_types_.find(pointee_id) != combined_types_.end()) { + combined_types_.insert(inst.result_id()); + combined_types_to_remove_.push_back(inst.result_id()); + } + } + } break; + + case spv::Op::OpVariable: + if (combined_types_.find(inst.type_id()) != combined_types_.end()) { + ordered_vars_.push_back(&inst); + } + break; + + default: + break; + } + } +} + +Instruction* SplitCombinedImageSamplerPass::GetSamplerType() { + if (!sampler_type_) { + analysis::Sampler s; + uint32_t sampler_type_id = type_mgr_->GetTypeInstruction(&s); + sampler_type_ = def_use_mgr_->GetDef(sampler_type_id); + if (sampler_type_ == nullptr) return nullptr; + assert(first_sampled_image_type_); + sampler_type_->InsertBefore(first_sampled_image_type_); + RegisterNewGlobal(sampler_type_->result_id()); + } + return sampler_type_; +} + +spv_result_t SplitCombinedImageSamplerPass::RemapVars() { + for (Instruction* var : ordered_vars_) { + CHECK_STATUS(RemapVar(var)); + } + return SPV_SUCCESS; +} + +std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType( + Instruction& combined_kind_type) { + if (auto where = type_remap_.find(combined_kind_type.result_id()); + where != type_remap_.end()) { + auto& type_remap = where->second; + return {type_remap.image_kind_type, type_remap.sampler_kind_type}; + } + + switch (combined_kind_type.opcode()) { + case spv::Op::OpTypeSampledImage: { + auto* image_type = + def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(0)); + auto* sampler_type = GetSamplerType(); + if (!sampler_type) return {nullptr, nullptr}; + type_remap_[combined_kind_type.result_id()] = {&combined_kind_type, + image_type, sampler_type}; + return {image_type, sampler_type}; + break; + } + case spv::Op::OpTypePointer: { + auto sc = static_cast<spv::StorageClass>( + combined_kind_type.GetSingleWordInOperand(0)); + if (sc == spv::StorageClass::UniformConstant) { + auto* pointee = + def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(1)); + auto [image_pointee, sampler_pointee] = SplitType(*pointee); + // These would be null if the pointee is an image type or a sampler + // type. Don't decompose them. Currently this method does not check the + // assumption that it is being only called on combined types. So code + // this defensively. + if (image_pointee && sampler_pointee) { + auto* ptr_image = MakeUniformConstantPointer(image_pointee); + if (!ptr_image) return {nullptr, nullptr}; + auto* ptr_sampler = MakeUniformConstantPointer(sampler_pointee); + if (!ptr_sampler) return {nullptr, nullptr}; + type_remap_[combined_kind_type.result_id()] = { + &combined_kind_type, ptr_image, ptr_sampler}; + return {ptr_image, ptr_sampler}; + } + } + break; + } + case spv::Op::OpTypeArray: { + const auto* array_ty = + type_mgr_->GetType(combined_kind_type.result_id())->AsArray(); + assert(array_ty); + const auto* sampled_image_ty = array_ty->element_type()->AsSampledImage(); + assert(sampled_image_ty); + + const analysis::Type* image_ty = sampled_image_ty->image_type(); + assert(image_ty); + analysis::Array array_image_ty(image_ty, array_ty->length_info()); + const uint32_t array_image_ty_id = + type_mgr_->GetTypeInstruction(&array_image_ty); + if (array_image_ty_id == 0) return {nullptr, nullptr}; + auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id); + if (!IsKnownGlobal(array_image_ty_id)) { + array_image_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_image_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + + auto* sampler_ty_inst = GetSamplerType(); + if (!sampler_ty_inst) return {nullptr, nullptr}; + analysis::Array sampler_array_ty( + type_mgr_->GetType(sampler_ty_inst->result_id()), + array_ty->length_info()); + const uint32_t array_sampler_ty_id = + type_mgr_->GetTypeInstruction(&sampler_array_ty); + if (array_sampler_ty_id == 0) return {nullptr, nullptr}; + auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id); + if (!IsKnownGlobal(array_sampler_ty_id)) { + array_sampler_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_sampler_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + return {array_image_ty_inst, array_sampler_ty_inst}; + } + case spv::Op::OpTypeRuntimeArray: { + // This is like the sized-array case, but there is no length parameter. + auto* array_ty = + type_mgr_->GetType(combined_kind_type.result_id())->AsRuntimeArray(); + assert(array_ty); + auto* sampled_image_ty = array_ty->element_type()->AsSampledImage(); + assert(sampled_image_ty); + + const analysis::Type* image_ty = sampled_image_ty->image_type(); + assert(image_ty); + analysis::RuntimeArray array_image_ty(image_ty); + const uint32_t array_image_ty_id = + type_mgr_->GetTypeInstruction(&array_image_ty); + if (array_image_ty_id == 0) return {nullptr, nullptr}; + auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id); + if (!IsKnownGlobal(array_image_ty_id)) { + array_image_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_image_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + + auto* sampler_ty_inst = GetSamplerType(); + if (!sampler_ty_inst) return {nullptr, nullptr}; + analysis::RuntimeArray sampler_array_ty( + type_mgr_->GetType(sampler_ty_inst->result_id())); + const uint32_t array_sampler_ty_id = + type_mgr_->GetTypeInstruction(&sampler_array_ty); + if (array_sampler_ty_id == 0) return {nullptr, nullptr}; + auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id); + if (!IsKnownGlobal(array_sampler_ty_id)) { + array_sampler_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_sampler_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + return {array_image_ty_inst, array_sampler_ty_inst}; + } + default: + break; + } + return {nullptr, nullptr}; +} + +spv_result_t SplitCombinedImageSamplerPass::RemapVar( + Instruction* combined_var) { + InstructionBuilder builder(context(), combined_var, + IRContext::kAnalysisDefUse); + + // Create an image variable, and a sampler variable. + auto* combined_var_type = def_use_mgr_->GetDef(combined_var->type_id()); + auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type); + if (!ptr_image_ty || !ptr_sampler_ty) return SPV_ERROR_INTERNAL; + Instruction* sampler_var = builder.AddVariable( + ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant); + if (sampler_var == nullptr) return SPV_ERROR_INTERNAL; + Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(), + SpvStorageClassUniformConstant); + if (image_var == nullptr) return SPV_ERROR_INTERNAL; + + modified_ = true; + return RemapUses(combined_var, image_var, sampler_var); +} + +spv_result_t SplitCombinedImageSamplerPass::RemapUses( + Instruction* combined, Instruction* image_part, Instruction* sampler_part) { + // The instructions to delete. + std::unordered_set<Instruction*> dead_insts; + // The insertion point should be updated before using this builder. + // We needed *something* here. + InstructionBuilder builder(context(), combined, IRContext::kAnalysisDefUse); + + // This code must maintain the SPIR-V "Data rule" about sampled image values: + // > All OpSampledImage instructions, or instructions that load an image or + // > sampler reference, must be in the same block in which their Result <id> + // > are consumed. + // + // When the code below inserts OpSampledImage instructions, it is always + // either: + // - in the same block as the previous OpSampledImage instruction it is + // replacing, or + // - in the same block as the instruction using sampled image value it is + // replacing. + // + // Assuming that rule is already honoured by the module, these updates will + // continue to honour the rule. + + // Represents a single use of a value to be remapped. + struct RemapUse { + uint32_t used_id; // The ID that is being used. + Instruction* user; + uint32_t index; + Instruction* image_part; // The image part of the replacement. + Instruction* sampler_part; // The sampler part of the replacement. + }; + // The work list of uses to be remapped. + std::vector<RemapUse> uses; + + // Adds remap records for each use of a value to be remapped. + // Also schedules the original value for deletion. + auto add_remap = [this, &dead_insts, &uses](Instruction* combined_arg, + Instruction* image_part_arg, + Instruction* sampler_part_arg) { + const uint32_t used_combined_id = combined_arg->result_id(); + + def_use_mgr_->ForEachUse( + combined_arg, [&](Instruction* user, uint32_t use_index) { + uses.push_back({used_combined_id, user, use_index, image_part_arg, + sampler_part_arg}); + }); + dead_insts.insert(combined_arg); + }; + + add_remap(combined, image_part, sampler_part); + + // Use index-based iteration because we can add to the work list as we go + // along, and reallocation would invalidate ordinary iterators. + for (size_t use_index = 0; use_index < uses.size(); ++use_index) { + auto& use = uses[use_index]; + switch (use.user->opcode()) { + case spv::Op::OpCopyObject: { + // Append the uses of this OpCopyObject to the work list. + add_remap(use.user, image_part, sampler_part); + break; + } + case spv::Op::OpLoad: { + assert(use.index == 2 && "variable used as non-pointer index on load"); + Instruction* load = use.user; + + // Assume the loaded value is a sampled image. + assert(def_use_mgr_->GetDef(load->type_id())->opcode() == + spv::Op::OpTypeSampledImage); + + // Create loads for the image part and sampler part. + builder.SetInsertPoint(load); + auto* image = builder.AddLoad(PointeeTypeId(use.image_part), + use.image_part->result_id()); + if (!image) return SPV_ERROR_INTERNAL; + auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part), + use.sampler_part->result_id()); + if (!sampler) return SPV_ERROR_INTERNAL; + + // Move decorations, such as RelaxedPrecision. + auto* deco_mgr = context()->get_decoration_mgr(); + deco_mgr->CloneDecorations(load->result_id(), image->result_id()); + deco_mgr->CloneDecorations(load->result_id(), sampler->result_id()); + deco_mgr->RemoveDecorationsFrom(load->result_id()); + + // Create a sampled image from the loads of the two parts. + auto* sampled_image = builder.AddSampledImage( + load->type_id(), image->result_id(), sampler->result_id()); + if (!sampled_image) return SPV_ERROR_INTERNAL; + // Replace the original sampled image value with the new one. + std::unordered_set<Instruction*> users; + def_use_mgr_->ForEachUse( + load, [&users, sampled_image](Instruction* user, uint32_t index) { + user->SetOperand(index, {sampled_image->result_id()}); + users.insert(user); + }); + for (auto* user : users) { + def_use_mgr_->AnalyzeInstUse(user); + } + dead_insts.insert(load); + break; + } + case spv::Op::OpDecorate: { + assert(use.index == 0 && "variable used as non-target index"); + builder.SetInsertPoint(use.user); + spv::Decoration deco{use.user->GetSingleWordInOperand(1)}; + std::vector<uint32_t> literals; + for (uint32_t i = 2; i < use.user->NumInOperands(); i++) { + literals.push_back(use.user->GetSingleWordInOperand(i)); + } + builder.AddDecoration(use.image_part->result_id(), deco, literals); + builder.AddDecoration(use.sampler_part->result_id(), deco, literals); + // KillInst will delete names and decorations, so don't schedule a + // deletion of this instruction. + break; + } + case spv::Op::OpEntryPoint: { + // The entry point lists variables in the shader interface, i.e. + // module-scope variables referenced by the static call tree rooted + // at the entry point. (It can be a proper superset). Before SPIR-V + // 1.4, only Input and Output variables are listed; in 1.4 and later, + // module-scope variables in all storage classes are listed. + // If a combined image+sampler is listed by the entry point, then + // the separated image and sampler variables should be. + assert(use.index >= 3 && + "variable used in OpEntryPoint but not as an interface ID"); + use.user->SetOperand(use.index, {use.image_part->result_id()}); + use.user->InsertOperand( + use.user->NumOperands(), + {SPV_OPERAND_TYPE_ID, {use.sampler_part->result_id()}}); + def_use_mgr_->AnalyzeInstUse(use.user); + break; + } + case spv::Op::OpName: { + // Synthesize new names from the old. + const auto name = use.user->GetOperand(1).AsString(); + AddOpName(use.image_part->result_id(), name + "_image"); + AddOpName(use.sampler_part->result_id(), name + "_sampler"); + + // KillInst will delete names and decorations, so don't schedule a + // deletion of this instruction. + break; + } + case spv::Op::OpFunctionCall: { + // Replace each combined arg with two args: the image part, then the + // sampler part. + // The combined value could have been used twice in the argument list. + // Moving things around now will invalidate the 'use' list above. + // So don't trust the use index value. + auto& call = *use.user; + // The insert API only takes absolute arg IDs, not "in" arg IDs. + const auto first_arg_operand_index = 3; // Skip the callee ID + for (uint32_t i = first_arg_operand_index; i < call.NumOperands(); + ++i) { + if (use.used_id == call.GetSingleWordOperand(i)) { + call.SetOperand(i, {use.sampler_part->result_id()}); + call.InsertOperand( + i, {SPV_OPERAND_TYPE_ID, {use.image_part->result_id()}}); + ++i; + } + } + def_use_mgr_->AnalyzeInstUse(&call); + break; + } + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + auto* original_access_chain = use.user; + builder.SetInsertPoint(original_access_chain); + // It can only be the base pointer + assert(use.index == 2); + + // Replace the original access chain with access chains for the image + // part and the sampler part. + std::vector<uint32_t> indices; + for (uint32_t i = 3; i < original_access_chain->NumOperands(); i++) { + indices.push_back(original_access_chain->GetSingleWordOperand(i)); + } + + auto [result_image_part_ty, result_sampler_part_ty] = + SplitType(*def_use_mgr_->GetDef(original_access_chain->type_id())); + if (!result_image_part_ty || !result_sampler_part_ty) + return Fail() << "failed to split type for access chain"; + auto* result_image_part = builder.AddOpcodeAccessChain( + use.user->opcode(), result_image_part_ty->result_id(), + use.image_part->result_id(), indices); + if (!result_image_part) + return Fail() << "failed to create access chain for image part"; + auto* result_sampler_part = builder.AddOpcodeAccessChain( + use.user->opcode(), result_sampler_part_ty->result_id(), + use.sampler_part->result_id(), indices); + if (!result_sampler_part) + return Fail() << "failed to create access chain for sampler part"; + + // Remap uses of the original access chain. + add_remap(original_access_chain, result_image_part, + result_sampler_part); + break; + } + default: { + uint32_t used_type_id = def_use_mgr_->GetDef(use.used_id)->type_id(); + auto* used_type = def_use_mgr_->GetDef(used_type_id); + if (used_type->opcode() == spv::Op::OpTypeSampledImage) { + // This value being used is a sampled image value. But it's + // being replaced, so recreate it here. + // Example: used by OpImage, OpImageSampleExplicitLod, etc. + builder.SetInsertPoint(use.user); + auto* sampled_image = + builder.AddSampledImage(used_type_id, use.image_part->result_id(), + use.sampler_part->result_id()); + use.user->SetOperand(use.index, {sampled_image->result_id()}); + def_use_mgr_->AnalyzeInstUse(use.user); + break; + } + return Fail() << "unhandled user: " << *use.user; + } + } + } + + for (auto* inst : dead_insts) { + KillInst(inst); + } + + return SPV_SUCCESS; +} + +spv_result_t SplitCombinedImageSamplerPass::RemapFunctions() { + // Remap function types. A combined type can appear as a parameter, but not as + // the return type. + { + std::unordered_set<Instruction*> dead_insts; + for (auto& inst : context()->types_values()) { + if (inst.opcode() != spv::Op::OpTypeFunction) { + continue; + } + analysis::Function* f_ty = + type_mgr_->GetType(inst.result_id())->AsFunction(); + std::vector<const analysis::Type*> new_params; + for (const auto* param_ty : f_ty->param_types()) { + const auto param_ty_id = type_mgr_->GetId(param_ty); + if (combined_types_.find(param_ty_id) != combined_types_.end()) { + auto* param_type = def_use_mgr_->GetDef(param_ty_id); + auto [image_type, sampler_type] = SplitType(*param_type); + if (!image_type || !sampler_type) return SPV_ERROR_INTERNAL; + // The image and sampler types must already exist, so there is no + // need to move them to the right spot. + new_params.push_back(type_mgr_->GetType(image_type->result_id())); + new_params.push_back(type_mgr_->GetType(sampler_type->result_id())); + } else { + new_params.push_back(param_ty); + } + } + if (new_params.size() != f_ty->param_types().size()) { + // Replace this type. + analysis::Function new_f_ty(f_ty->return_type(), new_params); + const uint32_t new_f_ty_id = type_mgr_->GetTypeInstruction(&new_f_ty); + std::unordered_set<Instruction*> users; + def_use_mgr_->ForEachUse( + &inst, + [&users, new_f_ty_id](Instruction* user, uint32_t use_index) { + user->SetOperand(use_index, {new_f_ty_id}); + users.insert(user); + }); + for (auto* user : users) { + def_use_mgr_->AnalyzeInstUse(user); + } + dead_insts.insert(&inst); + } + } + for (auto* inst : dead_insts) { + KillInst(inst); + } + } + + // Rewite OpFunctionParameter in function definitions. + for (Function& fn : *context()->module()) { + // Rewrite the function parameters and record their replacements. + struct Replacement { + Instruction* combined; + Instruction* image; + Instruction* sampler; + }; + std::vector<Replacement> replacements; + bool error = false; + + Function::RewriteParamFn rewriter = + [&](std::unique_ptr<Instruction>&& param, + std::back_insert_iterator<Function::ParamList>& appender) { + if (error) { + return; + } + if (combined_types_.count(param->type_id()) == 0) { + appender = std::move(param); + return; + } + + // Replace this parameter with two new parameters. + auto* combined_inst = param.release(); + auto* combined_type = def_use_mgr_->GetDef(combined_inst->type_id()); + auto [image_type, sampler_type] = SplitType(*combined_type); + if (!image_type || !sampler_type) { + error = true; + return; + } + + uint32_t image_param_id = context()->TakeNextId(); + if (image_param_id == 0) { + error = true; + return; + } + auto image_param = MakeUnique<Instruction>( + context(), spv::Op::OpFunctionParameter, image_type->result_id(), + image_param_id, Instruction::OperandList{}); + uint32_t sampler_param_id = context()->TakeNextId(); + if (sampler_param_id == 0) { + error = true; + return; + } + auto sampler_param = MakeUnique<Instruction>( + context(), spv::Op::OpFunctionParameter, + sampler_type->result_id(), sampler_param_id, + Instruction::OperandList{}); + replacements.push_back( + {combined_inst, image_param.get(), sampler_param.get()}); + appender = std::move(image_param); + appender = std::move(sampler_param); + }; + fn.RewriteParams(rewriter); + + if (error) { + return SPV_ERROR_INTERNAL; + } + + for (auto& r : replacements) { + modified_ = true; + def_use_mgr_->AnalyzeInstDefUse(r.image); + def_use_mgr_->AnalyzeInstDefUse(r.sampler); + CHECK_STATUS(RemapUses(r.combined, r.image, r.sampler)); + } + } + return SPV_SUCCESS; +} + +Instruction* SplitCombinedImageSamplerPass::MakeUniformConstantPointer( + Instruction* pointee) { + uint32_t ptr_id = type_mgr_->FindPointerToType( + pointee->result_id(), spv::StorageClass::UniformConstant); + if (ptr_id == 0) return nullptr; + auto* ptr = def_use_mgr_->GetDef(ptr_id); + if (!IsKnownGlobal(ptr_id)) { + // The pointer type was created at the end. Put it right after the + // pointee. + ptr->InsertBefore(pointee); + pointee->InsertBefore(ptr); + RegisterNewGlobal(ptr_id); + // FindPointerToType also updated the def-use manager. + } + return ptr; +} + +void SplitCombinedImageSamplerPass::AddOpName(uint32_t id, + const std::string& name) { + std::unique_ptr<Instruction> opname{new Instruction{ + context(), + spv::Op::OpName, + 0u, + 0u, + {{SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, + utils::MakeVector<spvtools::opt::Operand::OperandData>(name)}}}}; + + context()->AddDebug2Inst(std::move(opname)); +} + +spv_result_t SplitCombinedImageSamplerPass::RemoveDeadTypes() { + for (auto dead_type_id : combined_types_to_remove_) { + if (auto* ty = def_use_mgr_->GetDef(dead_type_id)) { + KillInst(ty); + } + } + return SPV_SUCCESS; +} + +void SplitCombinedImageSamplerPass::KillInst(Instruction* inst) { + // IRContext::KillInst will remove associated debug instructions and + // decorations. It will delete the object only if it is already in a list. + const bool was_in_list = inst->IsInAList(); + context()->KillInst(inst); + if (!was_in_list) { + // Avoid leaking + delete inst; + } + modified_ = true; +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/split_combined_image_sampler_pass.h b/source/opt/split_combined_image_sampler_pass.h new file mode 100644 index 0000000..253f34a --- /dev/null +++ b/source/opt/split_combined_image_sampler_pass.h
@@ -0,0 +1,169 @@ +// Copyright (c) 2025 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 LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ +#define LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ + +#include <unordered_map> +#include <utility> +#include <vector> + +#include "source/diagnostic.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// Replaces each combined-image sampler variable with an image variable +// and a sampler variable. Similar for function parameters. +// +// Copy the descriptor set and binding number. Vulkan allows this, surprisingly. +class SplitCombinedImageSamplerPass : public Pass { + public: + virtual ~SplitCombinedImageSamplerPass() override = default; + const char* name() const override { return "split-combined-image-sampler"; } + IRContext::Analysis GetPreservedAnalyses() override; + Status Process() override; + + private: + // Records failure for the current module, and returns a stream + // that can be used to provide user error information to the message + // consumer. + spvtools::DiagnosticStream Fail(); + + // Find variables that contain combined texture-samplers, or arrays of them. + // Also populate known_globals_. + void FindCombinedTextureSamplers(); + + // Returns the sampler type. If it does not yet exist, then it is created + // and placed before the first sampled image type. + Instruction* GetSamplerType(); + + // Remaps function types and function declarations. Each + // pointer-to-sampled-image-type operand is replaced with a pair of + // pointer-to-image-type and pointer-to-sampler-type pair. + // Updates the def-use manager and type manager. + spv_result_t RemapFunctions(); + // Remap resource variables. + // Updates the def-use manager. + spv_result_t RemapVars(); + // Remap a single resource variable for combined var. + // Updates the def-use manager and the decorations manager. + spv_result_t RemapVar(Instruction* combined_var); + // Transitively remaps uses of the combined object with uses of the + // decomposed image and sampler parts. The combined object can be sampled + // image value, a pointer to one, an array of one, or a pointer to an array + // of one. The image and sampler parts have corresponding shapes. + // Updates the def-use manager and the decorations manager. + spv_result_t RemapUses(Instruction* combined, Instruction* image_part, + Instruction* sampler_part); + // Removes types that are no longer referenced. + spv_result_t RemoveDeadTypes(); + + // Returns the type instruction for a UniformConstant pointer to the given + // pointee type. If it does not yet exist, the new type instruction is created + // and placed immediately after the pointee type instruction. Updates def-use + // and type managers, and the set of known globals. + Instruction* MakeUniformConstantPointer(Instruction* pointee); + + // Returns the ID of the pointee type for a pointer value instruction. + uint32_t PointeeTypeId(Instruction* ptr_value) { + auto* ptr_ty = def_use_mgr_->GetDef(ptr_value->type_id()); + assert(ptr_ty->opcode() == spv::Op::OpTypePointer); + return ptr_ty->GetSingleWordInOperand(1); + } + + // Creates a new OpName instruction mapping the given name to the given + // string, and adds it to the module at the end of the OpName and OpMemberName + // section. + void AddOpName(uint32_t id, const std::string& name); + + // Cached from the IRContext. Valid while Process() is running. + analysis::DefUseManager* def_use_mgr_ = nullptr; + // Cached from the IRContext. Valid while Process() is running. + analysis::TypeManager* type_mgr_ = nullptr; + + // Did processing modify the module? + bool modified_ = false; + Pass::Status Ok() { + return modified_ ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; + } + + // The first OpTypeSampledImage instruction in the module, if one exists. + Instruction* first_sampled_image_type_ = nullptr; + // An OpTypeSampler instruction, if one existed already, or if we created one. + Instruction* sampler_type_ = nullptr; + + // The known types and module-scope values. + // We use this to know when a new such value was created. + std::unordered_set<uint32_t> known_globals_; + bool IsKnownGlobal(uint32_t id) const { + return known_globals_.find(id) != known_globals_.end(); + } + void RegisterGlobal(uint32_t id) { known_globals_.insert(id); } + void RegisterNewGlobal(uint32_t id) { + modified_ = true; + RegisterGlobal(id); + } + + // Deletes an instruction and associated debug and decoration instructions. + // Updates the def-use manager. + void KillInst(Instruction* inst); + + // Combined types. The known combined sampled-image type, + // and recursively pointers or arrays of them. + std::unordered_set<uint32_t> combined_types_; + // The pre-existing types this pass should remove: pointer to + // combined type, array of combined type, pointer to array of combined type. + std::vector<uint32_t> combined_types_to_remove_; + // Is an OpTypeSampledImage used as a function parameter? Those should be + // transformed. + bool sampled_image_used_as_param_ = false; + + // Remaps a combined-kind type to corresponding sampler-kind and image-kind + // of type. + struct TypeRemapInfo { + // The instruction for the combined type, pointer to combined type, + // or point to array of combined type. + Instruction* combined_kind_type; + // The corresponding image type, with the same shape of indirection as the + // combined_kind_type. + Instruction* image_kind_type; + // The corresponding sampler type, with the same shape of indirection as the + // combined_kind_type. + Instruction* sampler_kind_type; + }; + // Maps the ID of a combined-image-sampler type kind to its corresponding + // split parts. + std::unordered_map<uint32_t, TypeRemapInfo> type_remap_; + + // Returns the image-like and sampler-like types of the same indirection shape + // as the given combined-like type. If combined_kind_type is not a combined + // type or a pointer to one, or an array of one or a pointer to an array of + // one, then returns a pair of null pointer. Either both components are + // non-null, or both components are null. Updates the def-use manager and the + // type manager if new instructions are created. + std::pair<Instruction*, Instruction*> SplitType( + Instruction& combined_kind_type); + + // The combined-image-sampler variables to be replaced. + std::vector<Instruction*> ordered_vars_; +}; +} // namespace opt +} // namespace spvtools +#endif // LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp index 3eb4ec3..615b129 100644 --- a/source/opt/ssa_rewrite_pass.cpp +++ b/source/opt/ssa_rewrite_pass.cpp
@@ -87,13 +87,15 @@ return str.str(); } -SSARewriter::PhiCandidate& SSARewriter::CreatePhiCandidate(uint32_t var_id, +SSARewriter::PhiCandidate* SSARewriter::CreatePhiCandidate(uint32_t var_id, BasicBlock* bb) { - // TODO(1841): Handle id overflow. uint32_t phi_result_id = pass_->context()->TakeNextId(); + if (phi_result_id == 0) { + return nullptr; + } auto result = phi_candidates_.emplace( phi_result_id, PhiCandidate(var_id, phi_result_id, bb)); - PhiCandidate& phi_candidate = result.first->second; + PhiCandidate* phi_candidate = &result.first->second; return phi_candidate; } @@ -268,11 +270,12 @@ // If there is more than one predecessor, this is a join block which may // require a Phi instruction. This will act as |var_id|'s current // definition to break potential cycles. - PhiCandidate& phi_candidate = CreatePhiCandidate(var_id, bb); + PhiCandidate* phi_candidate = CreatePhiCandidate(var_id, bb); + if (phi_candidate == nullptr) return 0; // Set the value for |bb| to avoid an infinite recursion. - WriteVariable(var_id, bb, phi_candidate.result_id()); - val_id = AddPhiOperands(&phi_candidate); + WriteVariable(var_id, bb, phi_candidate->result_id()); + val_id = AddPhiOperands(phi_candidate); } // If we could not find a store for this variable in the path from the root
diff --git a/source/opt/ssa_rewrite_pass.h b/source/opt/ssa_rewrite_pass.h index 2470f85..076d9e1 100644 --- a/source/opt/ssa_rewrite_pass.h +++ b/source/opt/ssa_rewrite_pass.h
@@ -232,7 +232,7 @@ // during rewriting. // // Once the candidate Phi is created, it returns its ID. - PhiCandidate& CreatePhiCandidate(uint32_t var_id, BasicBlock* bb); + PhiCandidate* CreatePhiCandidate(uint32_t var_id, BasicBlock* bb); // Attempts to remove a trivial Phi candidate |phi_cand|. Trivial Phis are // those that only reference themselves and one other value |val| any number
diff --git a/source/opt/strength_reduction_pass.cpp b/source/opt/strength_reduction_pass.cpp index 16a7869..b9d6265 100644 --- a/source/opt/strength_reduction_pass.cpp +++ b/source/opt/strength_reduction_pass.cpp
@@ -53,17 +53,15 @@ Pass::Status StrengthReductionPass::Process() { // Initialize the member variables on a per module basis. - bool modified = false; int32_type_id_ = 0; uint32_type_id_ = 0; std::memset(constant_ids_, 0, sizeof(constant_ids_)); FindIntTypesAndConstants(); - modified = ScanFunctions(); - return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); + return ScanFunctions(); } -bool StrengthReductionPass::ReplaceMultiplyByPowerOf2( +Pass::Status StrengthReductionPass::ReplaceMultiplyByPowerOf2( BasicBlock::iterator* inst) { assert((*inst)->opcode() == spv::Op::OpIMul && "Only works for multiplication of integers."); @@ -72,7 +70,7 @@ // Currently only works on 32-bit integers. if ((*inst)->type_id() != int32_type_id_ && (*inst)->type_id() != uint32_type_id_) { - return modified; + return Status::SuccessWithoutChange; } // Check the operands for a constant that is a power of 2. @@ -87,9 +85,11 @@ modified = true; uint32_t shiftAmount = CountTrailingZeros(constVal); uint32_t shiftConstResultId = GetConstantId(shiftAmount); + if (shiftConstResultId == 0) return Status::Failure; // Create the new instruction. uint32_t newResultId = TakeNextId(); + if (newResultId == 0) return Status::Failure; std::vector<Operand> newOperands; newOperands.push_back((*inst)->GetInOperand(1 - i)); Operand shiftOperand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, @@ -117,7 +117,7 @@ } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } void StrengthReductionPass::FindIntTypesAndConstants() { @@ -152,6 +152,7 @@ // Construct the constant. uint32_t resultId = TakeNextId(); + if (resultId == 0) return 0; Operand constant(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}); std::unique_ptr<Instruction> newConstant(new Instruction( @@ -169,7 +170,7 @@ return constant_ids_[val]; } -bool StrengthReductionPass::ScanFunctions() { +Pass::Status StrengthReductionPass::ScanFunctions() { // I did not use |ForEachInst| in the module because the function that acts on // the instruction gets a pointer to the instruction. We cannot use that to // insert a new instruction. I want an iterator. @@ -178,16 +179,19 @@ for (auto& bb : func) { for (auto inst = bb.begin(); inst != bb.end(); ++inst) { switch (inst->opcode()) { - case spv::Op::OpIMul: - if (ReplaceMultiplyByPowerOf2(&inst)) modified = true; + case spv::Op::OpIMul: { + Status s = ReplaceMultiplyByPowerOf2(&inst); + if (s == Status::Failure) return Status::Failure; + if (s == Status::SuccessWithChange) modified = true; break; + } default: break; } } } } - return modified; + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } } // namespace opt
diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h index 1cbbbcc..48e72ab 100644 --- a/source/opt/strength_reduction_pass.h +++ b/source/opt/strength_reduction_pass.h
@@ -32,7 +32,7 @@ private: // Replaces multiple by power of 2 with an equivalent bit shift. // Returns true if something changed. - bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); + Status ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); // Scan the types and constants in the module looking for the integer // types that we are @@ -47,7 +47,7 @@ // Replaces certain instructions in function bodies with presumably cheaper // ones. Returns true if something changed. - bool ScanFunctions(); + Status ScanFunctions(); // Type ids for the types of interest, or 0 if they do not exist. uint32_t int32_type_id_;
diff --git a/source/opt/trim_capabilities_pass.cpp b/source/opt/trim_capabilities_pass.cpp index 34fbc44..47f2cb2 100644 --- a/source/opt/trim_capabilities_pass.cpp +++ b/source/opt/trim_capabilities_pass.cpp
@@ -26,11 +26,11 @@ #include <vector> #include "source/enum_set.h" -#include "source/enum_string_mapping.h" #include "source/ext_inst.h" #include "source/opt/ir_context.h" #include "source/opt/reflect.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/util/string_utils.h" namespace spvtools { @@ -61,6 +61,7 @@ template <class UnaryPredicate> static void DFSWhile(const Instruction* instruction, UnaryPredicate condition) { std::stack<uint32_t> instructions_to_visit; + std::unordered_set<uint32_t> visited_instructions; instructions_to_visit.push(instruction->result_id()); const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); @@ -68,6 +69,11 @@ const Instruction* item = def_use_mgr->GetDef(instructions_to_visit.top()); instructions_to_visit.pop(); + // Forward references can be allowed, meaning we can have cycles + // between ID uses. Need to keep track of this. + if (visited_instructions.count(item->result_id())) continue; + visited_instructions.insert(item->result_id()); + if (!condition(item)) { continue; } @@ -241,6 +247,37 @@ : std::nullopt; } +static std::optional<spv::Capability> +Handler_OpTypePointer_StorageBuffer16BitAccess(const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // Requires StorageBuffer, ShaderRecordBufferKHR or PhysicalStorageBuffer + // storage classes. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::ShaderRecordBufferKHR && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return std::nullopt; + } + + const auto* decoration_mgr = instruction->context()->get_decoration_mgr(); + const bool matchesCondition = + AnyTypeOf(instruction, [decoration_mgr](const Instruction* item) { + if (!decoration_mgr->HasDecoration(item->result_id(), + spv::Decoration::Block)) { + return false; + } + + return AnyTypeOf(item, is16bitType); + }); + + return matchesCondition + ? std::optional(spv::Capability::StorageBuffer16BitAccess) + : std::nullopt; +} + static std::optional<spv::Capability> Handler_OpTypePointer_StorageUniform16( const Instruction* instruction) { assert(instruction->opcode() == spv::Op::OpTypePointer && @@ -388,40 +425,40 @@ } // Opcode of interest to determine capabilities requirements. -constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 13> kOpcodeHandlers{{ +constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 14> kOpcodeHandlers{{ // clang-format off - {spv::Op::OpImageRead, Handler_OpImageRead_StorageImageReadWithoutFormat}, - {spv::Op::OpImageWrite, Handler_OpImageWrite_StorageImageWriteWithoutFormat}, - {spv::Op::OpImageSparseRead, Handler_OpImageSparseRead_StorageImageReadWithoutFormat}, - {spv::Op::OpTypeFloat, Handler_OpTypeFloat_Float16 }, - {spv::Op::OpTypeFloat, Handler_OpTypeFloat_Float64 }, - {spv::Op::OpTypeImage, Handler_OpTypeImage_ImageMSArray}, - {spv::Op::OpTypeInt, Handler_OpTypeInt_Int16 }, - {spv::Op::OpTypeInt, Handler_OpTypeInt_Int64 }, - {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageInputOutput16}, - {spv::Op::OpTypePointer, Handler_OpTypePointer_StoragePushConstant16}, - {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}, - {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}, - {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniformBufferBlock16}, + {spv::Op::OpImageRead, Handler_OpImageRead_StorageImageReadWithoutFormat}, + {spv::Op::OpImageWrite, Handler_OpImageWrite_StorageImageWriteWithoutFormat}, + {spv::Op::OpImageSparseRead, Handler_OpImageSparseRead_StorageImageReadWithoutFormat}, + {spv::Op::OpTypeFloat, Handler_OpTypeFloat_Float16 }, + {spv::Op::OpTypeFloat, Handler_OpTypeFloat_Float64 }, + {spv::Op::OpTypeImage, Handler_OpTypeImage_ImageMSArray}, + {spv::Op::OpTypeInt, Handler_OpTypeInt_Int16 }, + {spv::Op::OpTypeInt, Handler_OpTypeInt_Int64 }, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageInputOutput16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StoragePushConstant16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniformBufferBlock16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageBuffer16BitAccess}, // clang-format on }}; // ============== End opcode handler implementations. ======================= namespace { -ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities, - const AssemblyGrammar& grammar) { +ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities) { ExtensionSet output; - const spv_operand_desc_t* desc = nullptr; + const spvtools::OperandDesc* desc = nullptr; for (auto capability : capabilities) { - if (SPV_SUCCESS != grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, - static_cast<uint32_t>(capability), - &desc)) { + if (SPV_SUCCESS != + spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast<uint32_t>(capability), &desc)) { continue; } - for (uint32_t i = 0; i < desc->numExtensions; ++i) { - output.insert(desc->extensions[i]); + for (auto extension : desc->extensions()) { + output.insert(extension); } } @@ -475,8 +512,8 @@ return; } - const spv_opcode_desc_t* desc = {}; - auto result = context()->grammar().lookupOpcode(opcode, &desc); + const spvtools::InstructionDesc* desc; + auto result = spvtools::LookupOpcode(opcode, &desc); if (result != SPV_SUCCESS) { return; } @@ -513,9 +550,9 @@ // case 1: Operand is a single value, can directly lookup. if (!spvOperandIsConcreteMask(operand.type)) { - const spv_operand_desc_t* desc = {}; - auto result = context()->grammar().lookupOperand(operand.type, - operand.words[0], &desc); + const spvtools::OperandDesc* desc = nullptr; + auto result = + spvtools::LookupOperand(operand.type, operand.words[0], &desc); if (result != SPV_SUCCESS) { return; } @@ -531,8 +568,8 @@ continue; } - const spv_operand_desc_t* desc = {}; - auto result = context()->grammar().lookupOperand(operand.type, mask, &desc); + const spvtools::OperandDesc* desc = nullptr; + auto result = spvtools::LookupOperand(operand.type, mask, &desc); if (result != SPV_SUCCESS) { continue; } @@ -561,9 +598,8 @@ spv_ext_inst_type_t instructionSet = spvExtInstImportTypeGet(extInstSet.AsString().c_str()); - spv_ext_inst_desc desc = {}; - auto result = - context()->grammar().lookupExtInst(instructionSet, extInstruction, &desc); + const ExtInstDesc* desc = nullptr; + auto result = LookupExtInst(instructionSet, extInstruction, &desc); if (result != SPV_SUCCESS) { return; } @@ -576,7 +612,9 @@ ExtensionSet* extensions) const { // Ignoring OpCapability and OpExtension instructions. if (instruction->opcode() == spv::Op::OpCapability || - instruction->opcode() == spv::Op::OpExtension) { + instruction->opcode() == spv::Op::OpConditionalCapabilityINTEL || + instruction->opcode() == spv::Op::OpExtension || + instruction->opcode() == spv::Op::OpConditionalExtensionINTEL) { return; } @@ -595,7 +633,7 @@ } // Last case: some complex logic needs to be run to determine capabilities. - auto[begin, end] = opcodeHandlers_.equal_range(instruction->opcode()); + auto [begin, end] = opcodeHandlers_.equal_range(instruction->opcode()); for (auto it = begin; it != end; it++) { const OpcodeHandler handler = it->second; auto result = handler(instruction); @@ -610,8 +648,8 @@ void TrimCapabilitiesPass::AddExtensionsForOperand( const spv_operand_type_t type, const uint32_t value, ExtensionSet* extensions) const { - const spv_operand_desc_t* desc = nullptr; - spv_result_t result = context()->grammar().lookupOperand(type, value, &desc); + const spvtools::OperandDesc* desc = nullptr; + spv_result_t result = spvtools::LookupOperand(type, value, &desc); if (result != SPV_SUCCESS) { return; } @@ -686,7 +724,7 @@ Pass::Status TrimCapabilitiesPass::TrimUnrequiredExtensions( const ExtensionSet& required_extensions) const { const auto supported_extensions = - getExtensionsRelatedTo(supportedCapabilities_, context()->grammar()); + getExtensionsRelatedTo(supportedCapabilities_); bool modified_module = false; for (auto extension : supported_extensions) { @@ -718,7 +756,7 @@ return Status::SuccessWithoutChange; } - auto[required_capabilities, required_extensions] = + auto [required_capabilities, required_extensions] = DetermineRequiredCapabilitiesAndExtensions(); Pass::Status capStatus = TrimUnrequiredCapabilities(required_capabilities);
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h index 1d1183a..06e989b 100644 --- a/source/opt/trim_capabilities_pass.h +++ b/source/opt/trim_capabilities_pass.h
@@ -28,6 +28,7 @@ #include "source/opt/module.h" #include "source/opt/pass.h" #include "source/spirv_target_env.h" +#include "source/table2.h" namespace spvtools { namespace opt { @@ -81,6 +82,7 @@ spv::Capability::FragmentShaderPixelInterlockEXT, spv::Capability::FragmentShaderSampleInterlockEXT, spv::Capability::FragmentShaderShadingRateInterlockEXT, + spv::Capability::Geometry, spv::Capability::GroupNonUniform, spv::Capability::GroupNonUniformArithmetic, spv::Capability::GroupNonUniformClustered, @@ -99,6 +101,7 @@ spv::Capability::RayTraversalPrimitiveCullingKHR, spv::Capability::Shader, spv::Capability::ShaderClockKHR, + spv::Capability::StorageBuffer16BitAccess, spv::Capability::StorageImageReadWithoutFormat, spv::Capability::StorageImageWriteWithoutFormat, spv::Capability::StorageInputOutput16, @@ -106,6 +109,7 @@ spv::Capability::StorageUniform16, spv::Capability::StorageUniformBufferBlock16, spv::Capability::VulkanMemoryModelDeviceScope, + spv::Capability::QuadControlKHR, // clang-format on }; @@ -127,14 +131,11 @@ private: // Inserts every capability listed by `descriptor` this pass supports into - // `output`. Expects a Descriptor like `spv_opcode_desc_t` or - // `spv_operand_desc_t`. - template <class Descriptor> - inline void addSupportedCapabilitiesToSet(const Descriptor* const descriptor, - CapabilitySet* output) const { - const uint32_t capabilityCount = descriptor->numCapabilities; - for (uint32_t i = 0; i < capabilityCount; ++i) { - const auto capability = descriptor->capabilities[i]; + // `output`. + template <typename Descriptor> + void addSupportedCapabilitiesToSet(const Descriptor* const descriptor, + CapabilitySet* output) const { + for (auto capability : descriptor->capabilities()) { if (supportedCapabilities_.contains(capability)) { output->insert(capability); } @@ -142,8 +143,8 @@ } // Inserts every extension listed by `descriptor` required by the module into - // `output`. Expects a Descriptor like `spv_opcode_desc_t` or - // `spv_operand_desc_t`. + // `output`. Expects a Descriptor like spvtools::OperandDesc or + // spvtools::InstructionDesc. template <class Descriptor> inline void addSupportedExtensionsToSet(const Descriptor* const descriptor, ExtensionSet* output) const { @@ -151,8 +152,8 @@ spvVersionForTargetEnv(context()->GetTargetEnv())) { return; } - output->insert(descriptor->extensions, - descriptor->extensions + descriptor->numExtensions); + output->insert(descriptor->extensions().begin(), + descriptor->extensions().end()); } void addInstructionRequirementsForOpcode(spv::Op opcode,
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index 88106b6..6f9ef0e 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -235,6 +237,7 @@ DefineParameterlessCase(AccelerationStructureNV); DefineParameterlessCase(RayQueryKHR); DefineParameterlessCase(HitObjectNV); + DefineParameterlessCase(HitObjectEXT); #undef DefineParameterlessCase case Type::kInteger: typeInst = MakeUnique<Instruction>( @@ -335,6 +338,17 @@ std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}}); break; } + case Type::kNodePayloadArrayAMDX: { + uint32_t subtype = + GetTypeInstruction(type->AsNodePayloadArrayAMDX()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeNodePayloadArrayAMDX, 0, id, + std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } case Type::kStruct: { std::vector<Operand> ops; const Struct* structTy = type->AsStruct(); @@ -361,16 +375,21 @@ } case Type::kPointer: { const Pointer* pointer = type->AsPointer(); - uint32_t subtype = GetTypeInstruction(pointer->pointee_type()); - if (subtype == 0) { - return 0; + if (pointer->is_untyped()) { + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeUntypedPointerKHR, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast<uint32_t>(pointer->storage_class())}}}); + } else { + uint32_t subtype = GetTypeInstruction(pointer->pointee_type()); + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypePointer, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast<uint32_t>(pointer->storage_class())}}, + {SPV_OPERAND_TYPE_ID, {subtype}}}); } - typeInst = MakeUnique<Instruction>( - context(), spv::Op::OpTypePointer, 0, id, - std::initializer_list<Operand>{ - {SPV_OPERAND_TYPE_STORAGE_CLASS, - {static_cast<uint32_t>(pointer->storage_class())}}, - {SPV_OPERAND_TYPE_ID, {subtype}}}); break; } case Type::kFunction: { @@ -463,11 +482,78 @@ 0, id, operands); break; } + case Type::kCooperativeVectorNV: { + auto coop_vec = type->AsCooperativeVectorNV(); + uint32_t const component_type = + GetTypeInstruction(coop_vec->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeVectorIdEXT, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_ID, {coop_vec->components()}}}); + break; + } + case Type::kTensorARM: { + auto tensor_type = type->AsTensorARM(); + uint32_t const element_type = + GetTypeInstruction(tensor_type->element_type()); + if (element_type == 0) { + return 0; + } + if (tensor_type->rank_id() != 0) { + if (tensor_type->shape_id() != 0) { + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeTensorARM, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {element_type}}, + {SPV_OPERAND_TYPE_ID, {tensor_type->rank_id()}}, + {SPV_OPERAND_TYPE_ID, {tensor_type->shape_id()}}}); + } else { + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeTensorARM, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {element_type}}, + {SPV_OPERAND_TYPE_ID, {tensor_type->rank_id()}}}); + } + } else { + typeInst = + MakeUnique<Instruction>(context(), spv::Op::OpTypeTensorARM, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {element_type}}}); + } + break; + } + case Type::kGraphARM: { + auto const gty = type->AsGraphARM(); + std::vector<Operand> ops; + ops.push_back( + Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {gty->num_inputs()})); + for (auto iotype : gty->io_types()) { + uint32_t iotype_id = GetTypeInstruction(iotype); + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {iotype_id})); + } + typeInst = MakeUnique<Instruction>(context(), spv::Op::OpTypeGraphARM, 0, + id, ops); + break; + } + case Type::kBufferEXT: { + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeBufferEXT, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast<uint32_t>(type->AsBufferEXT()->storage_class())}}}); + break; + } default: assert(false && "Unexpected type"); break; } context()->AddType(std::move(typeInst)); + // TODO(dneto): This next call to AnalyzeDefUse is redundant becaues + // IRContext::AddType already does it. context()->AnalyzeDefUse(&*--context()->types_values_end()); AttachDecorations(id, type); return id; @@ -577,6 +663,7 @@ DefineNoSubtypeCase(AccelerationStructureNV); DefineNoSubtypeCase(RayQueryKHR); DefineNoSubtypeCase(HitObjectNV); + DefineNoSubtypeCase(HitObjectEXT); #undef DefineNoSubtypeCase case Type::kVector: { const Vector* vec_ty = type.AsVector(); @@ -623,6 +710,13 @@ MakeUnique<RuntimeArray>(RebuildType(GetId(ele_ty), *ele_ty)); break; } + case Type::kNodePayloadArrayAMDX: { + const NodePayloadArrayAMDX* array_ty = type.AsNodePayloadArrayAMDX(); + const Type* ele_ty = array_ty->element_type(); + rebuilt_ty = + MakeUnique<NodePayloadArrayAMDX>(RebuildType(GetId(ele_ty), *ele_ty)); + break; + } case Type::kStruct: { const Struct* struct_ty = type.AsStruct(); std::vector<const Type*> subtypes; @@ -644,9 +738,13 @@ } case Type::kPointer: { const Pointer* pointer_ty = type.AsPointer(); - const Type* ele_ty = pointer_ty->pointee_type(); - rebuilt_ty = MakeUnique<Pointer>(RebuildType(GetId(ele_ty), *ele_ty), - pointer_ty->storage_class()); + if (pointer_ty->pointee_type()) { + const Type* ele_ty = pointer_ty->pointee_type(); + rebuilt_ty = MakeUnique<Pointer>(RebuildType(GetId(ele_ty), *ele_ty), + pointer_ty->storage_class()); + } else { + rebuilt_ty = MakeUnique<Pointer>(nullptr, pointer_ty->storage_class()); + } break; } case Type::kFunction: { @@ -701,6 +799,36 @@ tv_type->dim_id(), tv_type->has_dimensions_id(), tv_type->perm()); break; } + case Type::kCooperativeVectorNV: { + const CooperativeVectorNV* cv_type = type.AsCooperativeVectorNV(); + const Type* component_type = cv_type->component_type(); + rebuilt_ty = MakeUnique<CooperativeVectorNV>( + RebuildType(GetId(component_type), *component_type), + cv_type->components()); + break; + } + case Type::kTensorARM: { + const TensorARM* tensor_type = type.AsTensorARM(); + const Type* element_type = tensor_type->element_type(); + rebuilt_ty = MakeUnique<TensorARM>( + RebuildType(GetId(element_type), *element_type), + tensor_type->rank_id(), tensor_type->shape_id()); + break; + } + case Type::kGraphARM: { + const GraphARM* graph_type = type.AsGraphARM(); + std::vector<const Type*> io_types; + for (auto ioty : graph_type->io_types()) { + io_types.push_back(RebuildType(GetId(ioty), *ioty)); + } + rebuilt_ty = MakeUnique<GraphARM>(graph_type->num_inputs(), io_types); + break; + } + case Type::kBufferEXT: { + const BufferEXT* buffer_type = type.AsBufferEXT(); + rebuilt_ty = MakeUnique<BufferEXT>(buffer_type->storage_class()); + break; + } default: assert(false && "Unhandled type"); return nullptr; @@ -748,9 +876,13 @@ type = new Integer(inst.GetSingleWordInOperand(0), inst.GetSingleWordInOperand(1)); break; - case spv::Op::OpTypeFloat: - type = new Float(inst.GetSingleWordInOperand(0)); - break; + case spv::Op::OpTypeFloat: { + const spv::FPEncoding encoding = + inst.NumInOperands() > 1 + ? static_cast<spv::FPEncoding>(inst.GetSingleWordInOperand(1)) + : spv::FPEncoding::Max; + type = new Float(inst.GetSingleWordInOperand(0), encoding); + } break; case spv::Op::OpTypeVector: type = new Vector(GetType(inst.GetSingleWordInOperand(0)), inst.GetSingleWordInOperand(1)); @@ -837,6 +969,14 @@ return type; } break; + case spv::Op::OpTypeNodePayloadArrayAMDX: + type = new NodePayloadArrayAMDX(GetType(inst.GetSingleWordInOperand(0))); + if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + break; case spv::Op::OpTypeStruct: { std::vector<const Type*> element_types; bool incomplete_type = false; @@ -872,6 +1012,11 @@ id_to_incomplete_type_.erase(inst.result_id()); } break; + case spv::Op::OpTypeUntypedPointerKHR: { + type = new Pointer(nullptr, static_cast<spv::StorageClass>( + inst.GetSingleWordInOperand(0))); + id_to_incomplete_type_.erase(inst.result_id()); + } break; case spv::Op::OpTypeFunction: { bool incomplete_type = false; uint32_t return_type_id = inst.GetSingleWordInOperand(0); @@ -942,12 +1087,19 @@ inst.GetSingleWordInOperand(1), inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3), inst.GetSingleWordInOperand(4)); break; + case spv::Op::OpTypeVectorIdEXT: + type = new CooperativeVectorNV(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; case spv::Op::OpTypeRayQueryKHR: type = new RayQueryKHR(); break; case spv::Op::OpTypeHitObjectNV: type = new HitObjectNV(); break; + case spv::Op::OpTypeHitObjectEXT: + type = new HitObjectEXT(); + break; case spv::Op::OpTypeTensorLayoutNV: type = new TensorLayoutNV(inst.GetSingleWordInOperand(0), inst.GetSingleWordInOperand(1)); @@ -962,6 +1114,36 @@ inst.GetSingleWordInOperand(1), perm); break; } + case spv::Op::OpTypeTensorARM: { + switch (inst.NumInOperands()) { + case 1: + type = new TensorARM(GetType(inst.GetSingleWordInOperand(0))); + break; + case 2: + type = new TensorARM(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; + case 3: + type = new TensorARM(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1), + inst.GetSingleWordInOperand(2)); + break; + } + break; + } + case spv::Op::OpTypeGraphARM: { + std::vector<const Type*> io_types; + for (unsigned i = 1; i < inst.NumInOperands(); i++) { + io_types.push_back(GetType(inst.GetSingleWordInOperand(i))); + } + type = new GraphARM(inst.GetSingleWordInOperand(0), io_types); + break; + } + case spv::Op::OpTypeBufferEXT: { + type = new BufferEXT( + static_cast<spv::StorageClass>(inst.GetSingleWordInOperand(0))); + break; + } default: assert(false && "Type not handled by the type manager."); break; @@ -988,11 +1170,16 @@ if (!IsAnnotationInst(opcode)) return; switch (opcode) { - case spv::Op::OpDecorate: { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: { const auto count = inst.NumOperands(); std::vector<uint32_t> data; for (uint32_t i = 1; i < count; ++i) { - data.push_back(inst.GetSingleWordOperand(i)); + // LinkageAttributes has a literal string as an operand, which is a + // varible length word. We cannot assume that all operands are single + // word. + const Operand::OperandData& words = inst.GetOperand(i).words; + data.insert(data.end(), words.begin(), words.end()); } type->AddDecoration(std::move(data)); } break;
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h index 948b691..cadc7c2 100644 --- a/source/opt/type_manager.h +++ b/source/opt/type_manager.h
@@ -34,7 +34,7 @@ // Hashing functor. // -// All type pointers must be non-null. +// All type pointers must be non-null to reach here. struct HashTypePointer { size_t operator()(const Type* type) const { assert(type); @@ -203,7 +203,11 @@ return GetRegisteredType(&bool_type); } - uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); } + uint32_t GetBoolTypeId() { + Type* bool_type = GetBoolType(); + if (bool_type == nullptr) return 0; + return GetTypeInstruction(bool_type); + } Type* GetVoidType() { Void void_type;
diff --git a/source/opt/types.cpp b/source/opt/types.cpp index 8ccf6c9..fd475a1 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -90,6 +92,7 @@ case kStruct: case kArray: case kRuntimeArray: + case kNodePayloadArrayAMDX: return false; default: return true; @@ -129,8 +132,13 @@ DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); DeclareKindCase(CooperativeMatrixKHR); + DeclareKindCase(CooperativeVectorNV); DeclareKindCase(RayQueryKHR); DeclareKindCase(HitObjectNV); + DeclareKindCase(HitObjectEXT); + DeclareKindCase(TensorARM); + DeclareKindCase(GraphARM); + DeclareKindCase(BufferEXT); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -162,6 +170,7 @@ DeclareKindCase(SampledImage); DeclareKindCase(Array); DeclareKindCase(RuntimeArray); + DeclareKindCase(NodePayloadArrayAMDX); DeclareKindCase(Struct); DeclareKindCase(Opaque); DeclareKindCase(Pointer); @@ -177,10 +186,15 @@ DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); DeclareKindCase(CooperativeMatrixKHR); + DeclareKindCase(CooperativeVectorNV); DeclareKindCase(RayQueryKHR); DeclareKindCase(HitObjectNV); + DeclareKindCase(HitObjectEXT); DeclareKindCase(TensorLayoutNV); DeclareKindCase(TensorViewNV); + DeclareKindCase(TensorARM); + DeclareKindCase(GraphARM); + DeclareKindCase(BufferEXT); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -220,6 +234,7 @@ DeclareKindCase(SampledImage); DeclareKindCase(Array); DeclareKindCase(RuntimeArray); + DeclareKindCase(NodePayloadArrayAMDX); DeclareKindCase(Struct); DeclareKindCase(Opaque); DeclareKindCase(Pointer); @@ -235,10 +250,15 @@ DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); DeclareKindCase(CooperativeMatrixKHR); + DeclareKindCase(CooperativeVectorNV); DeclareKindCase(RayQueryKHR); DeclareKindCase(HitObjectNV); + DeclareKindCase(HitObjectEXT); DeclareKindCase(TensorLayoutNV); DeclareKindCase(TensorViewNV); + DeclareKindCase(TensorARM); + DeclareKindCase(GraphARM); + DeclareKindCase(BufferEXT); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -283,6 +303,78 @@ } } +std::optional<uint32_t> Type::GetByteOffset( + const std::vector<uint32_t>& access_chain) const { + uint32_t offset = 0; + const Type* current_type = this; + for (uint32_t index : access_chain) { + if (const Struct* struct_type = current_type->AsStruct()) { + std::optional<uint32_t> member_offset; + for (const auto& deco : struct_type->element_decorations()) { + if (deco.first != index) continue; + for (const auto& inst : deco.second) { + if (inst[0] == uint32_t(spv::Decoration::Offset)) { + member_offset = inst[1]; + break; + } + } + } + if (!member_offset) return {}; + offset += *member_offset; + current_type = struct_type->element_types()[index]; + } else if (const Array* array_type = current_type->AsArray()) { + std::optional<uint32_t> array_stride; + for (const auto& deco : array_type->decorations()) { + if (deco[0] == uint32_t(spv::Decoration::ArrayStride)) { + array_stride = deco[1]; + break; + } + } + if (!array_stride) return {}; + offset += *array_stride * index; + current_type = array_type->element_type(); + } else if (const RuntimeArray* runtime_array_type = + current_type->AsRuntimeArray()) { + std::optional<uint32_t> array_stride; + for (const auto& deco : runtime_array_type->decorations()) { + if (deco[0] == uint32_t(spv::Decoration::ArrayStride)) { + array_stride = deco[1]; + break; + } + } + if (!array_stride) return {}; + offset += *array_stride * index; + current_type = runtime_array_type->element_type(); + } else if (const Matrix* matrix_type = current_type->AsMatrix()) { + std::optional<uint32_t> matrix_stride; + for (const auto& deco : matrix_type->decorations()) { + if (deco[0] == uint32_t(spv::Decoration::MatrixStride)) { + matrix_stride = deco[1]; + break; + } + } + if (!matrix_stride) return {}; + offset += *matrix_stride * index; + current_type = matrix_type->element_type(); + } else if (const Vector* vector_type = current_type->AsVector()) { + const Type* component_type = vector_type->element_type(); + uint32_t component_size = 0; + if (component_type->AsInteger()) { + component_size = component_type->AsInteger()->width() / 8; + } else if (component_type->AsFloat()) { + component_size = component_type->AsFloat()->width() / 8; + } else { + return {}; + } + offset += component_size * index; + current_type = component_type; + } else { + return {}; + } + } + return offset; +} + bool Integer::IsSameImpl(const Type* that, IsSameCache*) const { const Integer* it = that->AsInteger(); return it && width_ == it->width_ && signed_ == it->signed_ && @@ -301,17 +393,34 @@ bool Float::IsSameImpl(const Type* that, IsSameCache*) const { const Float* ft = that->AsFloat(); - return ft && width_ == ft->width_ && HasSameDecorations(that); + return ft && width_ == ft->width_ && encoding_ == ft->encoding_ && + HasSameDecorations(that); } std::string Float::str() const { std::ostringstream oss; - oss << "float" << width_; + switch (encoding_) { + case spv::FPEncoding::BFloat16KHR: + assert(width_ == 16); + oss << "bfloat16"; + break; + case spv::FPEncoding::Float8E4M3EXT: + assert(width_ == 8); + oss << "fp8e4m3"; + break; + case spv::FPEncoding::Float8E5M2EXT: + assert(width_ == 8); + oss << "fp8e5m2"; + break; + default: + oss << "float" << width_; + break; + } return oss.str(); } size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const { - return hash_combine(hash, width_); + return hash_combine(hash, width_, encoding_); } Vector::Vector(const Type* type, uint32_t count) @@ -489,6 +598,34 @@ element_type_ = type; } +NodePayloadArrayAMDX::NodePayloadArrayAMDX(const Type* type) + : Type(kNodePayloadArrayAMDX), element_type_(type) { + assert(!type->AsVoid()); +} + +bool NodePayloadArrayAMDX::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const NodePayloadArrayAMDX* rat = that->AsNodePayloadArrayAMDX(); + if (!rat) return false; + return element_type_->IsSameImpl(rat->element_type_, seen) && + HasSameDecorations(that); +} + +std::string NodePayloadArrayAMDX::str() const { + std::ostringstream oss; + oss << "[" << element_type_->str() << "]"; + return oss.str(); +} + +size_t NodePayloadArrayAMDX::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + return element_type_->ComputeHashValue(hash, seen); +} + +void NodePayloadArrayAMDX::ReplaceElementType(const Type* type) { + element_type_ = type; +} + Struct::Struct(const std::vector<const Type*>& types) : Type(kStruct), element_types_(types) { for (const auto* t : types) { @@ -576,24 +713,39 @@ if (!p.second) { return true; } - bool same_pointee = pointee_type_->IsSameImpl(pt->pointee_type_, seen); - seen->erase(p.first); - if (!same_pointee) { - return false; + if (pointee_type_ != nullptr && pt->pointee_type_ != nullptr) { + bool same_pointee = pointee_type_->IsSameImpl(pt->pointee_type_, seen); + seen->erase(p.first); + if (!same_pointee) { + return false; + } + } else { + seen->erase(p.first); + // Either both are untyped or it is mixed typed and untyped. + if (pointee_type_ != pt->pointee_type_) { + return false; + } } return HasSameDecorations(that); } std::string Pointer::str() const { std::ostringstream os; - os << pointee_type_->str() << " " << static_cast<uint32_t>(storage_class_) - << "*"; + if (pointee_type_) { + os << pointee_type_->str(); + } else { + os << "untyped_ptr"; + } + os << " " << static_cast<uint32_t>(storage_class_) << "*"; return os.str(); } size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { hash = hash_combine(hash, uint32_t(storage_class_)); - return pointee_type_->ComputeHashValue(hash, seen); + if (pointee_type_) { + hash = pointee_type_->ComputeHashValue(hash, seen); + } + return hash; } void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } @@ -802,6 +954,145 @@ has_dimensions_id_ == tv->has_dimensions_id_ && perm_ == tv->perm_; } +CooperativeVectorNV::CooperativeVectorNV(const Type* type, + const uint32_t components) + : Type(kCooperativeVectorNV), + component_type_(type), + components_(components) { + assert(type != nullptr); + assert(components != 0); +} + +std::string CooperativeVectorNV::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << components_ << ">"; + return oss.str(); +} + +size_t CooperativeVectorNV::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, components_); + return component_type_->ComputeHashValue(hash, seen); +} + +bool CooperativeVectorNV::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeVectorNV* mt = that->AsCooperativeVectorNV(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + components_ == mt->components_ && HasSameDecorations(that); +} + +TensorARM::TensorARM(const Type* elty, const uint32_t rank, + const uint32_t shape) + : Type(kTensorARM), element_type_(elty), rank_id_(rank), shape_id_(shape) { + assert(elty != nullptr); + if (shape != 0) { + assert(rank != 0); + } +} + +std::string TensorARM::str() const { + std::ostringstream oss; + oss << "tensor<" << element_type_->str() << ", id(" << rank_id_ << "), id(" + << shape_id_ << ")>"; + return oss.str(); +} + +size_t TensorARM::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, rank_id_); + hash = hash_combine(hash, shape_id_); + return element_type_->ComputeHashValue(hash, seen); +} + +bool TensorARM::IsSameImpl(const Type* that, IsSameCache* seen) const { + const TensorARM* tt = that->AsTensorARM(); + if (!tt) return false; + return element_type_->IsSameImpl(tt->element_type_, seen) && + rank_id_ == tt->rank_id_ && shape_id_ == tt->shape_id_ && + HasSameDecorations(that); +} + +GraphARM::GraphARM(const uint32_t num_inputs, + const std::vector<const Type*>& io_types) + : Type(kGraphARM), num_inputs_(num_inputs), io_types_(io_types) { + assert(io_types.size() > 0); +} + +std::string GraphARM::str() const { + std::ostringstream oss; + oss << "graph<" << num_inputs_; + for (auto ioty : io_types_) { + oss << "," << ioty->str(); + } + oss << ">"; + return oss.str(); +} + +bool GraphARM::is_shaped() const { + // A graph is considered to be shaped if all its interface tensors are shaped + for (auto ioty : io_types_) { + auto tensor_type = ioty->AsTensorARM(); + assert(tensor_type); + if (!tensor_type->is_shaped()) { + return false; + } + } + return true; +} + +size_t GraphARM::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, num_inputs_); + for (auto ioty : io_types_) { + hash = ioty->ComputeHashValue(hash, seen); + } + return hash; +} + +bool GraphARM::IsSameImpl(const Type* that, IsSameCache* seen) const { + const GraphARM* og = that->AsGraphARM(); + if (!og) { + return false; + } + if (num_inputs_ != og->num_inputs_) { + return false; + } + if (io_types_.size() != og->io_types_.size()) { + return false; + } + for (size_t i = 0; i < io_types_.size(); i++) { + if (!io_types_[i]->IsSameImpl(og->io_types_[i], seen)) { + return false; + } + } + return true; +} + +BufferEXT::BufferEXT(spv::StorageClass storage_class) + : Type(kBufferEXT), storage_class_(storage_class) {} + +std::string BufferEXT::str() const { + std::ostringstream oss; + oss << "buffer<" << static_cast<uint32_t>(storage_class_) << ">"; + return oss.str(); +} + +size_t BufferEXT::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + hash = hash_combine(hash, static_cast<uint32_t>(storage_class_)); + return hash; +} + +bool BufferEXT::IsSameImpl(const Type* that, IsSameCache*) const { + const BufferEXT* og = that->AsBufferEXT(); + if (!og) { + return false; + } + if (storage_class_ != og->storage_class_) { + return false; + } + return true; +} + } // namespace analysis } // namespace opt } // namespace spvtools
diff --git a/source/opt/types.h b/source/opt/types.h index 6092c3c..90c3447 100644 --- a/source/opt/types.h +++ b/source/opt/types.h
@@ -1,4 +1,6 @@ // Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,6 +21,7 @@ #include <map> #include <memory> +#include <optional> #include <set> #include <string> #include <unordered_map> @@ -46,6 +49,7 @@ class SampledImage; class Array; class RuntimeArray; +class NodePayloadArrayAMDX; class Struct; class Opaque; class Pointer; @@ -61,10 +65,15 @@ class AccelerationStructureNV; class CooperativeMatrixNV; class CooperativeMatrixKHR; +class CooperativeVectorNV; class RayQueryKHR; class HitObjectNV; +class HitObjectEXT; class TensorLayoutNV; class TensorViewNV; +class TensorARM; +class GraphARM; +class BufferEXT; // Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods, // which is used as a way to probe the actual <subclass>. @@ -89,6 +98,7 @@ kSampledImage, kArray, kRuntimeArray, + kNodePayloadArrayAMDX, kStruct, kOpaque, kPointer, @@ -104,10 +114,15 @@ kAccelerationStructureNV, kCooperativeMatrixNV, kCooperativeMatrixKHR, + kCooperativeVectorNV, kRayQueryKHR, kHitObjectNV, + kHitObjectEXT, kTensorLayoutNV, kTensorViewNV, + kTensorARM, + kGraphARM, + kBufferEXT, kLast }; @@ -130,6 +145,12 @@ return IsSameImpl(that, &seen); } + // Returns true if this is a cooperative matrix. + bool IsCooperativeMatrix() const { + return kind() == analysis::Type::kCooperativeMatrixKHR || + kind() == analysis::Type::kCooperativeMatrixNV; + } + // Returns true if this type is exactly the same as |that| type, including // decorations. |seen| is the set of |Pointer*| pair that are currently being // compared in a parent call to |IsSameImpl|. @@ -176,9 +197,16 @@ // non-composite type. uint64_t NumberOfComponents() const; -// A bunch of methods for casting this type to a given type. Returns this if the -// cast can be done, nullptr otherwise. -// clang-format off + // Returns the byte offset of the member of this type that is identified + // by |access_chain|. The vector |access_chain| is a series of integers that + // are used to pick members as in the |OpCompositeExtract| instructions. + // Returns {} if the offset cannot be computed. + std::optional<uint32_t> GetByteOffset( + const std::vector<uint32_t>& access_chain) const; + + // A bunch of methods for casting this type to a given type. Returns this if + // the cast can be done, nullptr otherwise. + // clang-format off #define DeclareCastMethod(target) \ virtual target* As##target() { return nullptr; } \ virtual const target* As##target() const { return nullptr; } @@ -193,6 +221,7 @@ DeclareCastMethod(SampledImage) DeclareCastMethod(Array) DeclareCastMethod(RuntimeArray) + DeclareCastMethod(NodePayloadArrayAMDX) DeclareCastMethod(Struct) DeclareCastMethod(Opaque) DeclareCastMethod(Pointer) @@ -208,10 +237,15 @@ DeclareCastMethod(AccelerationStructureNV) DeclareCastMethod(CooperativeMatrixNV) DeclareCastMethod(CooperativeMatrixKHR) + DeclareCastMethod(CooperativeVectorNV) DeclareCastMethod(RayQueryKHR) DeclareCastMethod(HitObjectNV) + DeclareCastMethod(HitObjectEXT) DeclareCastMethod(TensorLayoutNV) DeclareCastMethod(TensorViewNV) + DeclareCastMethod(TensorARM) + DeclareCastMethod(GraphARM) + DeclareCastMethod(BufferEXT) #undef DeclareCastMethod protected: @@ -221,7 +255,9 @@ protected: // Decorations attached to this type. Each decoration is encoded as a vector // of uint32_t numbers. The first uint32_t number is the decoration value, - // and the rest are the parameters to the decoration (if exists). + // and the rest are the parameters to the decoration (if any exist). + // The parameters can be either all literals or all ids depending on the + // decoration value. std::vector<std::vector<uint32_t>> decorations_; private: @@ -257,7 +293,8 @@ class Float : public Type { public: - Float(uint32_t w) : Type(kFloat), width_(w) {} + Float(uint32_t w, spv::FPEncoding encoding = spv::FPEncoding::Max) + : Type(kFloat), width_(w), encoding_(encoding) {} Float(const Float&) = default; std::string str() const override; @@ -265,13 +302,15 @@ Float* AsFloat() override { return this; } const Float* AsFloat() const override { return this; } uint32_t width() const { return width_; } + spv::FPEncoding encoding() const { return encoding_; } size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; - uint32_t width_; // bit width + uint32_t width_; // bit width + spv::FPEncoding encoding_; // FPEncoding }; class Vector : public Type { @@ -440,6 +479,29 @@ const Type* element_type_; }; +class NodePayloadArrayAMDX : public Type { + public: + NodePayloadArrayAMDX(const Type* element_type); + NodePayloadArrayAMDX(const NodePayloadArrayAMDX&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + + NodePayloadArrayAMDX* AsNodePayloadArrayAMDX() override { return this; } + const NodePayloadArrayAMDX* AsNodePayloadArrayAMDX() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + void ReplaceElementType(const Type* element_type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; +}; + class Struct : public Type { public: Struct(const std::vector<const Type*>& element_types); @@ -514,6 +576,8 @@ const Type* pointee_type() const { return pointee_type_; } spv::StorageClass storage_class() const { return storage_class_; } + bool is_untyped() const { return pointee_type_ == nullptr; } + Pointer* AsPointer() override { return this; } const Pointer* AsPointer() const override { return this; } @@ -712,6 +776,100 @@ std::vector<uint32_t> perm_; }; +class CooperativeVectorNV : public Type { + public: + CooperativeVectorNV(const Type* type, const uint32_t components); + CooperativeVectorNV(const CooperativeVectorNV&) = default; + + std::string str() const override; + + CooperativeVectorNV* AsCooperativeVectorNV() override { return this; } + const CooperativeVectorNV* AsCooperativeVectorNV() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + const Type* component_type() const { return component_type_; } + uint32_t components() const { return components_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t components_; +}; + +class TensorARM : public Type { + public: + TensorARM(const Type* elty, const uint32_t rank = 0, + const uint32_t shape = 0); + TensorARM(const TensorARM&) = default; + + std::string str() const override; + + TensorARM* AsTensorARM() override { return this; } + const TensorARM* AsTensorARM() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + const Type* element_type() const { return element_type_; } + uint32_t rank_id() const { return rank_id_; } + uint32_t shape_id() const { return shape_id_; } + bool is_ranked() const { return rank_id_ != 0; } + bool is_shaped() const { return shape_id_ != 0; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + const uint32_t rank_id_; + const uint32_t shape_id_; +}; + +class GraphARM : public Type { + public: + GraphARM(const uint32_t num_inputs, const std::vector<const Type*>& io_types); + GraphARM(const GraphARM&) = default; + + std::string str() const override; + + GraphARM* AsGraphARM() override { return this; } + const GraphARM* AsGraphARM() const override { return this; } + + uint32_t num_inputs() const { return num_inputs_; } + const std::vector<const Type*>& io_types() const { return io_types_; } + bool is_shaped() const; + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const uint32_t num_inputs_; + const std::vector<const Type*> io_types_; +}; + +class BufferEXT : public Type { + public: + BufferEXT(spv::StorageClass storage_class_); + BufferEXT(const BufferEXT&) = default; + + std::string str() const override; + + BufferEXT* AsBufferEXT() override { return this; } + const BufferEXT* AsBufferEXT() const override { return this; } + + spv::StorageClass storage_class() const { return storage_class_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const spv::StorageClass storage_class_; +}; + #define DefineParameterlessType(type, name) \ class type : public Type { \ public: \ @@ -744,6 +902,7 @@ DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV); DefineParameterlessType(RayQueryKHR, rayQueryKHR); DefineParameterlessType(HitObjectNV, hitObjectNV); +DefineParameterlessType(HitObjectEXT, hitObjectEXT); #undef DefineParameterlessType } // namespace analysis
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp index 1b439a6..4af466d 100644 --- a/source/opt/upgrade_memory_model.cpp +++ b/source/opt/upgrade_memory_model.cpp
@@ -160,14 +160,38 @@ } switch (inst->opcode()) { - case spv::Op::OpLoad: + case spv::Op::OpLoad: { + Instruction* src_pointer = context()->get_def_use_mgr()->GetDef( + inst->GetSingleWordInOperand(0u)); + analysis::Type* src_type = + context()->get_type_mgr()->GetType(src_pointer->type_id()); + auto storage_class = src_type->AsPointer()->storage_class(); + if (storage_class == spv::StorageClass::Function || + storage_class == spv::StorageClass::Private) { + // If the buffer from function variable or private variable, flag + // NonPrivatePointer is unnecessary. + is_coherent = false; + } UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility, kMemory); break; - case spv::Op::OpStore: + } + case spv::Op::OpStore: { + Instruction* src_pointer = context()->get_def_use_mgr()->GetDef( + inst->GetSingleWordInOperand(0u)); + analysis::Type* src_type = + context()->get_type_mgr()->GetType(src_pointer->type_id()); + auto storage_class = src_type->AsPointer()->storage_class(); + if (storage_class == spv::StorageClass::Function || + storage_class == spv::StorageClass::Private) { + // If the buffer from function variable or private variable, flag + // NonPrivatePointer is unnecessary. + is_coherent = false; + } UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability, kMemory); break; + } case spv::Op::OpCopyMemory: case spv::Op::OpCopyMemorySized: start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u; @@ -366,6 +390,21 @@ indices.push_back(inst->GetSingleWordInOperand(i)); } break; + case spv::Op::OpLoad: + if (context()->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + analysis::Integer int_ty(32, false); + uint32_t int_id = + context()->get_type_mgr()->GetTypeInstruction(&int_ty); + const analysis::Constant* constant = + context()->get_constant_mgr()->GetConstant( + context()->get_type_mgr()->GetType(int_id), {0u}); + uint32_t constant_id = context() + ->get_constant_mgr() + ->GetDefiningInstruction(constant) + ->result_id(); + + indices.push_back(constant_id); + } default: break; } @@ -661,22 +700,29 @@ roots.push(e.GetSingleWordInOperand(1u)); if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) { for (auto barrier : barriers) { - // Add OutputMemoryKHR to the semantics of the barriers. + // Add OutputMemoryKHR to the semantics of the non-relaxed barriers. uint32_t semantics_id = barrier->GetSingleWordInOperand(2u); Instruction* semantics_inst = context()->get_def_use_mgr()->GetDef(semantics_id); analysis::Type* semantics_type = context()->get_type_mgr()->GetType(semantics_inst->type_id()); uint64_t semantics_value = GetIndexValue(semantics_inst); - const analysis::Constant* constant = - context()->get_constant_mgr()->GetConstant( - semantics_type, - {static_cast<uint32_t>(semantics_value) | - uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)}); - barrier->SetInOperand(2u, {context() - ->get_constant_mgr() - ->GetDefiningInstruction(constant) - ->result_id()}); + const uint64_t memory_order_mask = + uint64_t(spv::MemorySemanticsMask::Acquire | + spv::MemorySemanticsMask::Release | + spv::MemorySemanticsMask::AcquireRelease | + spv::MemorySemanticsMask::SequentiallyConsistent); + if (semantics_value & memory_order_mask) { + const analysis::Constant* constant = + context()->get_constant_mgr()->GetConstant( + semantics_type, + {static_cast<uint32_t>(semantics_value) | + uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)}); + barrier->SetInOperand(2u, {context() + ->get_constant_mgr() + ->GetDefiningInstruction(constant) + ->result_id()}); + } } } barriers.clear(); @@ -758,11 +804,13 @@ InstructionBuilder builder( context(), where, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // TODO(1841): Handle id overflow. auto extract_0 = builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0}); context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id()); // The extract's input was just changed to itself, so fix that. extract_0->SetInOperand(0u, {ext_inst->result_id()}); + // TODO(1841): Handle id overflow. auto extract_1 = builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1}); builder.AddStore(ptr_id, extract_1->result_id());
diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp index 743dc52..a93d33c 100644 --- a/source/opt/value_number_table.cpp +++ b/source/opt/value_number_table.cpp
@@ -38,6 +38,45 @@ return GetValueNumber(context()->get_def_use_mgr()->GetDef(id)); } +bool ValueNumberTable::IsReadOnlyLoad(Instruction* inst) { + if (!inst->IsLoad()) { + return false; + } + + Instruction* address_def = inst->GetBaseAddress(); + if (!address_def) { + return false; + } + + auto cached_result = read_only_variable_cache_.find(address_def->result_id()); + if (cached_result != read_only_variable_cache_.end()) { + return cached_result->second; + } + + bool is_read_only = IsReadOnlyVariable(address_def); + read_only_variable_cache_[address_def->result_id()] = is_read_only; + return is_read_only; +} + +bool ValueNumberTable::IsReadOnlyVariable(Instruction* address_def) { + if (address_def->opcode() == spv::Op::OpVariable) { + if (address_def->IsReadOnlyPointer()) { + return true; + } + } + + if (address_def->opcode() == spv::Op::OpLoad) { + const analysis::Type* address_type = + context()->get_type_mgr()->GetType(address_def->type_id()); + if (address_type->AsSampledImage() != nullptr) { + const auto* image_type = + address_type->AsSampledImage()->image_type()->AsImage(); + return image_type->sampled() == 1; + } + } + return false; +} + uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { // If it already has a value return that. uint32_t value = GetValueNumber(inst); @@ -45,38 +84,51 @@ return value; } + auto assign_new_number = [this](Instruction* i) { + const auto new_value = TakeNextValueNumber(); + id_to_value_[i->result_id()] = new_value; + return new_value; + }; + // If the instruction has other side effects, then it must // have its own value number. - // OpSampledImage and OpImage must remain in the same basic block in which - // they are used, because of this we will assign each one it own value number. if (!context()->IsCombinatorInstruction(inst) && !inst->IsCommonDebugInstr()) { - value = TakeNextValueNumber(); - id_to_value_[inst->result_id()] = value; - return value; + return assign_new_number(inst); } + // OpSampledImage and OpImage must remain in the same basic block in which + // they are used, because of this we will assign each one it own value number. switch (inst->opcode()) { case spv::Op::OpSampledImage: case spv::Op::OpImage: case spv::Op::OpVariable: - value = TakeNextValueNumber(); - id_to_value_[inst->result_id()] = value; - return value; + return assign_new_number(inst); default: break; } + // A load that yields an image, sampler, or sampled image must remain in + // the same basic block. So assign it its own value number. + if (inst->IsLoad()) { + switch (context()->get_def_use_mgr()->GetDef(inst->type_id())->opcode()) { + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: + return assign_new_number(inst); + default: + break; + } + } + // If it is a load from memory that can be modified, we have to assume the // memory has been modified, so we give it a new value number. // // Note that this test will also handle volatile loads because they are not // read only. However, if this is ever relaxed because we analyze stores, we // will have to add a new case for volatile loads. - if (inst->IsLoad() && !inst->IsReadOnlyLoad()) { - value = TakeNextValueNumber(); - id_to_value_[inst->result_id()] = value; - return value; + if (inst->IsLoad() && !IsReadOnlyLoad(inst)) { + return assign_new_number(inst); } analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr(); @@ -130,8 +182,15 @@ } } - // TODO: Implement a normal form for opcodes that commute like integer - // addition. This will let us know that a+b is the same value as b+a. + // Apply normal form, so a+b == b+a + if (spvOpcodeIsCommutativeBinaryOperator(value_ins.opcode())) { + if (value_ins.GetSingleWordInOperand(0) > + value_ins.GetSingleWordInOperand(1)) { + value_ins.SetInOperands( + {{SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(1)}}, + {SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(0)}}}); + } + } // Otherwise, we check if this value has been computed before. auto value_iterator = instruction_to_value_.find(value_ins);
diff --git a/source/opt/value_number_table.h b/source/opt/value_number_table.h index 39129ff..c7c909b 100644 --- a/source/opt/value_number_table.h +++ b/source/opt/value_number_table.h
@@ -70,6 +70,14 @@ // Assigns a value number to every result id in the module. void BuildDominatorTreeValueNumberTable(); + // Returns true if |inst| is a load from read-only memory. This is a cached + // version of |Instruction::IsReadOnlyLoad| that is local to this pass. + bool IsReadOnlyLoad(Instruction* inst); + + // Returns true if the variable pointed to by |address_def| is read-only. + // This is the part of |IsReadOnlyLoad| that is cached. + bool IsReadOnlyVariable(Instruction* address_def); + // Returns the new value number. uint32_t TakeNextValueNumber() { return next_value_number_++; } @@ -81,6 +89,10 @@ std::unordered_map<Instruction, uint32_t, ValueTableHash, ComputeSameValue> instruction_to_value_; std::unordered_map<uint32_t, uint32_t> id_to_value_; + // A cache for the results of |IsReadOnlyVariable|. The key is the base + // variable of a load. + std::unordered_map<uint32_t, bool> read_only_variable_cache_; + IRContext* context_; uint32_t next_value_number_; };
diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp index cc33f8b..5b4c68c 100644 --- a/source/parsed_operand.cpp +++ b/source/parsed_operand.cpp
@@ -43,12 +43,38 @@ *out << word; break; case SPV_NUMBER_FLOATING: - if (operand.number_bit_width == 16) { - *out << spvtools::utils::FloatProxy<spvtools::utils::Float16>( - uint16_t(word & 0xFFFF)); - } else { - // Assume 32-bit floats. - *out << spvtools::utils::FloatProxy<float>(word); + switch (operand.fp_encoding) { + case SPV_FP_ENCODING_IEEE754_BINARY16: + *out << spvtools::utils::FloatProxy<spvtools::utils::Float16>( + uint16_t(word & 0xFFFF)); + break; + case SPV_FP_ENCODING_IEEE754_BINARY32: + *out << spvtools::utils::FloatProxy<float>(word); + break; + case SPV_FP_ENCODING_FLOAT8_E4M3: + *out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>( + uint8_t(word & 0xFF)); + break; + case SPV_FP_ENCODING_FLOAT8_E5M2: + *out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>( + uint8_t(word & 0xFF)); + break; + case SPV_FP_ENCODING_BFLOAT16: + *out << spvtools::utils::FloatProxy<spvtools::utils::BFloat16>( + uint16_t(word & 0xFFFF)); + break; + case SPV_FP_ENCODING_UNKNOWN: + switch (operand.number_bit_width) { + case 16: + *out << spvtools::utils::FloatProxy<spvtools::utils::Float16>( + uint16_t(word & 0xFFFF)); + break; + case 32: + *out << spvtools::utils::FloatProxy<float>(word); + break; + } + default: + break; } break; default:
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt index 9ebe418..b631e9d 100644 --- a/source/reduce/CMakeLists.txt +++ b/source/reduce/CMakeLists.txt
@@ -75,11 +75,6 @@ structured_loop_to_selection_reduction_opportunity_finder.cpp ) -if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) - # Enable parallel builds across four cores for this lib - add_definitions(/MP4) -endif() - spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce) add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_REDUCE_SOURCES})
diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index 8e1b2dd..fe23fa6 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp
@@ -142,11 +142,20 @@ }; // When a new SPIR-V version is released, update this table. +// Users see this ordered list when running 'spirv-val --help'. Order +// matters for readability. static_assert(spv::Version == 0x10600); inline constexpr std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = { - {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + // Do not reorder blindly. The algorithm to find the target looks for + // the first entry where the key is a prefix of the string provided by + // the user. For example, if the user provides `vulkan1.2spv1.5`, it + // will match `vulkan1.2`. If this feature is to work correctly, the + // keys must be ordered so that a string is before its prefix. For + // example, `vulkan1.1spv1.4` must be before `vulkan1.1`. Otherwise, + // `vulkan1.1` will be returned when looking for `vulkan1.1spv1.4`. {"vulkan1.0", SPV_ENV_VULKAN_1_0}, + {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {"vulkan1.1", SPV_ENV_VULKAN_1_1}, {"vulkan1.2", SPV_ENV_VULKAN_1_2}, {"vulkan1.3", SPV_ENV_VULKAN_1_3}, @@ -230,7 +239,7 @@ // If no match, determine whether the header has ended (in which case, // assumption has failed.) // Skip until the next line. - i = j; + i += j; for (; i < text.size(); ++i) { if (text[i] == '\n') break; }
diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp index b72a644..a9591f6 100644 --- a/source/spirv_validator_options.cpp +++ b/source/spirv_validator_options.cpp
@@ -126,6 +126,16 @@ options->allow_localsizeid = val; } +void spvValidatorOptionsSetAllowOffsetTextureOperand( + spv_validator_options options, bool val) { + options->allow_offset_texture_operand = val; +} + +void spvValidatorOptionsSetAllowVulkan32BitBitwise( + spv_validator_options options, bool val) { + options->allow_vulkan_32_bit_bitwise = val; +} + void spvValidatorOptionsSetFriendlyNames(spv_validator_options options, bool val) { options->use_friendly_names = val;
diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h index 0145048..9f0c125 100644 --- a/source/spirv_validator_options.h +++ b/source/spirv_validator_options.h
@@ -48,6 +48,8 @@ workgroup_scalar_block_layout(false), skip_block_layout(false), allow_localsizeid(false), + allow_offset_texture_operand(false), + allow_vulkan_32_bit_bitwise(false), before_hlsl_legalization(false), use_friendly_names(true) {} @@ -60,6 +62,8 @@ bool workgroup_scalar_block_layout; bool skip_block_layout; bool allow_localsizeid; + bool allow_offset_texture_operand; + bool allow_vulkan_32_bit_bitwise; bool before_hlsl_legalization; bool use_friendly_names; };
diff --git a/source/table.cpp b/source/table.cpp index 01df1bc..3f31373 100644 --- a/source/table.cpp +++ b/source/table.cpp
@@ -49,16 +49,7 @@ return nullptr; } - spv_opcode_table opcode_table; - spv_operand_table operand_table; - spv_ext_inst_table ext_inst_table; - - spvOpcodeTableGet(&opcode_table, env); - spvOperandTableGet(&operand_table, env); - spvExtInstTableGet(&ext_inst_table, env); - - return new spv_context_t{env, opcode_table, operand_table, ext_inst_table, - nullptr /* a null default consumer */}; + return new spv_context_t{env, nullptr /* a null default consumer */}; } void spvContextDestroy(spv_context context) { delete context; }
diff --git a/source/table.h b/source/table.h index 47625c5..e61a4d4 100644 --- a/source/table.h +++ b/source/table.h
@@ -17,104 +17,14 @@ #include "source/extensions.h" #include "source/latest_version_spirv_header.h" +#include "source/util/index_range.h" #include "spirv-tools/libspirv.hpp" -typedef struct spv_opcode_desc_t { - const char* name; - const spv::Op opcode; - const uint32_t numAliases; - const char** aliases; - const uint32_t numCapabilities; - const spv::Capability* capabilities; - // operandTypes[0..numTypes-1] describe logical operands for the instruction. - // The operand types include result id and result-type id, followed by - // the types of arguments. - const uint16_t numTypes; - spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? - const bool hasResult; // Does the instruction have a result ID operand? - const bool hasType; // Does the instruction have a type ID operand? - // A set of extensions that enable this feature. If empty then this operand - // value is in core and its availability is subject to minVersion. The - // assembler, binary parser, and disassembler ignore this rule, so you can - // freely process invalid modules. - const uint32_t numExtensions; - const spvtools::Extension* extensions; - // Minimal core SPIR-V version required for this feature, if without - // extensions. ~0u means reserved for future use. ~0u and non-empty extension - // lists means only available in extensions. - const uint32_t minVersion; - const uint32_t lastVersion; -} spv_opcode_desc_t; - -typedef struct spv_operand_desc_t { - const char* name; - const uint32_t value; - const uint32_t numAliases; - const char** aliases; - const uint32_t numCapabilities; - const spv::Capability* capabilities; - // A set of extensions that enable this feature. If empty then this operand - // value is in core and its availability is subject to minVersion. The - // assembler, binary parser, and disassembler ignore this rule, so you can - // freely process invalid modules. - const uint32_t numExtensions; - const spvtools::Extension* extensions; - const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? - // Minimal core SPIR-V version required for this feature, if without - // extensions. ~0u means reserved for future use. ~0u and non-empty extension - // lists means only available in extensions. - const uint32_t minVersion; - const uint32_t lastVersion; -} spv_operand_desc_t; - -typedef struct spv_operand_desc_group_t { - const spv_operand_type_t type; - const uint32_t count; - const spv_operand_desc_t* entries; -} spv_operand_desc_group_t; - -typedef struct spv_ext_inst_desc_t { - const char* name; - const uint32_t ext_inst; - const uint32_t numCapabilities; - const spv::Capability* capabilities; - const spv_operand_type_t operandTypes[40]; // vksp needs at least 40 -} spv_ext_inst_desc_t; - -typedef struct spv_ext_inst_group_t { - const spv_ext_inst_type_t type; - const uint32_t count; - const spv_ext_inst_desc_t* entries; -} spv_ext_inst_group_t; - -typedef struct spv_opcode_table_t { - const uint32_t count; - const spv_opcode_desc_t* entries; -} spv_opcode_table_t; - -typedef struct spv_operand_table_t { - const uint32_t count; - const spv_operand_desc_group_t* types; -} spv_operand_table_t; - -typedef struct spv_ext_inst_table_t { - const uint32_t count; - const spv_ext_inst_group_t* groups; -} spv_ext_inst_table_t; - -typedef const spv_opcode_desc_t* spv_opcode_desc; -typedef const spv_operand_desc_t* spv_operand_desc; -typedef const spv_ext_inst_desc_t* spv_ext_inst_desc; - -typedef const spv_opcode_table_t* spv_opcode_table; -typedef const spv_operand_table_t* spv_operand_table; -typedef const spv_ext_inst_table_t* spv_ext_inst_table; +// NOTE: Instruction and operand tables have moved to table2.{h|cpp}, +// where they are represented more compactly. struct spv_context_t { const spv_target_env target_env; - const spv_opcode_table opcode_table; - const spv_operand_table operand_table; - const spv_ext_inst_table ext_inst_table; spvtools::MessageConsumer consumer; }; @@ -125,13 +35,4 @@ void SetContextMessageConsumer(spv_context context, MessageConsumer consumer); } // namespace spvtools -// Populates *table with entries for env. -spv_result_t spvOpcodeTableGet(spv_opcode_table* table, spv_target_env env); - -// Populates *table with entries for env. -spv_result_t spvOperandTableGet(spv_operand_table* table, spv_target_env env); - -// Populates *table with entries for env. -spv_result_t spvExtInstTableGet(spv_ext_inst_table* table, spv_target_env env); - #endif // SOURCE_TABLE_H_
diff --git a/source/table2.cpp b/source/table2.cpp new file mode 100644 index 0000000..dc1bff3 --- /dev/null +++ b/source/table2.cpp
@@ -0,0 +1,440 @@ +// Copyright (c) 2025 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. + +// Compressed grammar tables. + +#include "source/table2.h" + +#include <algorithm> +#include <array> +#include <cstring> + +#include "source/extensions.h" +#include "source/latest_version_spirv_header.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace { + +// This is used in the source for the generated tables. +constexpr inline IndexRange IR(uint32_t first, uint32_t count) { + return IndexRange{first, count}; +} + +struct NameIndex { + // Location of the null-terminated name in the global string table kStrings. + IndexRange name; + // Index of this name's entry in the corresponding by-value table. + uint32_t index; +}; + +struct NameValue { + // Location of the null-terminated name in the global string table kStrings. + IndexRange name; + // Enum value in the binary format. + uint32_t value; +}; + +// The generated include file contains variables: +// +// std::array<NameIndex,...> kOperandNames: +// Operand names and index, ordered by (operand kind, name) +// The index part is the named entry's index in kOperandsByValue array. +// Aliases are included as their own entries. +// +// std::array<OperandDesc, ...> kOperandsByValue: +// Operand descriptions, ordered by (operand kind, operand enum value). +// +// std::array<NameIndex,...> kInstructionNames: +// Instruction names and index, ordered by (name, value) +// The index part is the named entry's index in kInstructionDesc array. +// Aliases are included as their own entries. +// +// std::array<InstructionDesc, ...> kInstructionDesc +// Instruction descriptions, ordered by opcode. +// +// const char kStrings[] +// Array of characters, referenced by IndexRanges elsewhere. +// Each IndexRange denotes a string. +// +// const IndexRange kAliasSpans[] +// Array of IndexRanges, where each represents a string by referencing +// the kStrings table. +// This array contains all sequences of alias strings used in the grammar. +// This table is referenced by an IndexRange elsewhere, i.e. by the +// 'aliases' field of an instruction or operand description. +// +// const spv::Capability kCapabilitySpans[] +// Array of capabilities, referenced by IndexRanges elsewhere. +// Contains all sequences of capabilities used in the grammar. +// +// const spvtools::Extension kExtensionSpans[] = { +// Array of extensions, referenced by IndexRanges elsewhere. +// Contains all sequences of extensions used in the grammar. +// +// const spv_operand_type_t kOperandSpans[] = { +// Array of operand types, referenced by IndexRanges elsewhere. +// Contains all sequences of operand types used in the grammar. + +// Maps an operand kind to NameValue entries for that kind. +// The result is an IndexRange into kOperandNames, and are sorted +// by string name within that span. +IndexRange OperandNameRangeForKind(spv_operand_type_t type); + +// Maps an operand kind to possible operands for that kind. +// The result is an IndexRange into kOperandsByValue, and the operands +// are sorted by value within that span. +IndexRange OperandByValueRangeForKind(spv_operand_type_t type); + +// Maps an extended instruction set kind to NameValue entries for that kind. +// The result is an IndexRange into kExtIntNames, and are sorted +// by string name within that span. +IndexRange ExtInstNameRangeForKind(spv_ext_inst_type_t type); + +// Maps an extended instruction set kind to possible operands for that kind. +// The result is an IndexRange into kExtInstByValue, and the instructions +// are sorted by opcode value within that span. +IndexRange ExtInstByValueRangeForKind(spv_ext_inst_type_t type); + +// Returns the name of an extension, as an index into kStrings +IndexRange ExtensionToIndexRange(Extension extension); + +#include "core_tables_body.inc" + +// Returns a pointer to the null-terminated C-style string in the global +// strings table, as referenced by 'ir'. Assumes the given range is valid. +const char* getChars(IndexRange ir) { + assert(ir.first() < sizeof(kStrings)); + return ir.apply(kStrings).data(); +} + +} // anonymous namespace + +utils::Span<const spv_operand_type_t> OperandDesc::operands() const { + return operands_range.apply(kOperandSpans); +} +utils::Span<const char> OperandDesc::name() const { + return name_range.apply(kStrings); +} +utils::Span<const IndexRange> OperandDesc::aliases() const { + return name_range.apply(kAliasSpans); +} +utils::Span<const spv::Capability> OperandDesc::capabilities() const { + return capabilities_range.apply(kCapabilitySpans); +} +utils::Span<const spvtools::Extension> OperandDesc::extensions() const { + return extensions_range.apply(kExtensionSpans); +} + +utils::Span<const spv_operand_type_t> InstructionDesc::operands() const { + return operands_range.apply(kOperandSpans); +} +utils::Span<const char> InstructionDesc::name() const { + return name_range.apply(kStrings); +} +utils::Span<const IndexRange> InstructionDesc::aliases() const { + return name_range.apply(kAliasSpans); +} +utils::Span<const spv::Capability> InstructionDesc::capabilities() const { + return capabilities_range.apply(kCapabilitySpans); +} +utils::Span<const spvtools::Extension> InstructionDesc::extensions() const { + return extensions_range.apply(kExtensionSpans); +} + +utils::Span<const spv_operand_type_t> ExtInstDesc::operands() const { + return operands_range.apply(kOperandSpans); +} +utils::Span<const char> ExtInstDesc::name() const { + return name_range.apply(kStrings); +} +utils::Span<const spv::Capability> ExtInstDesc::capabilities() const { + return capabilities_range.apply(kCapabilitySpans); +} + +spv_result_t LookupOpcode(spv::Op opcode, const InstructionDesc** desc) { + // Metaphor: Look for the needle in the haystack. + const InstructionDesc needle(opcode); + auto where = std::lower_bound( + kInstructionDesc.begin(), kInstructionDesc.end(), needle, + [&](const InstructionDesc& lhs, const InstructionDesc& rhs) { + return uint32_t(lhs.opcode) < uint32_t(rhs.opcode); + }); + if (where != kInstructionDesc.end() && where->opcode == opcode) { + *desc = &*where; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t LookupOpcode(const char* name, const InstructionDesc** desc) { + // The comparison function knows to use 'name' string to compare against + // when the value is kSentinel. + const auto kSentinel = uint32_t(-1); + const NameIndex needle{{}, kSentinel}; + auto less = [&](const NameIndex& lhs, const NameIndex& rhs) { + const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name); + const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name); + return std::strcmp(lhs_chars, rhs_chars) < 0; + }; + + auto where = std::lower_bound(kInstructionNames.begin(), + kInstructionNames.end(), needle, less); + if (where != kInstructionNames.end() && + std::strcmp(getChars(where->name), name) == 0) { + *desc = &kInstructionDesc[where->index]; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} + +namespace { +template <typename KEY_TYPE> +spv_result_t LookupOpcodeForEnvInternal(spv_target_env env, KEY_TYPE key, + const InstructionDesc** desc) { + const InstructionDesc* desc_proxy; + auto status = LookupOpcode(key, &desc_proxy); + if (status != SPV_SUCCESS) { + return status; + } + const auto& entry = *desc_proxy; + const auto version = spvVersionForTargetEnv(env); + if ((version >= entry.minVersion && version <= entry.lastVersion) || + entry.extensions_range.count() > 0 || + entry.capabilities_range.count() > 0) { + *desc = desc_proxy; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} +} // namespace + +spv_result_t LookupOpcodeForEnv(spv_target_env env, const char* name, + const InstructionDesc** desc) { + return LookupOpcodeForEnvInternal(env, name, desc); +} + +spv_result_t LookupOpcodeForEnv(spv_target_env env, spv::Op opcode, + const InstructionDesc** desc) { + return LookupOpcodeForEnvInternal(env, opcode, desc); +} + +spv_result_t LookupOperand(spv_operand_type_t type, uint32_t value, + const OperandDesc** desc) { + auto ir = OperandByValueRangeForKind(type); + if (ir.empty()) { + return SPV_ERROR_INVALID_LOOKUP; + } + + auto span = ir.apply(kOperandsByValue.data()); + + // Metaphor: Look for the needle in the haystack. + // The operand value is the first member. + const OperandDesc needle{value}; + auto where = + std::lower_bound(span.begin(), span.end(), needle, + [&](const OperandDesc& lhs, const OperandDesc& rhs) { + return lhs.value < rhs.value; + }); + if (where != span.end() && where->value == value) { + *desc = &*where; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t LookupOperand(spv_operand_type_t type, const char* name, + size_t name_len, const OperandDesc** desc) { + auto ir = OperandNameRangeForKind(type); + if (ir.empty()) { + return SPV_ERROR_INVALID_LOOKUP; + } + + auto span = ir.apply(kOperandNames.data()); + + // The comparison function knows to use (name, name_len) as the + // string to compare against when the value is kSentinel. + const auto kSentinel = uint32_t(-1); + const NameIndex needle{{}, kSentinel}; + // The strings in the global string table are null-terminated, and the count + // reflects that. So always deduct 1 from its length. + auto less = [&](const NameIndex& lhs, const NameIndex& rhs) { + const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name); + const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name); + const auto content_cmp = std::strncmp(lhs_chars, rhs_chars, name_len); + if (content_cmp != 0) { + return content_cmp < 0; + } + const auto lhs_len = + lhs.index == kSentinel ? name_len : lhs.name.count() - 1; + const auto rhs_len = + rhs.index == kSentinel ? name_len : rhs.name.count() - 1; + return lhs_len < rhs_len; + }; + + auto where = std::lower_bound(span.begin(), span.end(), needle, less); + if (where != span.end() && where->name.count() - 1 == name_len && + std::strncmp(getChars(where->name), name, name_len) == 0) { + *desc = &kOperandsByValue[where->index]; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t LookupExtInst(spv_ext_inst_type_t type, const char* name, + const ExtInstDesc** desc) { + auto ir = ExtInstNameRangeForKind(type); + if (ir.empty()) { + return SPV_ERROR_INVALID_LOOKUP; + } + + auto span = ir.apply(kExtInstNames.data()); + + // The comparison function knows to use 'name' string to compare against + // when the value is kSentinel. + const auto kSentinel = uint32_t(-1); + const NameIndex needle{{}, kSentinel}; + auto less = [&](const NameIndex& lhs, const NameIndex& rhs) { + const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name); + const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name); + return std::strcmp(lhs_chars, rhs_chars) < 0; + }; + + auto where = std::lower_bound(span.begin(), span.end(), needle, less); + if (where != span.end() && std::strcmp(getChars(where->name), name) == 0) { + *desc = &kExtInstByValue[where->index]; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} + +// Finds the extended instruction description by opcode value. +// On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupExtInst(spv_ext_inst_type_t type, uint32_t value, + const ExtInstDesc** desc) { + auto ir = ExtInstByValueRangeForKind(type); + if (ir.empty()) { + return SPV_ERROR_INVALID_LOOKUP; + } + + auto span = ir.apply(kExtInstByValue.data()); + + // Metaphor: Look for the needle in the haystack. + // The operand value is the first member. + const ExtInstDesc needle(value); + auto where = + std::lower_bound(span.begin(), span.end(), needle, + [&](const ExtInstDesc& lhs, const ExtInstDesc& rhs) { + return lhs.value < rhs.value; + }); + if (where != span.end() && where->value == value) { + *desc = &*where; + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_LOOKUP; +} + +const char* ExtensionToString(Extension extension) { + return getChars(ExtensionToIndexRange(extension)); +} + +bool GetExtensionFromString(const char* name, Extension* extension) { + // The comparison function knows to use 'name' string to compare against + // when the value is kSentinel. + const auto kSentinel = uint32_t(-1); + const NameValue needle{{}, kSentinel}; + auto less = [&](const NameValue& lhs, const NameValue& rhs) { + const char* lhs_chars = lhs.value == kSentinel ? name : getChars(lhs.name); + const char* rhs_chars = rhs.value == kSentinel ? name : getChars(rhs.name); + return std::strcmp(lhs_chars, rhs_chars) < 0; + }; + + auto where = std::lower_bound(kExtensionNames.begin(), kExtensionNames.end(), + needle, less); + if (where != kExtensionNames.end() && + std::strcmp(getChars(where->name), name) == 0) { + *extension = static_cast<Extension>(where->value); + return true; + } + return false; +} + +// This is dirty copy of the spirv.hpp11 function +// TODO - Use a generated version of this function +const char* StorageClassToString(spv::StorageClass value) { + switch (value) { + case spv::StorageClass::UniformConstant: + return "UniformConstant"; + case spv::StorageClass::Input: + return "Input"; + case spv::StorageClass::Uniform: + return "Uniform"; + case spv::StorageClass::Output: + return "Output"; + case spv::StorageClass::Workgroup: + return "Workgroup"; + case spv::StorageClass::CrossWorkgroup: + return "CrossWorkgroup"; + case spv::StorageClass::Private: + return "Private"; + case spv::StorageClass::Function: + return "Function"; + case spv::StorageClass::Generic: + return "Generic"; + case spv::StorageClass::PushConstant: + return "PushConstant"; + case spv::StorageClass::AtomicCounter: + return "AtomicCounter"; + case spv::StorageClass::Image: + return "Image"; + case spv::StorageClass::StorageBuffer: + return "StorageBuffer"; + case spv::StorageClass::TileImageEXT: + return "TileImageEXT"; + case spv::StorageClass::TileAttachmentQCOM: + return "TileAttachmentQCOM"; + case spv::StorageClass::NodePayloadAMDX: + return "NodePayloadAMDX"; + case spv::StorageClass::CallableDataKHR: + return "CallableDataKHR"; + case spv::StorageClass::IncomingCallableDataKHR: + return "IncomingCallableDataKHR"; + case spv::StorageClass::RayPayloadKHR: + return "RayPayloadKHR"; + case spv::StorageClass::HitAttributeKHR: + return "HitAttributeKHR"; + case spv::StorageClass::IncomingRayPayloadKHR: + return "IncomingRayPayloadKHR"; + case spv::StorageClass::ShaderRecordBufferKHR: + return "ShaderRecordBufferKHR"; + case spv::StorageClass::PhysicalStorageBuffer: + return "PhysicalStorageBuffer"; + case spv::StorageClass::HitObjectAttributeNV: + return "HitObjectAttributeNV"; + case spv::StorageClass::TaskPayloadWorkgroupEXT: + return "TaskPayloadWorkgroupEXT"; + case spv::StorageClass::CodeSectionINTEL: + return "CodeSectionINTEL"; + case spv::StorageClass::DeviceOnlyINTEL: + return "DeviceOnlyINTEL"; + case spv::StorageClass::HostOnlyINTEL: + return "HostOnlyINTEL"; + default: + return "Unknown"; + } +} + +} // namespace spvtools
diff --git a/source/table2.h b/source/table2.h new file mode 100644 index 0000000..1218fb2 --- /dev/null +++ b/source/table2.h
@@ -0,0 +1,263 @@ +// Copyright (c) 2025 The Khronos Group Inc. +// Copyright (c) 2025 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_TABLE2_H_ +#define SOURCE_TABLE2_H_ + +#include "source/latest_version_spirv_header.h" +#include "source/util/index_range.h" +#include "spirv-tools/libspirv.hpp" + +// Define the objects that describe the grammatical structure of SPIR-V +// instructions and their operands. The objects are owned by static +// tables populated at C++ build time from the grammar files from SPIRV-Headers. +// +// Clients use freestanding methods to lookup an opcode or an operand, either +// by numeric value (in the binary), or by name. +// +// For historical reasons, the opcode lookup can also use a target enviroment +// enum to filter for opcodes supported in that environment. +// +// It should be very fast for the system loader to load (and possibly relocate) +// the static tables. In particular, there should be very few global symbols +// with independent addresses. Prefer a very few large tables of items rather +// than dozens or hundreds of global symbols. +// +// The overall structure among containers (i.e. skipping scalar data members) +// is as follows: +// +// An OperandDesc describes an operand. +// An InstructionDesc desribes an instruction. +// An ExtInstDesc describes an extended intruction. +// +// Both OperandDesc and InstructionDesc have members: +// - a name string +// - array of alias strings +// - array of spv::Capability (as an enum) +// - array of spv_operand_type_t (as an enum) +// - array of spvtools::Extension (as an enum) +// - a minVersion +// - a lastVersion +// +// An OperandDesc also has: +// - a uint32_t value. +// +// An InstructionDesc also has: +// - a spv::Op opcode +// - a bool hasResult +// - a bool hasType +// - a printing class +// +// An ExtInstDesc has: +// - a name +// - array of spv::Capability (as an enum) +// - array of spv_operand_type_t (as an enum) +// +// The arrays are represented by spans into a global static array, with one +// array for each of: +// - null-terminated strings, for names +// - arrays of null-terminated strings, for alias lists +// - spv_operand_type_t +// - spv::Capability +// - spvtools::Extension +// +// Note: Currently alias lists never have more than one element. +// The data structures and code do not assume this. + +// TODO(dneto): convert the tables for extended instructions +// Currently (as defined in table.h): +// An spv_ext_inst_group_t has: +// - array of spv_ext_inst_desc_t +// +// An spv_ext_inst_desc_t has: +// - a name string +// - array of spv::Capability +// - array of spv_operand_type_t + +namespace spvtools { + +#include "core_tables_header.inc" + +using IndexRange = utils::IndexRange<uint32_t, uint32_t>; + +// Describes a SPIR-V operand. +struct OperandDesc { + const uint32_t value; + + const IndexRange operands_range; // Indexes kOperandSpans + const IndexRange name_range; // Indexes kStrings + const IndexRange aliases_range; // Indexes kAliasSpans + const IndexRange capabilities_range; // Indexes kCapabilitySpans + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const IndexRange extensions_range; // Indexes kExtensionSpans + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty + // extension lists means only available in extensions. + const uint32_t minVersion = 0xFFFFFFFFu; + const uint32_t lastVersion = 0xFFFFFFFFu; + utils::Span<const spv_operand_type_t> operands() const; + utils::Span<const char> name() const; + utils::Span<const IndexRange> aliases() const; + utils::Span<const spv::Capability> capabilities() const; + utils::Span<const spvtools::Extension> extensions() const; + + constexpr OperandDesc(uint32_t v, IndexRange o, IndexRange n, IndexRange a, + IndexRange c, IndexRange e, uint32_t mv, uint32_t lv) + : value(v), + operands_range(o), + name_range(n), + aliases_range(a), + capabilities_range(c), + extensions_range(e), + minVersion(mv), + lastVersion(lv) {} + + constexpr OperandDesc(uint32_t v) : value(v) {} + + OperandDesc(const OperandDesc&) = delete; + OperandDesc(OperandDesc&&) = delete; +}; + +// Describes an Instruction +struct InstructionDesc { + const spv::Op opcode; + const bool hasResult = false; + const bool hasType = false; + + const IndexRange operands_range; // Indexes kOperandSpans + const IndexRange name_range; // Indexes kStrings + const IndexRange aliases_range; // Indexes kAliasSpans + const IndexRange capabilities_range; // Indexes kCapbilitySpans + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const IndexRange extensions_range; // Indexes kExtensionSpans + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty + // extension lists means only available in extensions. + const uint32_t minVersion = 0xFFFFFFFFu; + const uint32_t lastVersion = 0xFFFFFFFFu; + // The printing class specifies what kind of instruction it is, e.g. what + // section of the SPIR-V spec. E.g. kImage, kComposite + const PrintingClass printingClass = PrintingClass::kReserved; + // Returns the span of elements in the global grammar tables corresponding + // to the privately-stored index ranges + utils::Span<const spv_operand_type_t> operands() const; + utils::Span<const char> name() const; + utils::Span<const IndexRange> aliases() const; + utils::Span<const spv::Capability> capabilities() const; + utils::Span<const spvtools::Extension> extensions() const; + + constexpr InstructionDesc(spv::Op oc, bool hr, bool ht, IndexRange o, + IndexRange n, IndexRange a, IndexRange c, + IndexRange e, uint32_t mv, uint32_t lv, + PrintingClass pc) + : opcode(oc), + hasResult(hr), + hasType(ht), + operands_range(o), + name_range(n), + aliases_range(a), + capabilities_range(c), + extensions_range(e), + minVersion(mv), + lastVersion(lv), + printingClass(pc) {} + + constexpr InstructionDesc(spv::Op oc) : opcode(oc) {} + + InstructionDesc(const InstructionDesc&) = delete; + InstructionDesc(InstructionDesc&&) = delete; +}; + +// Describes an extended instruction +struct ExtInstDesc { + const uint32_t value; + const IndexRange operands_range; // Indexes kOperandSpans + const IndexRange name_range; // Indexes kStrings + const IndexRange capabilities_range; // Indexes kCapbilitySpans + // Returns the span of elements in the global grammar tables corresponding + // to the privately-stored index ranges + utils::Span<const spv_operand_type_t> operands() const; + utils::Span<const char> name() const; + utils::Span<const spv::Capability> capabilities() const; + + constexpr ExtInstDesc(uint32_t v, IndexRange o, IndexRange n, IndexRange c) + : value(v), operands_range(o), name_range(n), capabilities_range(c) {} + + constexpr ExtInstDesc(uint32_t v) : value(v) {} + + ExtInstDesc(const ExtInstDesc&) = delete; + ExtInstDesc(ExtInstDesc&&) = delete; +}; + +// Finds the instruction description by opcode name. The name should not +// have the "Op" prefix. On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupOpcode(const char* name, const InstructionDesc** desc); +// Finds the instruction description by opcode value. +// On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupOpcode(spv::Op opcode, const InstructionDesc** desc); + +// Finds the instruction description by opcode name, without the "Op" prefix. +// A lookup will succeed if: +// - The instruction exists, and +// - Either the target environment supports the SPIR-V version of the +// instruction, +// or the instruction is enabled by at least one extension, +// or the instruction is enabled by at least one capability., +// On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupOpcodeForEnv(spv_target_env env, const char* name, + const InstructionDesc** desc); + +// Finds the instruction description by opcode value. +// A lookup will succeed if: +// - The instruction exists, and +// - Either the target environment supports the SPIR-V version of the +// instruction, +// or the instruction is enabled by at least one extension, +// or the instruction is enabled by at least one capability., +// On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupOpcodeForEnv(spv_target_env env, spv::Op, + const InstructionDesc** desc); + +spv_result_t LookupOperand(spv_operand_type_t type, const char* name, + size_t name_len, const OperandDesc** desc); +spv_result_t LookupOperand(spv_operand_type_t type, uint32_t operand, + const OperandDesc** desc); + +// Finds the extended instruction description by opcode name. +// On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupExtInst(spv_ext_inst_type_t type, const char* name, + const ExtInstDesc** desc); +// Finds the extended instruction description by opcode value. +// On success, returns SPV_SUCCESS and updates *desc. +spv_result_t LookupExtInst(spv_ext_inst_type_t type, uint32_t value, + const ExtInstDesc** desc); + +// Finds Extension enum corresponding to |str|. Returns false if not found. +bool GetExtensionFromString(const char* str, Extension* extension); + +// Returns text string corresponding to |extension|. +const char* ExtensionToString(Extension extension); + +/// Used to provide better error message +const char* StorageClassToString(spv::StorageClass value); + +} // namespace spvtools +#endif // SOURCE_TABLE2_H_
diff --git a/source/text.cpp b/source/text.cpp index 2154e85..0978d81 100644 --- a/source/text.cpp +++ b/source/text.cpp
@@ -38,6 +38,7 @@ #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/table.h" +#include "source/table2.h" #include "source/text_handler.h" #include "source/util/bitutils.h" #include "source/util/parse_number.h" @@ -241,14 +242,20 @@ case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { // The assembler accepts the symbolic name for an extended instruction, // and emits its corresponding number. - spv_ext_inst_desc extInst; - if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) == + const spvtools::ExtInstDesc* desc = nullptr; + if (spvtools::LookupExtInst(pInst->extInstType, textValue, &desc) == SPV_SUCCESS) { // if we know about this extended instruction, push the numeric value - spvInstructionAddWord(pInst, extInst->ext_inst); + spvInstructionAddWord(pInst, desc->value); + + // Push VARIABLE_ID so extra trailing operands from future NSDI + // versions are silently absorbed after the instruction-specific ones. + if (spvExtInstIsNonSemantic(pInst->extInstType)) { + pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } // Prepare to parse the operands for the extended instructions. - spvPushOperandTypes(extInst->operandTypes, pExpectedOperands); + spvPushOperandTypes(desc->operands(), pExpectedOperands); } else { // if we don't know this extended instruction and the set isn't // non-semantic, we cannot process further @@ -284,8 +291,8 @@ return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) << " '" << textValue << "'."; } - spv_opcode_desc opcodeEntry = nullptr; - if (grammar.lookupOpcode(opcode, &opcodeEntry)) { + const spvtools::InstructionDesc* opcodeEntry = nullptr; + if (LookupOpcodeForEnv(grammar.target_env(), opcode, &opcodeEntry)) { return context->diagnostic(SPV_ERROR_INTERNAL) << "OpSpecConstant opcode table out of sync"; } @@ -295,8 +302,9 @@ // type Id and result Id, since they've already been processed. assert(opcodeEntry->hasType); assert(opcodeEntry->hasResult); - assert(opcodeEntry->numTypes >= 2); - spvPushOperandTypes(opcodeEntry->operandTypes + 2, pExpectedOperands); + assert(opcodeEntry->operands().size() >= 2); + spvPushOperandTypes(opcodeEntry->operands().subspan(2), + pExpectedOperands); } break; case SPV_OPERAND_TYPE_LITERAL_INTEGER: @@ -346,10 +354,11 @@ context->getTypeOfTypeGeneratingValue(pInst->resultTypeId); if (!spvtools::isScalarFloating(expected_type) && !spvtools::isScalarIntegral(expected_type)) { - spv_opcode_desc d; + const spvtools::InstructionDesc* opcodeEntry = nullptr; const char* opcode_name = "opcode"; - if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) { - opcode_name = d->name; + if (SPV_SUCCESS == LookupOpcode(pInst->opcode, &opcodeEntry)) { + opcode_name = + opcodeEntry->name().data(); // assumes it's null-terminated } return context->diagnostic() << "Type for " << opcode_name @@ -409,6 +418,8 @@ case SPV_OPERAND_TYPE_LOOP_CONTROL: case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_TENSOR_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: @@ -416,7 +427,8 @@ case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: - case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: { + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: { uint32_t value; if (auto error = grammar.parseMaskOperand(type, textValue, &value)) { return context->diagnostic(error) @@ -454,8 +466,8 @@ default: { // NOTE: All non literal operands are handled here using the operand // table. - spv_operand_desc entry; - if (grammar.lookupOperand(type, textValue, strlen(textValue), &entry)) { + const spvtools::OperandDesc* entry = nullptr; + if (spvtools::LookupOperand(type, textValue, strlen(textValue), &entry)) { return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) << " '" << textValue << "'."; } @@ -465,7 +477,7 @@ } // Prepare to parse the operands for this logical operand. - spvPushOperandTypes(entry->operandTypes, pExpectedOperands); + spvPushOperandTypes(entry->operands(), pExpectedOperands); } break; } return SPV_SUCCESS; @@ -502,7 +514,7 @@ if (operandValue == "=") return context->diagnostic() << firstWord << " not allowed before =."; - // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be + // Needed to pass to spvTextEncodeOperand(), but it shouldn't ever be // expanded. spv_operand_pattern_t dummyExpectedOperands; error = spvTextEncodeOperand( @@ -514,6 +526,124 @@ return SPV_SUCCESS; } +/// @brief Translate an instruction started by OpUnknown and the following +/// operands to binary form +/// +/// @param[in] grammar the grammar to use for compilation +/// @param[in, out] context the dynamic compilation info +/// @param[out] pInst returned binary Opcode +/// +/// @return result code +spv_result_t encodeInstructionStartingWithOpUnknown( + const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, spv_instruction_t* pInst) { + spv_position_t nextPosition = {}; + + uint16_t opcode; + uint16_t wordCount; + + // The '(' character. + if (context->advance()) + return context->diagnostic() << "Expected '(', found end of stream."; + if ('(' != context->peek()) { + return context->diagnostic() << "'(' expected after OpUnknown but found '" + << context->peek() << "'."; + } + context->seekForward(1); + + // The opcode enumerant. + if (context->advance()) + return context->diagnostic() + << "Expected opcode enumerant, found end of stream."; + std::string opcodeString; + spv_result_t error = context->getWord(&opcodeString, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + if (!spvtools::utils::ParseNumber(opcodeString.c_str(), &opcode)) { + return context->diagnostic() + << "Invalid opcode enumerant: \"" << opcodeString << "\"."; + } + + context->setPosition(nextPosition); + + // The ',' character. + if (context->advance()) + return context->diagnostic() << "Expected ',', found end of stream."; + if (',' != context->peek()) { + return context->diagnostic() + << "',' expected after opcode enumerant but found '" + << context->peek() << "'."; + } + context->seekForward(1); + + // The number of words. + if (context->advance()) + return context->diagnostic() + << "Expected number of words, found end of stream."; + std::string wordCountString; + error = context->getWord(&wordCountString, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + if (!spvtools::utils::ParseNumber(wordCountString.c_str(), &wordCount)) { + return context->diagnostic() + << "Invalid number of words: \"" << wordCountString << "\"."; + } + + if (wordCount == 0) { + return context->diagnostic() << "Number of words (which includes the " + "opcode) must be greater than zero."; + } + + context->setPosition(nextPosition); + + // The ')' character. + if (context->advance()) + return context->diagnostic() << "Expected ')', found end of stream."; + if (')' != context->peek()) { + return context->diagnostic() + << "')' expected after number of words but found '" + << context->peek() << "'."; + } + context->seekForward(1); + + pInst->opcode = static_cast<spv::Op>(opcode); + context->binaryEncodeU32(spvOpcodeMake(wordCount, pInst->opcode), pInst); + + wordCount--; // Subtract the opcode from the number of words left to read. + + while (wordCount-- > 0) { + if (context->advance() == SPV_END_OF_STREAM) { + return context->diagnostic() << "Expected " << wordCount + 1 + << " more operands, found end of stream."; + } + if (context->isStartOfNewInst()) { + std::string invalid; + context->getWord(&invalid, &nextPosition); + return context->diagnostic() + << "Unexpected start of new instruction: \"" << invalid + << "\". Expected " << wordCount + 1 << " more operands"; + } + + std::string operandValue; + if ((error = context->getWord(&operandValue, &nextPosition))) + return context->diagnostic(error) << "Internal Error"; + + if (operandValue == "=") + return context->diagnostic() << "OpUnknown not allowed before =."; + + // Needed to pass to spvTextEncodeOperand(), but it shouldn't ever be + // expanded. + spv_operand_pattern_t dummyExpectedOperands; + error = spvTextEncodeOperand( + grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), + pInst, &dummyExpectedOperands); + if (error) return error; + context->setPosition(nextPosition); + } + + return SPV_SUCCESS; +} + /// @brief Translate single Opcode and operands to binary form /// /// @param[in] grammar the grammar to use for compilation @@ -573,11 +703,21 @@ } } + if (opcodeName == "OpUnknown") { + if (!result_id.empty()) { + return context->diagnostic() + << "OpUnknown not allowed in assignment. Use an explicit result " + "id operand instead."; + } + context->setPosition(nextPosition); + return encodeInstructionStartingWithOpUnknown(grammar, context, pInst); + } + // NOTE: The table contains Opcode names without the "Op" prefix. const char* pInstName = opcodeName.data() + 2; - spv_opcode_desc opcodeEntry; - error = grammar.lookupOpcode(pInstName, &opcodeEntry); + const spvtools::InstructionDesc* opcodeEntry = nullptr; + error = LookupOpcodeForEnv(grammar.target_env(), pInstName, &opcodeEntry); if (error) { return context->diagnostic(error) << "Invalid Opcode name '" << opcodeName << "'"; @@ -605,10 +745,15 @@ // ExecutionMode), or for extended instructions that may have their // own operands depending on the selected extended instruction. spv_operand_pattern_t expectedOperands; - expectedOperands.reserve(opcodeEntry->numTypes); - for (auto i = 0; i < opcodeEntry->numTypes; i++) - expectedOperands.push_back( - opcodeEntry->operandTypes[opcodeEntry->numTypes - i - 1]); + { + const auto operands = opcodeEntry->operands(); + const auto n = operands.size(); + expectedOperands.reserve(n); + for (auto i = 0u; i < n; i++) { + auto ty = operands[n - i - 1]; + expectedOperands.push_back(ty); + } + } while (!expectedOperands.empty()) { const spv_operand_type_t type = expectedOperands.back(); @@ -724,10 +869,6 @@ if (!text->str) return context.diagnostic() << "Missing assembly text."; - if (!grammar.isValid()) { - return SPV_ERROR_INVALID_TABLE; - } - // Skip past whitespace and comments. context.advance(); @@ -773,10 +914,6 @@ spvtools::AssemblyContext context(text, consumer, std::move(ids_to_preserve)); if (!text->str) return context.diagnostic() << "Missing assembly text."; - - if (!grammar.isValid()) { - return SPV_ERROR_INVALID_TABLE; - } if (!pBinary) return SPV_ERROR_INVALID_POINTER; std::vector<spv_instruction_t> instructions;
diff --git a/source/text_handler.cpp b/source/text_handler.cpp index a778c2c..22401e9 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp
@@ -18,6 +18,7 @@ #include <cassert> #include <cstdlib> #include <cstring> +#include <string_view> #include <tuple> #include "source/assembly_grammar.h" @@ -118,6 +119,9 @@ break; case ' ': case ';': + case ',': + case '(': + case ')': case '\t': case '\n': case '\r': @@ -149,6 +153,31 @@ return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z')); } +// Returns false if the the floating point encoding requires a bit width +// different from the given width. Write the expected bit width via *expected. +bool validBitWidthForFPEncoding(spv_fp_encoding_t enc, uint32_t width, + uint32_t* expected) { + switch (enc) { + case SPV_FP_ENCODING_IEEE754_BINARY16: + case SPV_FP_ENCODING_BFLOAT16: + *expected = 16; + break; + case SPV_FP_ENCODING_IEEE754_BINARY32: + *expected = 32; + break; + case SPV_FP_ENCODING_IEEE754_BINARY64: + *expected = 64; + break; + case SPV_FP_ENCODING_FLOAT8_E5M2: + case SPV_FP_ENCODING_FLOAT8_E4M3: + *expected = 8; + break; + default: + return true; + } + return width == *expected; +} + } // namespace const IdType kUnknownType = {0, false, IdTypeClass::kBottom}; @@ -251,13 +280,13 @@ << "Unexpected numeric literal type"; case IdTypeClass::kScalarIntegerType: if (type.isSigned) { - number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT}; + number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT, type.encoding}; } else { - number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT}; + number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT, type.encoding}; } break; case IdTypeClass::kScalarFloatType: - number_type = {type.bitwidth, SPV_NUMBER_FLOATING}; + number_type = {type.bitwidth, SPV_NUMBER_FLOATING, type.encoding}; break; case IdTypeClass::kBottom: // kBottom means the type is unknown and we need to infer the type before @@ -267,11 +296,11 @@ // signed integer, otherwise an unsigned integer. uint32_t bitwidth = static_cast<uint32_t>(assumedBitWidth(type)); if (strchr(val, '.')) { - number_type = {bitwidth, SPV_NUMBER_FLOATING}; + number_type = {bitwidth, SPV_NUMBER_FLOATING, type.encoding}; } else if (type.isSigned || val[0] == '-') { - number_type = {bitwidth, SPV_NUMBER_SIGNED_INT}; + number_type = {bitwidth, SPV_NUMBER_SIGNED_INT, type.encoding}; } else { - number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT}; + number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT, type.encoding}; } break; } @@ -327,14 +356,36 @@ if (pInst->words.size() != 4) return diagnostic() << "Invalid OpTypeInt instruction"; types_[value] = {pInst->words[2], pInst->words[3] != 0, - IdTypeClass::kScalarIntegerType}; + IdTypeClass::kScalarIntegerType, SPV_FP_ENCODING_UNKNOWN}; } else if (pInst->opcode == spv::Op::OpTypeFloat) { if ((pInst->words.size() != 3) && (pInst->words.size() != 4)) return diagnostic() << "Invalid OpTypeFloat instruction"; - // TODO(kpet) Do we need to record the FP Encoding here? - types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType}; + spv_fp_encoding_t enc = SPV_FP_ENCODING_UNKNOWN; + if (pInst->words.size() >= 4) { + const spvtools::OperandDesc* desc = nullptr; + spv_result_t status = spvtools::LookupOperand(SPV_OPERAND_TYPE_FPENCODING, + pInst->words[3], &desc); + if (status == SPV_SUCCESS) { + enc = spvFPEncodingFromOperandFPEncoding( + static_cast<spv::FPEncoding>(desc->value)); + uint32_t expected_width; + if (!validBitWidthForFPEncoding(enc, pInst->words[2], + &expected_width)) { + const auto& name_span = desc->name(); + const std::string_view name(name_span.data(), name_span.size() - 1); + return diagnostic() << "Invalid bit width " << pInst->words[2] + << " for floating point encoding " << name + << "; expected " << expected_width; + } + } else { + return diagnostic() << "Invalid OpTypeFloat encoding"; + } + } + types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType, + enc}; } else { - types_[value] = {0, false, IdTypeClass::kOtherType}; + types_[value] = {0, false, IdTypeClass::kOtherType, + SPV_FP_ENCODING_UNKNOWN}; } return SPV_SUCCESS; }
diff --git a/source/text_handler.h b/source/text_handler.h index 19972e9..54ffe61 100644 --- a/source/text_handler.h +++ b/source/text_handler.h
@@ -47,6 +47,7 @@ uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits. bool isSigned; // This is only significant if type_class is integral. IdTypeClass type_class; + spv_fp_encoding_t encoding; }; // Default equality operator for IdType. Tests if all members are the same. @@ -141,7 +142,14 @@ spv_result_t advance(); // Sets word to the next word in the input text. Fills next_position with - // the next location past the end of the word. + // the next location past the end of the word. Returns an error if the + // context is invalid or has no more text. Otherwise returns SPV_SUCCESS. + // Assumes the next part of the input is not whitespace. + // + // A word ends at the next comment or whitespace. However, double-quoted + // strings remain intact, and a backslash always escapes the next character. + // The input stream may end before a matching double-quote, or immediately + // after a backslash. Both such cases still count as success. spv_result_t getWord(std::string* word, spv_position next_position); // Returns true if the next word in the input is the start of a new Opcode.
diff --git a/source/util/bitutils.h b/source/util/bitutils.h index 2763bc2..dbf6a9c 100644 --- a/source/util/bitutils.h +++ b/source/util/bitutils.h
@@ -206,6 +206,23 @@ return utils::ClearHighBits(value, bit_width - number_of_bits); } +// Returns the the least significant bit from |value|. +template <typename T> +constexpr T LSB(T value) { + static_assert(std::is_integral<T>::value, "LSB requires integer type"); + if constexpr (std::is_unsigned_v<T>) { + // Prevent warnings about doing a -x on unsigned values. + return value & (~value + 1); + } else { + return value & -value; + } +} + +static_assert(LSB<uint32_t>(UINT32_MAX) == uint32_t(0x00000001), "LSB failed"); +static_assert(LSB<uint32_t>(0x10001000) == uint32_t(0x00001000), "LSB failed"); +static_assert(LSB<uint32_t>(0x10000000) == uint32_t(0x10000000), "LSB failed"); +static_assert(LSB<int32_t>(-1) == int32_t(0x00000001), "LSB failed"); + } // namespace utils } // namespace spvtools
diff --git a/source/util/hex_float.h b/source/util/hex_float.h index 98353a4..03efb14 100644 --- a/source/util/hex_float.h +++ b/source/util/hex_float.h
@@ -36,6 +36,50 @@ namespace spvtools { namespace utils { +class Float8_E4M3 { + public: + Float8_E4M3(uint8_t v) : val(v) {} + Float8_E4M3() = default; + static bool isNan(const Float8_E4M3& val) { return (val.val & 0x7f) == 0x7f; } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float8_E4M3&) { + return false; // E4M3 has no infinity representation + } + Float8_E4M3(const Float8_E4M3& other) { val = other.val; } + uint8_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float8_E4M3 max() { return Float8_E4M3(0x7e); } + // Returns the lowest normal value. + static Float8_E4M3 lowest() { return Float8_E4M3(0x8); } + + private: + uint8_t val; +}; + +class Float8_E5M2 { + public: + Float8_E5M2(uint8_t v) : val(v) {} + Float8_E5M2() = default; + static bool isNan(const Float8_E5M2& val) { + return ((val.val & 0x7c) == 0x7c) && ((val.val & 0x3) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float8_E5M2& val) { + return (val.val & 0x7f) == 0x7c; + } + Float8_E5M2(const Float8_E5M2& other) { val = other.val; } + uint8_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float8_E5M2 max() { return Float8_E5M2(0x7b); } + // Returns the lowest normal value. + static Float8_E5M2 lowest() { return Float8_E5M2(0x4); } + + private: + uint8_t val; +}; + class Float16 { public: Float16(uint16_t v) : val(v) {} @@ -59,6 +103,34 @@ uint16_t val; }; +class BFloat16 { + public: + BFloat16(uint16_t v) : val(v) {} + BFloat16() = default; + BFloat16(const BFloat16& other) { val = other.val; } + + // Exponent mask: 0x7F80, Mantissa mask: 0x007F + static bool isNan(const BFloat16& val) { + return ((val.val & 0x7F80) == 0x7F80) && ((val.val & 0x007F) != 0); + } + static bool isInfinity(const BFloat16& val) { + return ((val.val & 0x7F80) == 0x7F80) && ((val.val & 0x007F) == 0); + } + + uint16_t get_value() const { return val; } + + // a sign bit of 0, and an all 1 mantissa. + static BFloat16 max() { return BFloat16(0x7F7F); } + // a sign bit of 1, and an all 1 mantissa. + static BFloat16 lowest() { return BFloat16(0xFF7F); } + + private: + // 15: Sign + // 14-7: Exponent + // 6-0: Mantissa + uint16_t val; +}; + // To specialize this type, you must override uint_type to define // an unsigned integer that can fit your floating point type. // You must also add a isNan function that returns true if @@ -111,6 +183,46 @@ }; template <> +struct FloatProxyTraits<Float8_E4M3> { + using uint_type = uint8_t; + static bool isNan(Float8_E4M3 f) { return Float8_E4M3::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float8_E4M3 f) { return Float8_E4M3::isInfinity(f); } + // Returns the maximum normal value. + static Float8_E4M3 max() { return Float8_E4M3::max(); } + // Returns the lowest normal value. + static Float8_E4M3 lowest() { return Float8_E4M3::lowest(); } + // Returns the value as the native floating point format. + static Float8_E4M3 getAsFloat(const uint_type& t) { return Float8_E4M3(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const Float8_E4M3& t) { + return t.get_value(); + } + // Returns the bitwidth. + static uint32_t width() { return 8u; } +}; + +template <> +struct FloatProxyTraits<Float8_E5M2> { + using uint_type = uint8_t; + static bool isNan(Float8_E5M2 f) { return Float8_E5M2::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float8_E5M2 f) { return Float8_E5M2::isInfinity(f); } + // Returns the maximum normal value. + static Float8_E5M2 max() { return Float8_E5M2::max(); } + // Returns the lowest normal value. + static Float8_E5M2 lowest() { return Float8_E5M2::lowest(); } + // Returns the value as the native floating point format. + static Float8_E5M2 getAsFloat(const uint_type& t) { return Float8_E5M2(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const Float8_E5M2& t) { + return t.get_value(); + } + // Returns the bitwidth. + static uint32_t width() { return 8u; } +}; + +template <> struct FloatProxyTraits<Float16> { using uint_type = uint16_t; static bool isNan(Float16 f) { return Float16::isNan(f); } @@ -128,6 +240,24 @@ static uint32_t width() { return 16u; } }; +template <> +struct FloatProxyTraits<BFloat16> { + using uint_type = uint16_t; + static bool isNan(BFloat16 f) { return BFloat16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(BFloat16 f) { return BFloat16::isInfinity(f); } + // Returns the maximum normal value. + static BFloat16 max() { return BFloat16::max(); } + // Returns the lowest normal value. + static BFloat16 lowest() { return BFloat16::lowest(); } + // Returns the value as the native floating point format. + static BFloat16 getAsFloat(const uint_type& t) { return BFloat16(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const BFloat16& t) { return t.get_value(); } + // Returns the bitwidth. + static uint32_t width() { return 16u; } +}; + // Since copying a floating point number (especially if it is NaN) // does not guarantee that bits are preserved, this class lets us // store the type and use it as a float when necessary. @@ -216,6 +346,7 @@ using int_type = void; // The numerical type that this HexFloat represents. using underlying_type = void; + using underlying_typetraits = void; // The type needed to construct the underlying type. using native_type = void; // The number of bits that are actually relevant in the uint_type. @@ -229,6 +360,8 @@ // The bias of the exponent. (How much we need to subtract from the stored // value to get the correct value.) static const uint32_t exponent_bias = 0; + static const bool has_infinity = true; + static const uint32_t NaN_pattern = 0; }; // Traits for IEEE float. @@ -238,11 +371,14 @@ using uint_type = uint32_t; using int_type = int32_t; using underlying_type = FloatProxy<float>; + using underlying_typetraits = FloatProxyTraits<float>; using native_type = float; static const uint_type num_used_bits = 32; static const uint_type num_exponent_bits = 8; static const uint_type num_fraction_bits = 23; static const uint_type exponent_bias = 127; + static const bool has_infinity = true; + static const uint_type NaN_pattern = 0x7f80000; }; // Traits for IEEE double. @@ -252,11 +388,48 @@ using uint_type = uint64_t; using int_type = int64_t; using underlying_type = FloatProxy<double>; + using underlying_typetraits = FloatProxyTraits<double>; using native_type = double; static const uint_type num_used_bits = 64; static const uint_type num_exponent_bits = 11; static const uint_type num_fraction_bits = 52; static const uint_type exponent_bias = 1023; + static const bool has_infinity = true; + static const uint_type NaN_pattern = 0x7FF0000000000000; +}; + +// Traits for FP8 E4M3. +// 1 sign bit, 4 exponent bits, 3 fractional bits. +template <> +struct HexFloatTraits<FloatProxy<Float8_E4M3>> { + using uint_type = uint8_t; + using int_type = int8_t; + using underlying_type = FloatProxy<Float8_E4M3>; + using underlying_typetraits = FloatProxyTraits<Float8_E4M3>; + using native_type = uint8_t; + static const uint_type num_used_bits = 8; + static const uint_type num_exponent_bits = 4; + static const uint_type num_fraction_bits = 3; + static const uint_type exponent_bias = 7; + static const bool has_infinity = false; + static const uint_type NaN_pattern = 0x7F; +}; + +// Traits for FP8 E5M2. +// 1 sign bit, 4 exponent bits, 3 fractional bits. +template <> +struct HexFloatTraits<FloatProxy<Float8_E5M2>> { + using uint_type = uint8_t; + using int_type = int8_t; + using underlying_type = FloatProxy<Float8_E5M2>; + using underlying_typetraits = FloatProxyTraits<Float8_E5M2>; + using native_type = uint8_t; + static const uint_type num_used_bits = 8; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 2; + static const uint_type exponent_bias = 15; + static const bool has_infinity = true; + static const uint_type NaN_pattern = 0x7c; }; // Traits for IEEE half. @@ -265,12 +438,32 @@ struct HexFloatTraits<FloatProxy<Float16>> { using uint_type = uint16_t; using int_type = int16_t; - using underlying_type = uint16_t; + using underlying_type = FloatProxy<Float16>; + using underlying_typetraits = FloatProxyTraits<Float16>; using native_type = uint16_t; static const uint_type num_used_bits = 16; static const uint_type num_exponent_bits = 5; static const uint_type num_fraction_bits = 10; static const uint_type exponent_bias = 15; + static const bool has_infinity = true; + static const uint_type NaN_pattern = 0x7c00; +}; + +// Traits for BFloat16. +// 1 sign bit, 7 exponent bits, 8 fractional bits. +template <> +struct HexFloatTraits<FloatProxy<BFloat16>> { + using uint_type = uint16_t; + using int_type = int16_t; + using underlying_type = FloatProxy<BFloat16>; + using underlying_typetraits = FloatProxyTraits<BFloat16>; + using native_type = uint16_t; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 7; + static const uint_type exponent_bias = 127; + static const bool has_infinity = true; + static const uint_type NaN_pattern = 0x7F80; }; enum class round_direction { @@ -291,6 +484,7 @@ using int_type = typename Traits::int_type; using underlying_type = typename Traits::underlying_type; using native_type = typename Traits::native_type; + using traits = Traits; explicit HexFloat(T f) : value_(f) {} @@ -493,9 +687,9 @@ struct negatable_left_shift { static uint_type val(uint_type val) { if (N > 0) { - return static_cast<uint_type>(val << N); + return static_cast<uint_type>(static_cast<uint64_t>(val) << N); } else { - return static_cast<uint_type>(val >> N); + return static_cast<uint_type>(static_cast<uint64_t>(val) >> N); } } }; @@ -519,28 +713,28 @@ template <int_type N, typename enable = void> struct negatable_left_shift { static uint_type val(uint_type val) { - return static_cast<uint_type>(val >> -N); + return static_cast<uint_type>(static_cast<uint64_t>(val) >> -N); } }; template <int_type N> struct negatable_left_shift<N, typename std::enable_if<N >= 0>::type> { static uint_type val(uint_type val) { - return static_cast<uint_type>(val << N); + return static_cast<uint_type>(static_cast<uint64_t>(val) << N); } }; template <int_type N, typename enable = void> struct negatable_right_shift { static uint_type val(uint_type val) { - return static_cast<uint_type>(val << -N); + return static_cast<uint_type>(static_cast<uint64_t>(val) << -N); } }; template <int_type N> struct negatable_right_shift<N, typename std::enable_if<N >= 0>::type> { static uint_type val(uint_type val) { - return static_cast<uint_type>(val >> N); + return static_cast<uint_type>(static_cast<uint64_t>(val) >> N); } }; #endif @@ -639,6 +833,9 @@ // underflow to (0 or min depending on rounding) if the number underflows. template <typename other_T> void castTo(other_T& other, round_direction round_dir) { + using other_traits = typename other_T::traits; + using other_underlyingtraits = typename other_traits::underlying_typetraits; + other = other_T(static_cast<typename other_T::native_type>(0)); bool negate = isNegative(); if (getUnsignedBits() == 0) { @@ -664,18 +861,24 @@ } } - bool is_nan = - (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_nan = T(getBits()).isNan(); bool is_inf = !is_nan && ((exponent + carried) > static_cast<int_type>(other_T::exponent_bias) || - (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + T(getBits()).isInfinity()); // If we are Nan or Inf we should pass that through. if (is_inf) { - other.set_value(typename other_T::underlying_type( - static_cast<typename other_T::uint_type>( - (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + if (other_traits::has_infinity) + other.set_value(typename other_T::underlying_type( + static_cast<typename other_T::uint_type>( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + else // if the type doesnt use infinity, set it to max value (E4M3) + other.set_value(typename other_T::underlying_type( + static_cast<typename other_T::uint_type>( + (negate ? other_T::sign_mask : 0) | + other_underlyingtraits::getBitsFromFloat( + other_underlyingtraits::max())))); return; } if (is_nan) { @@ -690,7 +893,8 @@ // just set the last bit. other.set_value(typename other_T::underlying_type( static_cast<typename other_T::uint_type>( - (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + other_traits::NaN_pattern | (negate ? other_T::sign_mask : 0) | + other_T::exponent_mask | (shifted_significand == 0 ? 0x1 : shifted_significand)))); return; } @@ -738,8 +942,8 @@ template <typename T, typename Traits> std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) { using HF = HexFloat<T, Traits>; - using uint_type = typename HF::uint_type; - using int_type = typename HF::int_type; + using uint_type = uint64_t; + using int_type = int64_t; static_assert(HF::num_used_bits != 0, "num_used_bits must be non-zero for a valid float"); @@ -807,13 +1011,17 @@ return os; } -// Returns true if negate_value is true and the next character on the -// input stream is a plus or minus sign. In that case we also set the fail bit -// on the stream and set the value to the zero value for its type. +// Encodes whether a leading sign has been seen, and if so which one. +enum class LeadingSign { None, Plus, Minus }; + +// Returns true if leading_sign is either Plus or Minus, and the next character +// on the input stream is a plus or minus sign. In that case we also set the +// fail bit on the stream and set the value to the zero value for its type. template <typename T, typename Traits> -inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, +inline bool RejectParseDueToLeadingSign(std::istream& is, + LeadingSign leading_sign, HexFloat<T, Traits>& value) { - if (negate_value) { + if (leading_sign != LeadingSign::None) { auto next_char = is.peek(); if (next_char == '-' || next_char == '+') { // Fail the parse. Emulate standard behaviour by setting the value to @@ -828,22 +1036,24 @@ // Parses a floating point number from the given stream and stores it into the // value parameter. -// If negate_value is true then the number may not have a leading minus or -// plus, and if it successfully parses, then the number is negated before -// being stored into the value parameter. +// If leading_sign is Plus or Minus, then the number may not have a leading +// minus or plus. If it successfully parses, and the leading sign was Minus, +// then the number is negated before being stored into the value parameter. // If the value cannot be correctly parsed or overflows the target floating // point type, then set the fail bit on the stream. // TODO(dneto): Promise C++11 standard behavior in how the value is set in // the error case, but only after all target platforms implement it correctly. // In particular, the Microsoft C++ runtime appears to be out of spec. template <typename T, typename Traits> -inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, +inline std::istream& ParseNormalFloat(std::istream& is, + LeadingSign leading_sign, HexFloat<T, Traits>& value) { - if (RejectParseDueToLeadingSign(is, negate_value, value)) { + if (RejectParseDueToLeadingSign(is, leading_sign, value)) { return is; } T val; is >> val; + const bool negate_value = leading_sign == LeadingSign::Minus; if (negate_value) { val = -val; } @@ -866,8 +1076,9 @@ // This will parse the float as it were a 32-bit floating point number, // and then round it down to fit into a Float16 value. // The number is rounded towards zero. -// If negate_value is true then the number may not have a leading minus or -// plus, and if it successfully parses, then the number is negated before +// If leading_sign is Plus or Minus, then the number may not have a leading +// minus or plus. If it successfully parses, and the leading sign was Minus, +// then the number is negated before being stored into the value parameter. // being stored into the value parameter. // If the value cannot be correctly parsed or overflows the target floating // point type, then set the fail bit on the stream. @@ -877,11 +1088,11 @@ template <> inline std::istream& ParseNormalFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>( - std::istream& is, bool negate_value, + std::istream& is, LeadingSign leading_sign, HexFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>& value) { // First parse as a 32-bit float. HexFloat<FloatProxy<float>> float_val(0.0f); - ParseNormalFloat(is, negate_value, float_val); + ParseNormalFloat(is, leading_sign, float_val); // Then convert to 16-bit float, saturating at infinities, and // rounding toward zero. @@ -889,6 +1100,8 @@ // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the // fail bit and set the lowest or highest value. + // /!\ We get an error if there is no overflow but the value is infinity. + // Is it what we want? if (Float16::isInfinity(value.value().getAsFloat())) { value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); is.setstate(std::ios_base::failbit); @@ -896,6 +1109,98 @@ return is; } +// Same flow as Float16 +template <> +inline std::istream& +ParseNormalFloat<FloatProxy<BFloat16>, HexFloatTraits<FloatProxy<BFloat16>>>( + std::istream& is, LeadingSign leading_sign, + HexFloat<FloatProxy<BFloat16>, HexFloatTraits<FloatProxy<BFloat16>>>& + value) { + HexFloat<FloatProxy<float>> float_val(0.0f); + ParseNormalFloat(is, leading_sign, float_val); + + float_val.castTo(value, round_direction::kToZero); + + if (BFloat16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? BFloat16::lowest() : BFloat16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy<Float8_E4M3> values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float8_E4M3 value. +// The number is rounded towards zero. +// If leading_sign is Plus or Minus, then the number may not have a leading +// minus or plus. If it successfully parses, and the leading sign was Minus, +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& ParseNormalFloat<FloatProxy<Float8_E4M3>, + HexFloatTraits<FloatProxy<Float8_E4M3>>>( + std::istream& is, LeadingSign leading_sign, + HexFloat<FloatProxy<Float8_E4M3>, HexFloatTraits<FloatProxy<Float8_E4M3>>>& + value) { + // First parse as a 32-bit float. + HexFloat<FloatProxy<float>> float_val(0.0f); + ParseNormalFloat(is, leading_sign, float_val); + + if (float_val.value().getAsFloat() > 448.0f) { + is.setstate(std::ios_base::failbit); + value.set_value(Float8_E4M3::max()); + return is; + } else if (float_val.value().getAsFloat() < -448.0f) { + is.setstate(std::ios_base::failbit); + value.set_value(0x80 | Float8_E4M3::max().get_value()); + return is; + } + // Then convert to E4M3 float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, round_direction::kToZero); + + return is; +} +// Specialization of ParseNormalFloat for FloatProxy<Float8_E5M2> values. +// This will parse the float as it were a Float8_E5M2 floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If leading_sign is Plus or Minus, then the number may not have a leading +// minus or plus. If it successfully parses, and the leading sign was Minus, +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& ParseNormalFloat<FloatProxy<Float8_E5M2>, + HexFloatTraits<FloatProxy<Float8_E5M2>>>( + std::istream& is, LeadingSign leading_sign, + HexFloat<FloatProxy<Float8_E5M2>, HexFloatTraits<FloatProxy<Float8_E5M2>>>& + value) { + // First parse as a 32-bit float. + HexFloat<FloatProxy<float>> float_val(0.0f); + ParseNormalFloat(is, leading_sign, float_val); + + // Then convert to Float8_E5M2 float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, round_direction::kToZero); + + // Overflow on Float8_E5M2 behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float8_E5M2::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float8_E5M2::lowest() + : Float8_E5M2::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + namespace detail { // Returns a new value formed from 'value' by setting 'bit' that is the @@ -972,14 +1277,19 @@ } auto next_char = is.peek(); - bool negate_value = false; - if (next_char != '-' && next_char != '0') { - return ParseNormalFloat(is, negate_value, value); + auto leading_sign = LeadingSign::None; + + if (next_char != '-' && next_char != '0' && next_char != '+') { + return ParseNormalFloat(is, LeadingSign::None, value); } if (next_char == '-') { - negate_value = true; + leading_sign = LeadingSign::Minus; + is.get(); + next_char = is.peek(); + } else if (next_char == '+') { + leading_sign = LeadingSign::Plus; is.get(); next_char = is.peek(); } @@ -989,12 +1299,12 @@ auto maybe_hex_start = is.peek(); if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { is.unget(); - return ParseNormalFloat(is, negate_value, value); + return ParseNormalFloat(is, leading_sign, value); } else { is.get(); // Throw away the 'x'; } } else { - return ParseNormalFloat(is, negate_value, value); + return ParseNormalFloat(is, leading_sign, value); } // This "looks" like a hex-float so treat it as one. @@ -1210,7 +1520,8 @@ } uint_type output_bits = static_cast<uint_type>( - static_cast<uint_type>(negate_value ? 1 : 0) << HF::top_bit_left_shift); + static_cast<uint_type>(leading_sign == LeadingSign::Minus ? 1 : 0) + << HF::top_bit_left_shift); output_bits |= fraction; uint_type shifted_exponent = static_cast<uint_type>( @@ -1253,6 +1564,27 @@ return os; } +template <> +inline std::ostream& operator<< <BFloat16>(std::ostream& os, + const FloatProxy<BFloat16>& value) { + os << HexFloat<FloatProxy<BFloat16>>(value); + return os; +} + +template <> +inline std::ostream& operator<< <Float8_E4M3>( + std::ostream& os, const FloatProxy<Float8_E4M3>& value) { + os << HexFloat<FloatProxy<Float8_E4M3>>(value); + return os; +} + +template <> +inline std::ostream& operator<< <Float8_E5M2>( + std::ostream& os, const FloatProxy<Float8_E5M2>& value) { + os << HexFloat<FloatProxy<Float8_E5M2>>(value); + return os; +} + } // namespace utils } // namespace spvtools
diff --git a/source/util/index_range.h b/source/util/index_range.h new file mode 100644 index 0000000..d256bde --- /dev/null +++ b/source/util/index_range.h
@@ -0,0 +1,71 @@ +// Copyright 2025 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_UTIL_INDEX_RANGE_H_ +#define SOURCE_UTIL_INDEX_RANGE_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> + +#include "source/util/span.h" + +namespace spvtools { +namespace utils { + +// Implement a range of indicies, to index over an array of values of type T, +// but whose base pointer is supplied externally. Think of this as a span +// but without the base pointer, which is to be applied later. Parameterization +// by T makes usage more readable and less error-prone. +template <typename T, class IndexType = uint32_t, class CountType = IndexType> +class IndexRange { + public: + static_assert(std::is_integral<IndexType>::value); + static_assert(std::is_unsigned<IndexType>::value); + static_assert(std::is_integral<CountType>::value); + static_assert(std::is_unsigned<CountType>::value); + using value_type = T; + using index_type = IndexType; + using size_type = CountType; + + constexpr IndexRange() {} + constexpr IndexRange(index_type first, size_type count) + : first_(first), count_(count) {} + + size_type count() const { return count_; } + bool empty() const { return count() == size_type(0); } + + IndexType first() const { return first_; } + + // Returns the span of indexed elements using the given base pointer. + template <typename E> + spvtools::utils::Span<E> apply(E* base) const { + using span_type = spvtools::utils::Span<E>; + return base ? span_type(base + first_, count_) : span_type(); + } + template <typename E = int> + spvtools::utils::Span<E> apply(std::nullptr_t) const { + using span_type = spvtools::utils::Span<E>; + return span_type(); + } + + private: + index_type first_ = 0; + size_type count_ = 0; +}; + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_INDEX_RANGE_H_
diff --git a/source/util/parse_number.cpp b/source/util/parse_number.cpp index c3351c2..117e6f2 100644 --- a/source/util/parse_number.cpp +++ b/source/util/parse_number.cpp
@@ -131,6 +131,19 @@ return EncodeNumberStatus::kSuccess; } +spv_fp_encoding_t DeduceEncoding(const NumberType& type) { + if (type.encoding != SPV_FP_ENCODING_UNKNOWN) return type.encoding; + switch (type.bitwidth) { + case 16: + return SPV_FP_ENCODING_IEEE754_BINARY16; + case 32: + return SPV_FP_ENCODING_IEEE754_BINARY32; + case 64: + return SPV_FP_ENCODING_IEEE754_BINARY64; + default: + return SPV_FP_ENCODING_UNKNOWN; + } +} EncodeNumberStatus ParseAndEncodeFloatingPointNumber( const char* text, const NumberType& type, std::function<void(uint32_t)> emit, std::string* error_msg) { @@ -145,8 +158,43 @@ } const auto bit_width = AssumedBitWidth(type); - switch (bit_width) { - case 16: { + switch (DeduceEncoding(type)) { + case SPV_FP_ENCODING_FLOAT8_E4M3: { + HexFloat<FloatProxy<Float8_E4M3>> hVal(0); + if (!ParseNumber(text, &hVal)) { + ErrorMsgStream(error_msg) << "Invalid E4M3 float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + // getAsFloat will return the Float16 value, and get_value + // will return a uint16_t representing the bits of the float. + // The encoding is therefore correct from the perspective of the SPIR-V + // spec since the top 16 bits will be 0. + emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value())); + return EncodeNumberStatus::kSuccess; + } break; + case SPV_FP_ENCODING_FLOAT8_E5M2: { + HexFloat<FloatProxy<Float8_E5M2>> hVal(0); + if (!ParseNumber(text, &hVal)) { + ErrorMsgStream(error_msg) << "Invalid E5M2 float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + // getAsFloat will return the Float16 value, and get_value + // will return a uint16_t representing the bits of the float. + // The encoding is therefore correct from the perspective of the SPIR-V + // spec since the top 16 bits will be 0. + emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value())); + return EncodeNumberStatus::kSuccess; + } break; + case SPV_FP_ENCODING_BFLOAT16: { + HexFloat<FloatProxy<BFloat16>> hVal(0); + if (!ParseNumber(text, &hVal)) { + ErrorMsgStream(error_msg) << "Invalid bfloat16 literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value())); + return EncodeNumberStatus::kSuccess; + } break; + case SPV_FP_ENCODING_IEEE754_BINARY16: { HexFloat<FloatProxy<Float16>> hVal(0); if (!ParseNumber(text, &hVal)) { ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text; @@ -159,7 +207,7 @@ emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value())); return EncodeNumberStatus::kSuccess; } break; - case 32: { + case SPV_FP_ENCODING_IEEE754_BINARY32: { HexFloat<FloatProxy<float>> fVal(0.0f); if (!ParseNumber(text, &fVal)) { ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text; @@ -168,7 +216,7 @@ emit(BitwiseCast<uint32_t>(fVal)); return EncodeNumberStatus::kSuccess; } break; - case 64: { + case SPV_FP_ENCODING_IEEE754_BINARY64: { HexFloat<FloatProxy<double>> dVal(0.0); if (!ParseNumber(text, &dVal)) { ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text;
diff --git a/source/util/parse_number.h b/source/util/parse_number.h index d0f2a09..27a0327 100644 --- a/source/util/parse_number.h +++ b/source/util/parse_number.h
@@ -32,6 +32,7 @@ // SPV_NUMBER_NONE means the type is unknown and is invalid to be used with // ParseAndEncode{|Integer|Floating}Number(). spv_number_kind_t kind; + spv_fp_encoding_t encoding; }; // Returns true if the type is a scalar integer type. @@ -160,6 +161,14 @@ return true; } +template <typename T> +struct IsHexFloat { + static const bool value = false; +}; +template <typename T> +struct IsHexFloat<HexFloat<T>> { + static const bool value = true; +}; // Parses a numeric value of a given type from the given text. The number // should take up the entire string, and should be within bounds for the target // type. On success, returns true and populates the object referenced by @@ -169,8 +178,10 @@ // C++11 doesn't define std::istringstream(int8_t&), so calling this method // with a single-byte type leads to implementation-defined behaviour. // Similarly for uint8_t. - static_assert(sizeof(T) > 1, - "Single-byte types are not supported in this parse method"); + // HexFloat<T> overloads the operator + static_assert(sizeof(T) > 1 || IsHexFloat<T>::value, + "Single-byte types other than HexFloat<> are not supported in " + "this parse method"); if (!text) return false; std::istringstream text_stream(text);
diff --git a/source/util/small_vector.h b/source/util/small_vector.h index 1351475..c15ede1 100644 --- a/source/util/small_vector.h +++ b/source/util/small_vector.h
@@ -43,6 +43,7 @@ template <class T, size_t small_size> class SmallVector { public: + using value_type = T; using iterator = T*; using const_iterator = const T*; @@ -183,7 +184,8 @@ } // Avoid infinite recursion from rewritten operators in C++20 -#if __cplusplus <= 201703L +#if (defined(_MSVC_LANG) && _MSVC_LANG <= 201703L) || \ + (!defined(_MSVC_LANG) && __cplusplus <= 201703L) friend bool operator==(const std::vector<T>& lhs, const SmallVector& rhs) { return rhs == lhs; }
diff --git a/source/util/span.h b/source/util/span.h new file mode 100644 index 0000000..1e29017 --- /dev/null +++ b/source/util/span.h
@@ -0,0 +1,72 @@ +// Copyright 2025 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_UTIL_SPAN_H_ +#define SOURCE_UTIL_SPAN_H_ + +#include <cstddef> +#include <iterator> +#include <type_traits> + +namespace spvtools { +namespace utils { + +// Implement a subset of the C++20 std::span, using at most C++17 functionality. +// Replace this when SPIRV-Tools can use C++20. +template <class T> +class Span { + public: + using element_type = T; + using value_type = std::remove_cv_t<T>; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = T*; + using const_iterator = const T*; + + Span() {} + Span(iterator first, size_type count) : first_(first), count_(count) {} + + iterator begin() const { return first_; } + iterator end() const { return first_ ? first_ + count_ : nullptr; } + const_iterator cbegin() const { return first_; } + const_iterator cend() const { return first_ ? first_ + count_ : nullptr; } + + size_type size() const { return count_; } + size_type size_bytes() const { return count_ * sizeof(T); } + bool empty() const { return first_ == nullptr || count_ == 0; } + + reference front() const { return *first_; } + reference back() const { return *(first_ + count_ - 1); } + pointer data() const { return first_; } + reference operator[](size_type idx) const { return first_[idx]; } + Span<T> subspan(size_type offset) const { + if (count_ > offset) { + return Span(first_ + offset, count_ - offset); + } + return Span<T>(); + } + + private: + T* first_ = nullptr; + size_type count_ = 0; +}; + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_SPAN_H_
diff --git a/source/enum_string_mapping.cpp b/source/util/status.h similarity index 66% rename from source/enum_string_mapping.cpp rename to source/util/status.h index 32361a0..3799d38 100644 --- a/source/enum_string_mapping.cpp +++ b/source/util/status.h
@@ -1,4 +1,4 @@ -// Copyright (c) 2017 Google Inc. +// Copyright (c) 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,18 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/enum_string_mapping.h" - -#include <algorithm> -#include <cassert> -#include <cstring> -#include <string> -#include <unordered_map> - -#include "source/extensions.h" +#ifndef SOURCE_UTIL_STATUS_H_ +#define SOURCE_UTIL_STATUS_H_ namespace spvtools { +namespace utils { -#include "enum_string_mapping.inc" +// The result of processing a module. +enum class Status { + Failure = 0x0, + SuccessWithChange = 0x10, + SuccessWithoutChange = 0x11 +}; +} // namespace utils } // namespace spvtools + +#endif // SOURCE_UTIL_STATUS_H_
diff --git a/source/util/string_utils.h b/source/util/string_utils.h index 03e20b3..590f8cf 100644 --- a/source/util/string_utils.h +++ b/source/util/string_utils.h
@@ -17,13 +17,12 @@ #include <assert.h> +#include <cstdint> #include <cstring> #include <sstream> #include <string> #include <vector> -#include "source/util/string_utils.h" - namespace spvtools { namespace utils { @@ -48,8 +47,9 @@ // Encodes a string as a sequence of words, using the SPIR-V encoding, appending // to an existing vector. -inline void AppendToVector(const std::string& input, - std::vector<uint32_t>* result) { +template <class VectorType = std::vector<uint32_t>> +inline void AppendToVector(const std::string& input, VectorType* result) { + static_assert(std::is_same<uint32_t, typename VectorType::value_type>::value); uint32_t word = 0; size_t num_bytes = input.size(); // SPIR-V strings are null-terminated. The byte_index == num_bytes @@ -70,8 +70,10 @@ } // Encodes a string as a sequence of words, using the SPIR-V encoding. -inline std::vector<uint32_t> MakeVector(const std::string& input) { - std::vector<uint32_t> result; +template <class VectorType = std::vector<uint32_t>> +inline VectorType MakeVector(const std::string& input) { + static_assert(std::is_same<uint32_t, typename VectorType::value_type>::value); + VectorType result; AppendToVector(input, &result); return result; }
diff --git a/source/val/function.h b/source/val/function.h index c4cd5a4..d3dd639 100644 --- a/source/val/function.h +++ b/source/val/function.h
@@ -20,6 +20,7 @@ #include <map> #include <set> #include <string> +#include <type_traits> #include <unordered_map> #include <unordered_set> #include <utility>
diff --git a/source/val/instruction.h b/source/val/instruction.h index 59e8af1..6b42489 100644 --- a/source/val/instruction.h +++ b/source/val/instruction.h
@@ -24,6 +24,7 @@ #include "source/ext_inst.h" #include "source/opcode.h" #include "source/table.h" +#include "source/table2.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -83,9 +84,7 @@ const spv_parsed_instruction_t& c_inst() const { return inst_; } /// Provides direct access to instructions spv_ext_inst_type_t object. - const spv_ext_inst_type_t& ext_inst_type() const { - return inst_.ext_inst_type; - } + spv_ext_inst_type_t ext_inst_type() const { return inst_.ext_inst_type; } bool IsNonSemantic() const { return spvIsExtendedInstruction(opcode()) && @@ -113,7 +112,7 @@ private: const std::vector<uint32_t> words_; const std::vector<spv_parsed_operand_t> operands_; - spv_parsed_instruction_t inst_; + const spv_parsed_instruction_t inst_; size_t line_num_ = 0; /// The function in which this instruction was declared
diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 2d10347..41c7a91 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp
@@ -22,12 +22,12 @@ #include "source/binary.h" #include "source/diagnostic.h" -#include "source/enum_string_mapping.h" #include "source/extensions.h" #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_endian.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/val/construct.h" #include "source/val/instruction.h" #include "source/val/validation_state.h" @@ -64,9 +64,12 @@ spv_result_t ProcessExtensions(void* user_data, const spv_parsed_instruction_t* inst) { const spv::Op opcode = static_cast<spv::Op>(inst->opcode); - if (opcode == spv::Op::OpCapability) return SPV_SUCCESS; + if (opcode == spv::Op::OpCapability || + opcode == spv::Op::OpConditionalCapabilityINTEL) + return SPV_SUCCESS; - if (opcode == spv::Op::OpExtension) { + if (opcode == spv::Op::OpExtension || + opcode == spv::Op::OpConditionalExtensionINTEL) { ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data)); RegisterExtension(_, inst); return SPV_SUCCESS; @@ -115,10 +118,11 @@ _.ComputeFunctionToEntryPointMapping(); _.ComputeRecursiveEntryPoints(); - if (_.entry_points().empty() && !_.HasCapability(spv::Capability::Linkage)) { + if (_.entry_points().empty() && !_.HasCapability(spv::Capability::Linkage) && + !_.HasCapability(spv::Capability::GraphARM)) { return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) << "No OpEntryPoint instruction was found. This is only allowed if " - "the Linkage capability is being used."; + "the Linkage or GraphARM capability is being used."; } for (const auto& entry_point : _.entry_points()) { @@ -151,6 +155,16 @@ return SPV_SUCCESS; } +spv_result_t ValidateGraphEntryPoints(ValidationState_t& _) { + if (_.graph_entry_points().empty() && + _.HasCapability(spv::Capability::GraphARM)) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "No OpGraphEntryPointARM instruction was found but the GraphARM " + "capability is declared."; + } + return SPV_SUCCESS; +} + spv_result_t ValidateBinaryUsingContextAndValidationState( const spv_context_t& context, const uint32_t* words, const size_t num_words, spv_diagnostic* pDiagnostic, ValidationState_t* vstate) { @@ -217,43 +231,59 @@ // able to, briefly, de-const the instruction. Instruction* inst = const_cast<Instruction*>(&instruction); - if (inst->opcode() == spv::Op::OpEntryPoint) { - const auto entry_point = inst->GetOperandAs<uint32_t>(1); - const auto execution_model = inst->GetOperandAs<spv::ExecutionModel>(0); - const std::string desc_name = inst->GetOperandAs<std::string>(2); + if ((inst->opcode() == spv::Op::OpEntryPoint) || + (inst->opcode() == spv::Op::OpConditionalEntryPointINTEL)) { + const int i_model = inst->opcode() == spv::Op::OpEntryPoint ? 0 : 1; + const int i_point = inst->opcode() == spv::Op::OpEntryPoint ? 1 : 2; + const int i_name = inst->opcode() == spv::Op::OpEntryPoint ? 2 : 3; + const int min_num_operands = + inst->opcode() == spv::Op::OpEntryPoint ? 3 : 4; + + const auto entry_point = inst->GetOperandAs<uint32_t>(i_point); + const auto execution_model = + inst->GetOperandAs<spv::ExecutionModel>(i_model); + const std::string desc_name = inst->GetOperandAs<std::string>(i_name); ValidationState_t::EntryPointDescription desc; desc.name = desc_name; std::vector<uint32_t> interfaces; - for (size_t j = 3; j < inst->operands().size(); ++j) + for (size_t j = min_num_operands; j < inst->operands().size(); ++j) desc.interfaces.push_back(inst->word(inst->operand(j).offset)); vstate->RegisterEntryPoint(entry_point, execution_model, std::move(desc)); - if (visited_entry_points.size() > 0) { - for (const Instruction* check_inst : visited_entry_points) { - const auto check_execution_model = - check_inst->GetOperandAs<spv::ExecutionModel>(0); - const std::string check_name = - check_inst->GetOperandAs<std::string>(2); + if (inst->opcode() == spv::Op::OpEntryPoint) { + // conditional entry points are allowed to share the same name and + // exec mode + if (visited_entry_points.size() > 0) { + for (const Instruction* check_inst : visited_entry_points) { + const auto check_execution_model = + check_inst->GetOperandAs<spv::ExecutionModel>(i_model); + const std::string check_name = + check_inst->GetOperandAs<std::string>(i_name); - if (desc_name == check_name && - execution_model == check_execution_model) { - return vstate->diag(SPV_ERROR_INVALID_DATA, inst) - << "2 Entry points cannot share the same name and " - "ExecutionMode."; + if (desc_name == check_name && + execution_model == check_execution_model) { + return vstate->diag(SPV_ERROR_INVALID_DATA, inst) + << "2 Entry points cannot share the same name and " + "ExecutionMode."; + } } } + visited_entry_points.push_back(inst); } - visited_entry_points.push_back(inst); has_mask_task_nv |= (execution_model == spv::ExecutionModel::TaskNV || execution_model == spv::ExecutionModel::MeshNV); has_mask_task_ext |= (execution_model == spv::ExecutionModel::TaskEXT || execution_model == spv::ExecutionModel::MeshEXT); } + if (inst->opcode() == spv::Op::OpGraphEntryPointARM) { + const auto graph = inst->GetOperandAs<uint32_t>(1); + vstate->RegisterGraphEntryPoint(graph); + } if (inst->opcode() == spv::Op::OpFunctionCall) { if (!vstate->in_function_body()) { return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction) @@ -299,6 +329,10 @@ return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) << "Missing OpFunctionEnd at end of module."; + if (vstate->graph_definition_region() != kGraphDefinitionOutside) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing OpGraphEndARM at end of module."; + if (vstate->HasCapability(spv::Capability::BindlessTextureNV) && !vstate->has_samplerimage_variable_address_mode_specified()) return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) @@ -314,7 +348,7 @@ if (auto error = ValidateForwardDecls(*vstate)) return error; // Calculate reachability after all the blocks are parsed, but early that it - // can be relied on in subsequent pases. + // can be relied on in subsequent passes. ReachabilityPass(*vstate); // ID usage needs be handled in its own iteration of the instructions, @@ -356,17 +390,22 @@ if (auto error = AtomicsPass(*vstate, &instruction)) return error; if (auto error = PrimitivesPass(*vstate, &instruction)) return error; if (auto error = BarriersPass(*vstate, &instruction)) return error; - // Group + if (auto error = DotProductPass(*vstate, &instruction)) return error; + if (auto error = GroupPass(*vstate, &instruction)) return error; // Device-Side Enqueue - // Pipe + if (auto error = PipePass(*vstate, &instruction)) return error; if (auto error = NonUniformPass(*vstate, &instruction)) return error; if (auto error = LiteralsPass(*vstate, &instruction)) return error; if (auto error = RayQueryPass(*vstate, &instruction)) return error; if (auto error = RayTracingPass(*vstate, &instruction)) return error; if (auto error = RayReorderNVPass(*vstate, &instruction)) return error; + if (auto error = RayReorderEXTPass(*vstate, &instruction)) return error; if (auto error = MeshShadingPass(*vstate, &instruction)) return error; if (auto error = TensorLayoutPass(*vstate, &instruction)) return error; + if (auto error = TensorPass(*vstate, &instruction)) return error; + if (auto error = GraphPass(*vstate, &instruction)) return error; + if (auto error = InvalidTypePass(*vstate, &instruction)) return error; } // Validate the preconditions involving adjacent instructions. e.g. @@ -375,6 +414,7 @@ if (auto error = ValidateAdjacency(*vstate)) return error; if (auto error = ValidateEntryPoints(*vstate)) return error; + if (auto error = ValidateGraphEntryPoints(*vstate)) return error; // CFG checks are performed after the binary has been parsed // and the CFGPass has collected information about the control flow if (auto error = PerformCfgChecks(*vstate)) return error; @@ -392,6 +432,7 @@ if (auto error = ValidateQCOMImageProcessingTextureUsages(*vstate, &inst)) return error; } + if (auto error = ValidateLogicalPointers(*vstate)) return error; return SPV_SUCCESS; }
diff --git a/source/val/validate.h b/source/val/validate.h index 5514ff7..717fb34 100644 --- a/source/val/validate.h +++ b/source/val/validate.h
@@ -180,6 +180,12 @@ /// Validates correctness of barrier instructions. spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst); +/// Validates correctness of DotProduct instructions. +spv_result_t DotProductPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of Group (Kernel) instructions. +spv_result_t GroupPass(ValidationState_t& _, const Instruction* inst); + /// Validates correctness of literal numbers. spv_result_t LiteralsPass(ValidationState_t& _, const Instruction* inst); @@ -189,14 +195,17 @@ /// Validates correctness of annotation instructions. spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst); +/// Validates correctness of pipe instructions. +spv_result_t PipePass(ValidationState_t& _, const Instruction* inst); + /// Validates correctness of non-uniform group instructions. spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst); /// Validates correctness of debug instructions. spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst); -// Validates that capability declarations use operands allowed in the current -// context. +/// Validates that capability declarations use operands allowed in the current +/// context. spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst); /// Validates correctness of primitive instructions. @@ -220,9 +229,21 @@ /// Validates correctness of shader execution reorder instructions. spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst); +/// Validates correctness of shader execution reorder EXT instructions. +spv_result_t RayReorderEXTPass(ValidationState_t& _, const Instruction* inst); + /// Validates correctness of mesh shading instructions. spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst); +/// Validates correctness of tensor instructions. +spv_result_t TensorPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of graph instructions. +spv_result_t GraphPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of certain special type instructions. +spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst); + /// Calculates the reachability of basic blocks. void ReachabilityPass(ValidationState_t& _); @@ -250,6 +271,9 @@ spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _, const Instruction* inst); +/// Validates logical pointer restrictions. +spv_result_t ValidateLogicalPointers(ValidationState_t& _); + /// @brief Validate the ID's within a SPIR-V binary /// /// @param[in] pInstructions array of instructions
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index cf6f96b..b8ac6cb 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -30,6 +32,15 @@ case spv::Decoration::AlignmentId: case spv::Decoration::MaxByteOffsetId: case spv::Decoration::HlslCounterBufferGOOGLE: + case spv::Decoration::NodeMaxPayloadsAMDX: + case spv::Decoration::NodeSharesPayloadLimitsWithAMDX: + case spv::Decoration::PayloadNodeArraySizeAMDX: + case spv::Decoration::PayloadNodeNameAMDX: + case spv::Decoration::PayloadNodeBaseIndexAMDX: + case spv::Decoration::ArrayStrideIdEXT: + case spv::Decoration::OffsetIdEXT: + case spv::Decoration::AliasScopeINTEL: + case spv::Decoration::NoAliasINTEL: return true; default: break; @@ -58,6 +69,7 @@ case spv::Decoration::Block: case spv::Decoration::BufferBlock: case spv::Decoration::ArrayStride: + case spv::Decoration::ArrayStrideIdEXT: case spv::Decoration::GLSLShared: case spv::Decoration::GLSLPacked: case spv::Decoration::CPacked: @@ -107,8 +119,12 @@ }; switch (dec) { case spv::Decoration::SpecId: - if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { - return fail(0) << "must be a scalar specialization constant"; + if (target->opcode() != spv::Op::OpSpecConstantTrue && + target->opcode() != spv::Op::OpSpecConstantFalse && + target->opcode() != spv::Op::OpSpecConstant && + target->opcode() != spv::Op::OpSpecConstantDataKHR) { + return fail(0) << "must be OpSpecConstantTrue, OpSpecConstantFalse, " + "OpSpecConstant, or OpSpecConstantDataKHR"; } break; case spv::Decoration::Block: @@ -163,10 +179,12 @@ case spv::Decoration::Stream: case spv::Decoration::RestrictPointer: case spv::Decoration::AliasedPointer: + case spv::Decoration::PerPrimitiveEXT: if (target->opcode() != spv::Op::OpVariable && target->opcode() != spv::Op::OpUntypedVariableKHR && target->opcode() != spv::Op::OpFunctionParameter && - target->opcode() != spv::Op::OpRawAccessChainNV) { + target->opcode() != spv::Op::OpRawAccessChainNV && + target->opcode() != spv::Op::OpBufferPointerEXT) { return fail(0) << "must be a memory object declaration"; } if (!_.IsPointerType(target->type_id())) { @@ -209,6 +227,7 @@ sc != spv::StorageClass::IncomingCallableDataKHR && sc != spv::StorageClass::ShaderRecordBufferKHR && sc != spv::StorageClass::HitObjectAttributeNV && + sc != spv::StorageClass::HitObjectAttributeEXT && sc != spv::StorageClass::TileImageEXT) { return _.diag(SPV_ERROR_INVALID_ID, target) << _.VkErrorID(6672) << _.SpvDecorationString(dec) @@ -225,7 +244,8 @@ case spv::Decoration::DescriptorSet: if (sc != spv::StorageClass::StorageBuffer && sc != spv::StorageClass::Uniform && - sc != spv::StorageClass::UniformConstant) { + sc != spv::StorageClass::UniformConstant && + sc != spv::StorageClass::TileAttachmentQCOM) { return fail(6491) << "must be in the StorageBuffer, Uniform, or " "UniformConstant storage class"; } @@ -324,6 +344,14 @@ } spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { + const auto target_id = inst->GetOperandAs<uint32_t>(0); + const auto target = _.FindDef(target_id); + if (target && spv::Op::OpDecorationGroup == target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberDecorate Target <id> " << _.getIdName(target_id) + << " must not be an OpDecorationGroup instruction."; + } + const auto decoration = inst->GetOperandAs<spv::Decoration>(1); if (!DecorationTakesIdParameters(decoration)) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -331,6 +359,56 @@ "OpDecorateId"; } + if (decoration == spv::Decoration::ArrayStrideIdEXT) { + if (target->opcode() != spv::Op::OpTypeArray && + target->opcode() != spv::Op::OpTypeRuntimeArray) { + // ArrayStrideIdEXT is suppose to identical to ArrayStride, which would + // allow it to be a OpTypePointer/OpTypeUntypedPointerKHR + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArrayStrideIdEXT decoration must only be applied to array " + "types."; + } else { + const uint32_t operand_id = inst->GetOperandAs<uint32_t>(2); + if (!_.IsIntScalarType(_.GetTypeId(operand_id), 32)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArrayStrideIdEXT extra operand must be a 32-bit int " + "scalar type."; + } + + // Even if spec constant, validation layers will test when frozen + uint64_t stride_value = 0; + if (_.EvalConstantValUint64(operand_id, &stride_value)) { + if (stride_value == 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArrayStrideIdEXT contains a stride of zero."; + } + } + + // Strip array and should be the descriptor type + const uint32_t element_type = + _.FindDef(target_id)->GetOperandAs<uint32_t>(1); + if (!_.IsDescriptorType(element_type)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArrayStrideIdEXT decoration must only be applied to" + << " array type containing a Descriptor type."; + } + } + } + + for (uint32_t i = 2; i < inst->operands().size(); ++i) { + const auto param_id = inst->GetOperandAs<uint32_t>(i); + const auto param = _.FindDef(param_id); + + // Both target and param are elements of ordered_instructions we can + // determine their relative positions in the SPIR-V module by comparing + // pointers. + if (target <= param) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Parameter <ID> " << _.getIdName(param_id) + << " must appear earlier in the binary than the target"; + } + } + // No member decorations take id parameters, so we don't bother checking if // we are using a member only decoration here. @@ -343,24 +421,70 @@ const Instruction* inst) { const auto struct_type_id = inst->GetOperandAs<uint32_t>(0); const auto struct_type = _.FindDef(struct_type_id); + const bool is_mem_dec_id_inst = + (inst->opcode() == spv::Op::OpMemberDecorateIdEXT); if (!struct_type || spv::Op::OpTypeStruct != struct_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpMemberDecorate Structure type <id> " - << _.getIdName(struct_type_id) << " is not a struct type."; + << (is_mem_dec_id_inst ? "OpMemberDecorateIdEXT" + : "OpMemberDecorate") + << " Structure type <id> " << _.getIdName(struct_type_id) + << " is not a struct type."; } const auto member = inst->GetOperandAs<uint32_t>(1); const auto member_count = static_cast<uint32_t>(struct_type->words().size() - 2); if (member_count <= member) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Index " << member - << " provided in OpMemberDecorate for struct <id> " - << _.getIdName(struct_type_id) + << "Index " << member << " provided in " + << (is_mem_dec_id_inst ? "OpMemberDecorateIdEXT" + : "OpMemberDecorate") + << " for struct <id> " << _.getIdName(struct_type_id) << " is out of bounds. The structure has " << member_count << " members. Largest valid index is " << member_count - 1 << "."; } const auto decoration = inst->GetOperandAs<spv::Decoration>(2); + if (is_mem_dec_id_inst) { + if (decoration != spv::Decoration::OffsetIdEXT) { + if (decoration == spv::Decoration::ArrayStrideIdEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArrayStrideIdEXT could only be directly applied" + << " to array type using OpDecorateId."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Decoration operand could only be OffsetIdEXT."; + } + } + + const auto is_descriptor_type = [&_](const Instruction* type_inst) { + return _.IsDescriptorType(type_inst->opcode()); + }; + + // recursively scans the struct to find if anything has a descriptor type, + // must be at least 1 + if (decoration == spv::Decoration::OffsetIdEXT) { + const uint32_t operand_id = inst->GetOperandAs<uint32_t>(3); + if (!_.IsIntScalarType(_.GetTypeId(operand_id), 32)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OffsetIdEXT extra operand must be a 32-bit int scalar type."; + } + if (!_.ContainsType(struct_type_id, is_descriptor_type, true)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OffsetIdEXT decoration in MemberDecorateIdEXT must only be " + "applied to members of structs where the struct contains " + "descriptor types."; + } + } + + for (uint32_t elem_idx = 3; elem_idx < inst->operands().size(); + elem_idx++) { + if (_.FindDef(inst->GetOperandAs<uint32_t>(elem_idx)) > struct_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "All <id> Extra Operands must appear before Structure Type."; + } + } + } + if (IsNotMemberDecoration(decoration)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << _.SpvDecorationString(decoration) @@ -379,8 +503,7 @@ if (use->opcode() != spv::Op::OpDecorate && use->opcode() != spv::Op::OpGroupDecorate && use->opcode() != spv::Op::OpGroupMemberDecorate && - use->opcode() != spv::Op::OpName && - use->opcode() != spv::Op::OpDecorateId && !use->IsNonSemantic()) { + use->opcode() != spv::Op::OpName && !use->IsNonSemantic()) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Result id of OpDecorationGroup can only " << "be targeted by OpName, OpGroupDecorate, " @@ -466,7 +589,8 @@ _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params)); break; } - case spv::Op::OpMemberDecorate: { + case spv::Op::OpMemberDecorate: + case spv::Op::OpMemberDecorateIdEXT: { const uint32_t struct_id = inst->word(1); const uint32_t index = inst->word(2); const spv::Decoration dec_type = @@ -537,6 +661,7 @@ // TODO(dneto): spv::Op::OpDecorateStringGOOGLE // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253 case spv::Op::OpMemberDecorate: + case spv::Op::OpMemberDecorateIdEXT: if (auto error = ValidateMemberDecorate(_, inst)) return error; break; case spv::Op::OpDecorationGroup:
diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp index 8b0049c..03697c6 100644 --- a/source/val/validate_arithmetics.cpp +++ b/source/val/validate_arithmetics.cpp
@@ -24,776 +24,895 @@ namespace spvtools { namespace val { -// Validates correctness of arithmetic instructions. -spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { +spv_result_t ValidateFloat(ValidationState_t& _, const Instruction* inst, + uint32_t starting_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + bool supportsCoopMat = + (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem && + opcode != spv::Op::OpFMod); + bool supportsCoopVec = + (opcode != spv::Op::OpFRem && opcode != spv::Op::OpFMod); + if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && + !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type)) && + !(opcode == spv::Op::OpFMul && + _.IsCooperativeMatrixKHRType(result_type) && + _.IsFloatCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected floating scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + for (size_t operand_index = starting_index; + operand_index < inst->operands().size(); ++operand_index) { + if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeVectorNVType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } else if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeMatrixKHRType(type_id) || + !_.IsFloatCooperativeMatrixType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); + if (ret != SPV_SUCCESS) return ret; + } else if (_.GetOperandTypeId(inst, operand_index) != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateUnsignedInt(ValidationState_t& _, const Instruction* inst, + uint32_t starting_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + bool supportsCoopMat = (opcode == spv::Op::OpUDiv); + bool supportsCoopVec = (opcode == spv::Op::OpUDiv); + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !(supportsCoopMat && _.IsUnsignedIntCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsUnsignedIntCooperativeVectorNVType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + for (size_t operand_index = starting_index; + operand_index < inst->operands().size(); ++operand_index) { + if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeVectorNVType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } else if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeMatrixKHRType(type_id) || + !_.IsUnsignedIntCooperativeMatrixType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); + if (ret != SPV_SUCCESS) return ret; + } else if (_.GetOperandTypeId(inst, operand_index) != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateSignedInt(ValidationState_t& _, const Instruction* inst, + uint32_t starting_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + bool supportsCoopMat = + (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem && + opcode != spv::Op::OpSMod); + bool supportsCoopVec = + (opcode != spv::Op::OpSRem && opcode != spv::Op::OpSMod); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) && + !(opcode == spv::Op::OpIMul && + _.IsCooperativeMatrixKHRType(result_type) && + _.IsIntCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t dimension = _.GetDimension(result_type); + const uint32_t bit_width = _.GetBitWidth(result_type); + + for (size_t operand_index = starting_index; + operand_index < inst->operands().size(); ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + + if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) { + if (!_.IsCooperativeVectorNVType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } + + if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { + if (!_.IsCooperativeMatrixKHRType(type_id) || + !_.IsIntCooperativeMatrixType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + } + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); + if (ret != SPV_SUCCESS) return ret; + } + + if (!type_id || + (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && + !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) && + !(opcode == spv::Op::OpIMul && + _.IsCooperativeMatrixKHRType(result_type) && + _.IsIntCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type)))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as operand: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + + if (_.GetDimension(type_id) != dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + if (_.GetBitWidth(type_id) != bit_width) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateDot(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar type as Result Type: " + << spvOpcodeString(opcode); + + if (_.IsBfloat16ScalarType(result_type)) { + if (!_.HasCapability(spv::Capability::BFloat16DotProductKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpDot Result Type <id> " << _.getIdName(result_type) + << "requires BFloat16DotProductKHR be declared."; + } + } + + uint32_t first_vector_num_components = 0; + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + + if (!type_id || !_.IsFloatVectorType(type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector as operand: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + const uint32_t component_type = _.GetComponentType(type_id); + if (component_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component type to be equal to Result Type: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + + const uint32_t num_components = _.GetDimension(type_id); + if (operand_index == 2) { + first_vector_num_components = num_components; + } else if (num_components != first_vector_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same number of components: " + << spvOpcodeString(opcode); + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorTimesScalar(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); + if (result_type != vector_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector operand type to be equal to Result Type: " + << spvOpcodeString(opcode); + + const uint32_t component_type = _.GetComponentType(vector_type_id); + + const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); + if (component_type != scalar_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar operand type to be equal to the component " + << "type of the vector operand: " << spvOpcodeString(opcode); + + return SPV_SUCCESS; +} + +spv_result_t ValidateMatrixTimesScalar(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatMatrixType(result_type) && + !(_.IsCooperativeMatrixType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); + if (result_type != matrix_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected matrix operand type to be equal to Result Type: " + << spvOpcodeString(opcode); + + const uint32_t component_type = _.GetComponentType(matrix_type_id); + + const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); + if (component_type != scalar_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar operand type to be equal to the component " + << "type of the matrix operand: " << spvOpcodeString(opcode); + + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorTimesMatrix(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3); + + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t res_component_type = _.GetComponentType(result_type); + + if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as left operand: " + << spvOpcodeString(opcode); + + if (res_component_type != _.GetComponentType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and vector to be " + << "equal: " << spvOpcodeString(opcode); + + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, &matrix_num_cols, + &matrix_col_type, &matrix_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as right operand: " + << spvOpcodeString(opcode); + + if (res_component_type != matrix_component_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and matrix to be " + << "equal: " << spvOpcodeString(opcode); + + if (matrix_num_cols != _.GetDimension(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to " + << "Result Type vector size: " << spvOpcodeString(opcode); + + if (matrix_num_rows != _.GetDimension(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of rows of the matrix to be equal to the " + << "vector operand size: " << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateMatrixTimesVector(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3); + + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, &matrix_num_cols, + &matrix_col_type, &matrix_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as left operand: " + << spvOpcodeString(opcode); + + if (result_type != matrix_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column type of the matrix to be equal to Result " + "Type: " + << spvOpcodeString(opcode); + + if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as right operand: " + << spvOpcodeString(opcode); + + if (matrix_component_type != _.GetComponentType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of the operands to be equal: " + << spvOpcodeString(opcode); + + if (matrix_num_cols != _.GetDimension(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to the " + << "vector size: " << spvOpcodeString(opcode); + + return SPV_SUCCESS; +} + +spv_result_t ValidateMatrixTimesMatrix(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t left_num_rows = 0; + uint32_t left_num_cols = 0; + uint32_t left_col_type = 0; + uint32_t left_component_type = 0; + if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols, + &left_col_type, &left_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as left operand: " + << spvOpcodeString(opcode); + + uint32_t right_num_rows = 0; + uint32_t right_num_cols = 0; + uint32_t right_col_type = 0; + uint32_t right_component_type = 0; + if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols, + &right_col_type, &right_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as right operand: " + << spvOpcodeString(opcode); + + if (!_.IsFloatScalarType(res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + if (res_col_type != left_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column types of Result Type and left matrix to be " + << "equal: " << spvOpcodeString(opcode); + + if (res_component_type != right_component_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and right matrix to " + "be " + << "equal: " << spvOpcodeString(opcode); + + if (res_num_cols != right_num_cols) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of Result Type and right matrix " + "to " + << "be equal: " << spvOpcodeString(opcode); + + if (left_num_cols != right_num_rows) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of left matrix and number of " + "rows " + << "of right matrix to be equal: " << spvOpcodeString(opcode); + + assert(left_num_rows == res_num_rows); + return SPV_SUCCESS; +} + +spv_result_t ValidateOuterProduct(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + if (left_type_id != res_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column type of Result Type to be equal to the type " + << "of the left operand: " << spvOpcodeString(opcode); + + if (!right_type_id || !_.IsFloatVectorType(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as right operand: " + << spvOpcodeString(opcode); + + if (res_component_type != _.GetComponentType(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of the operands to be equal: " + << spvOpcodeString(opcode); + + if (res_num_cols != _.GetDimension(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to the " + << "vector size of the right operand: " << spvOpcodeString(opcode); + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtendedCarry(ValidationState_t& _, + const Instruction* inst) { const spv::Op opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); - switch (opcode) { + std::vector<uint32_t> result_types; + if (!_.GetStructMemberTypes(result_type, &result_types)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected a struct as Result Type: " << spvOpcodeString(opcode); + + if (result_types.size() != 2) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct to have two members: " + << spvOpcodeString(opcode); + + if (opcode == spv::Op::OpSMulExtended) { + if (!_.IsIntScalarType(result_types[0]) && + !_.IsIntVectorType(result_types[0])) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be integer " + "scalar " + << "or vector: " << spvOpcodeString(opcode); + } else { + if (!_.IsUnsignedIntScalarType(result_types[0]) && + !_.IsUnsignedIntVectorType(result_types[0])) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be unsigned " + << "integer scalar or vector: " << spvOpcodeString(opcode); + } + + if (result_types[0] != result_types[1]) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be identical: " + << spvOpcodeString(opcode); + + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + if (left_type_id != result_types[0] || right_type_id != result_types[0]) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to be of Result Type member type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixMulAddNV(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); + const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); + const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); + + if (!_.IsCooperativeMatrixNVType(A_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as A Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixNVType(B_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as B Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixNVType(C_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as C Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixNVType(D_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + const auto A = _.FindDef(A_type_id); + const auto B = _.FindDef(B_type_id); + const auto C = _.FindDef(C_type_id); + const auto D = _.FindDef(D_type_id); + + std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, A_rows, + B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; + + A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2)); + B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2)); + C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2)); + D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2)); + + A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3)); + B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3)); + C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3)); + D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3)); + + A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4)); + B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4)); + C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4)); + D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4)); + + const auto notEqual = [](std::tuple<bool, bool, uint32_t> X, + std::tuple<bool, bool, uint32_t> Y) { + return (std::get<1>(X) && std::get<1>(Y) && + std::get<2>(X) != std::get<2>(Y)); + }; + + if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || + notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || + notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix scopes must match: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || + notEqual(C_rows, D_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'M' mismatch: " << spvOpcodeString(opcode); + } + + if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || + notEqual(C_cols, D_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'N' mismatch: " << spvOpcodeString(opcode); + } + + if (notEqual(A_cols, B_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'K' mismatch: " << spvOpcodeString(opcode); + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixMulAddKHR(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); + const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); + const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); + + if (!_.IsCooperativeMatrixAType(A_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix type must be A Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixBType(B_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix type must be B Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixAccType(C_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix type must be Accumulator Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixKHRType(D_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + const auto A = _.FindDef(A_type_id); + const auto B = _.FindDef(B_type_id); + const auto C = _.FindDef(C_type_id); + const auto D = _.FindDef(D_type_id); + + std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, A_rows, + B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; + + A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2)); + B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2)); + C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2)); + D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2)); + + A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3)); + B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3)); + C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3)); + D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3)); + + A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4)); + B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4)); + C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4)); + D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4)); + + const auto notEqual = [](std::tuple<bool, bool, uint32_t> X, + std::tuple<bool, bool, uint32_t> Y) { + return (std::get<1>(X) && std::get<1>(Y) && + std::get<2>(X) != std::get<2>(Y)); + }; + + if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || + notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || + notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix scopes must match: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || + notEqual(C_rows, D_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'M' mismatch: " << spvOpcodeString(opcode); + } + + if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || + notEqual(C_cols, D_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'N' mismatch: " << spvOpcodeString(opcode); + } + + if (notEqual(A_cols, B_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'K' mismatch: " << spvOpcodeString(opcode); + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixReduceNV(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsCooperativeMatrixKHRType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be a cooperative matrix type: " + << spvOpcodeString(opcode); + } + + const auto result_comp_type_id = + _.FindDef(result_type)->GetOperandAs<uint32_t>(1); + + const auto matrix_id = inst->GetOperandAs<uint32_t>(2); + const auto matrix = _.FindDef(matrix_id); + const auto matrix_type_id = matrix->type_id(); + if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Matrix must have a cooperative matrix type: " + << spvOpcodeString(opcode); + } + const auto matrix_type = _.FindDef(matrix_type_id); + const auto matrix_comp_type_id = matrix_type->GetOperandAs<uint32_t>(1); + if (matrix_comp_type_id != result_comp_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type and Matrix type must have the same component " + "type: " + << spvOpcodeString(opcode); + } + if (_.FindDef(result_type)->GetOperandAs<uint32_t>(2) != + matrix_type->GetOperandAs<uint32_t>(2)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type and Matrix type must have the same scope: " + << spvOpcodeString(opcode); + } + + if (!_.IsCooperativeMatrixAccType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must have UseAccumulator: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixAccType(matrix_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Matrix type must have UseAccumulator: " + << spvOpcodeString(opcode); + } + + const auto reduce_value = inst->GetOperandAs<uint32_t>(3); + + if ((reduce_value & + uint32_t( + spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) && + (reduce_value & uint32_t(spv::CooperativeMatrixReduceMask::Row | + spv::CooperativeMatrixReduceMask::Column))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reduce 2x2 must not be used with Row/Column: " + << spvOpcodeString(opcode); + } + + std::tuple<bool, bool, uint32_t> result_rows, result_cols, matrix_rows, + matrix_cols; + result_rows = + _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(3)); + result_cols = + _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(4)); + matrix_rows = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(3)); + matrix_cols = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(4)); + + if (reduce_value & + uint32_t(spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) { + if (std::get<1>(result_rows) && std::get<1>(result_cols) && + std::get<1>(matrix_rows) && std::get<1>(matrix_cols) && + (std::get<2>(result_rows) != std::get<2>(matrix_rows) / 2 || + std::get<2>(result_cols) != std::get<2>(matrix_cols) / 2)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For Reduce2x2, result rows/cols must be half of matrix " + "rows/cols: " + << spvOpcodeString(opcode); + } + } + if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Row)) { + if (std::get<1>(result_rows) && std::get<1>(matrix_rows) && + std::get<2>(result_rows) != std::get<2>(matrix_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For ReduceRow, result rows must match matrix rows: " + << spvOpcodeString(opcode); + } + } + if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Column)) { + if (std::get<1>(result_cols) && std::get<1>(matrix_cols) && + std::get<2>(result_cols) != std::get<2>(matrix_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For ReduceColumn, result cols must match matrix cols: " + << spvOpcodeString(opcode); + } + } + + const auto combine_func_id = inst->GetOperandAs<uint32_t>(4); + const auto combine_func = _.FindDef(combine_func_id); + if (!combine_func || combine_func->opcode() != spv::Op::OpFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "CombineFunc must be a function: " << spvOpcodeString(opcode); + } + const auto function_type_id = combine_func->GetOperandAs<uint32_t>(3); + const auto function_type = _.FindDef(function_type_id); + if (function_type->operands().size() != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "CombineFunc must have two parameters: " + << spvOpcodeString(opcode); + } + for (uint32_t i = 0; i < 3; ++i) { + // checks return type and two params + const auto param_type_id = function_type->GetOperandAs<uint32_t>(i + 1); + if (param_type_id != matrix_comp_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "CombineFunc return type and parameters must match matrix " + "component type: " + << spvOpcodeString(opcode); + } + } + return SPV_SUCCESS; +} + +// Validates correctness of arithmetic instructions. +spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { case spv::Op::OpFAdd: case spv::Op::OpFSub: case spv::Op::OpFMul: case spv::Op::OpFDiv: case spv::Op::OpFRem: case spv::Op::OpFMod: - case spv::Op::OpFNegate: { - bool supportsCoopMat = - (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem && - opcode != spv::Op::OpFMod); - if (!_.IsFloatScalarType(result_type) && - !_.IsFloatVectorType(result_type) && - !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type)) && - !(opcode == spv::Op::OpFMul && - _.IsCooperativeMatrixKHRType(result_type) && - _.IsFloatCooperativeMatrixType(result_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected floating scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - for (size_t operand_index = 2; operand_index < inst->operands().size(); - ++operand_index) { - if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { - const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); - if (!_.IsCooperativeMatrixKHRType(type_id) || - !_.IsFloatCooperativeMatrixType(type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to be of Result Type: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - } - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); - if (ret != SPV_SUCCESS) return ret; - } else if (_.GetOperandTypeId(inst, operand_index) != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to be of Result Type: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - } - break; - } - + case spv::Op::OpFNegate: + case spv::Op::OpFmaKHR: + return ValidateFloat(_, inst); case spv::Op::OpUDiv: - case spv::Op::OpUMod: { - bool supportsCoopMat = (opcode == spv::Op::OpUDiv); - if (!_.IsUnsignedIntScalarType(result_type) && - !_.IsUnsignedIntVectorType(result_type) && - !(supportsCoopMat && - _.IsUnsignedIntCooperativeMatrixType(result_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected unsigned int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - for (size_t operand_index = 2; operand_index < inst->operands().size(); - ++operand_index) { - if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { - const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); - if (!_.IsCooperativeMatrixKHRType(type_id) || - !_.IsUnsignedIntCooperativeMatrixType(type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to be of Result Type: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - } - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); - if (ret != SPV_SUCCESS) return ret; - } else if (_.GetOperandTypeId(inst, operand_index) != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to be of Result Type: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - } - break; - } - + case spv::Op::OpUMod: + return ValidateUnsignedInt(_, inst); case spv::Op::OpISub: case spv::Op::OpIAdd: case spv::Op::OpIMul: case spv::Op::OpSDiv: case spv::Op::OpSMod: case spv::Op::OpSRem: - case spv::Op::OpSNegate: { - bool supportsCoopMat = - (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem && - opcode != spv::Op::OpSMod); - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && - !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) && - !(opcode == spv::Op::OpIMul && - _.IsCooperativeMatrixKHRType(result_type) && - _.IsIntCooperativeMatrixType(result_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t dimension = _.GetDimension(result_type); - const uint32_t bit_width = _.GetBitWidth(result_type); - - for (size_t operand_index = 2; operand_index < inst->operands().size(); - ++operand_index) { - const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); - - if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { - if (!_.IsCooperativeMatrixKHRType(type_id) || - !_.IsIntCooperativeMatrixType(type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to be of Result Type: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - } - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); - if (ret != SPV_SUCCESS) return ret; - } - - if (!type_id || - (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && - !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) && - !(opcode == spv::Op::OpIMul && - _.IsCooperativeMatrixKHRType(result_type) && - _.IsIntCooperativeMatrixType(result_type)))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as operand: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - - if (_.GetDimension(type_id) != dimension) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to have the same dimension " - << "as Result Type: " << spvOpcodeString(opcode) - << " operand index " << operand_index; - - if (_.GetBitWidth(type_id) != bit_width) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected arithmetic operands to have the same bit width " - << "as Result Type: " << spvOpcodeString(opcode) - << " operand index " << operand_index; - } - break; - } - - case spv::Op::OpDot: { - if (!_.IsFloatScalarType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float scalar type as Result Type: " - << spvOpcodeString(opcode); - - uint32_t first_vector_num_components = 0; - - for (size_t operand_index = 2; operand_index < inst->operands().size(); - ++operand_index) { - const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); - - if (!type_id || !_.IsFloatVectorType(type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector as operand: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - - const uint32_t component_type = _.GetComponentType(type_id); - if (component_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected component type to be equal to Result Type: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - - const uint32_t num_components = _.GetDimension(type_id); - if (operand_index == 2) { - first_vector_num_components = num_components; - } else if (num_components != first_vector_num_components) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to have the same number of components: " - << spvOpcodeString(opcode); - } - } - break; - } - - case spv::Op::OpVectorTimesScalar: { - if (!_.IsFloatVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); - if (result_type != vector_type_id) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected vector operand type to be equal to Result Type: " - << spvOpcodeString(opcode); - - const uint32_t component_type = _.GetComponentType(vector_type_id); - - const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); - if (component_type != scalar_type_id) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected scalar operand type to be equal to the component " - << "type of the vector operand: " << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpMatrixTimesScalar: { - if (!_.IsFloatMatrixType(result_type) && - !(_.IsCooperativeMatrixType(result_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); - if (result_type != matrix_type_id) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected matrix operand type to be equal to Result Type: " - << spvOpcodeString(opcode); - - const uint32_t component_type = _.GetComponentType(matrix_type_id); - - const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); - if (component_type != scalar_type_id) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected scalar operand type to be equal to the component " - << "type of the matrix operand: " << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpVectorTimesMatrix: { - const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3); - - if (!_.IsFloatVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t res_component_type = _.GetComponentType(result_type); - - if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector type as left operand: " - << spvOpcodeString(opcode); - - if (res_component_type != _.GetComponentType(vector_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected component types of Result Type and vector to be " - << "equal: " << spvOpcodeString(opcode); - - uint32_t matrix_num_rows = 0; - uint32_t matrix_num_cols = 0; - uint32_t matrix_col_type = 0; - uint32_t matrix_component_type = 0; - if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, - &matrix_num_cols, &matrix_col_type, - &matrix_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as right operand: " - << spvOpcodeString(opcode); - - if (res_component_type != matrix_component_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected component types of Result Type and matrix to be " - << "equal: " << spvOpcodeString(opcode); - - if (matrix_num_cols != _.GetDimension(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of columns of the matrix to be equal to " - << "Result Type vector size: " << spvOpcodeString(opcode); - - if (matrix_num_rows != _.GetDimension(vector_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of rows of the matrix to be equal to the " - << "vector operand size: " << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpMatrixTimesVector: { - const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3); - - if (!_.IsFloatVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector type as Result Type: " - << spvOpcodeString(opcode); - - uint32_t matrix_num_rows = 0; - uint32_t matrix_num_cols = 0; - uint32_t matrix_col_type = 0; - uint32_t matrix_component_type = 0; - if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, - &matrix_num_cols, &matrix_col_type, - &matrix_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as left operand: " - << spvOpcodeString(opcode); - - if (result_type != matrix_col_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected column type of the matrix to be equal to Result " - "Type: " - << spvOpcodeString(opcode); - - if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector type as right operand: " - << spvOpcodeString(opcode); - - if (matrix_component_type != _.GetComponentType(vector_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected component types of the operands to be equal: " - << spvOpcodeString(opcode); - - if (matrix_num_cols != _.GetDimension(vector_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of columns of the matrix to be equal to the " - << "vector size: " << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpMatrixTimesMatrix: { - const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); - - uint32_t res_num_rows = 0; - uint32_t res_num_cols = 0; - uint32_t res_col_type = 0; - uint32_t res_component_type = 0; - if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, - &res_col_type, &res_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as Result Type: " - << spvOpcodeString(opcode); - - uint32_t left_num_rows = 0; - uint32_t left_num_cols = 0; - uint32_t left_col_type = 0; - uint32_t left_component_type = 0; - if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols, - &left_col_type, &left_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as left operand: " - << spvOpcodeString(opcode); - - uint32_t right_num_rows = 0; - uint32_t right_num_cols = 0; - uint32_t right_col_type = 0; - uint32_t right_component_type = 0; - if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols, - &right_col_type, &right_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as right operand: " - << spvOpcodeString(opcode); - - if (!_.IsFloatScalarType(res_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as Result Type: " - << spvOpcodeString(opcode); - - if (res_col_type != left_col_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected column types of Result Type and left matrix to be " - << "equal: " << spvOpcodeString(opcode); - - if (res_component_type != right_component_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected component types of Result Type and right matrix to " - "be " - << "equal: " << spvOpcodeString(opcode); - - if (res_num_cols != right_num_cols) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of columns of Result Type and right matrix " - "to " - << "be equal: " << spvOpcodeString(opcode); - - if (left_num_cols != right_num_rows) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of columns of left matrix and number of " - "rows " - << "of right matrix to be equal: " << spvOpcodeString(opcode); - - assert(left_num_rows == res_num_rows); - break; - } - - case spv::Op::OpOuterProduct: { - const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); - - uint32_t res_num_rows = 0; - uint32_t res_num_cols = 0; - uint32_t res_col_type = 0; - uint32_t res_component_type = 0; - if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, - &res_col_type, &res_component_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float matrix type as Result Type: " - << spvOpcodeString(opcode); - - if (left_type_id != res_col_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected column type of Result Type to be equal to the type " - << "of the left operand: " << spvOpcodeString(opcode); - - if (!right_type_id || !_.IsFloatVectorType(right_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float vector type as right operand: " - << spvOpcodeString(opcode); - - if (res_component_type != _.GetComponentType(right_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected component types of the operands to be equal: " - << spvOpcodeString(opcode); - - if (res_num_cols != _.GetDimension(right_type_id)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of columns of the matrix to be equal to the " - << "vector size of the right operand: " - << spvOpcodeString(opcode); - - break; - } - + case spv::Op::OpSNegate: + return ValidateSignedInt(_, inst); + case spv::Op::OpDot: + return ValidateDot(_, inst); + case spv::Op::OpVectorTimesScalar: + return ValidateVectorTimesScalar(_, inst); + case spv::Op::OpMatrixTimesScalar: + return ValidateMatrixTimesScalar(_, inst); + case spv::Op::OpVectorTimesMatrix: + return ValidateVectorTimesMatrix(_, inst); + case spv::Op::OpMatrixTimesVector: + return ValidateMatrixTimesVector(_, inst); + case spv::Op::OpMatrixTimesMatrix: + return ValidateMatrixTimesMatrix(_, inst); + case spv::Op::OpOuterProduct: + return ValidateOuterProduct(_, inst); case spv::Op::OpIAddCarry: case spv::Op::OpISubBorrow: case spv::Op::OpUMulExtended: - case spv::Op::OpSMulExtended: { - std::vector<uint32_t> result_types; - if (!_.GetStructMemberTypes(result_type, &result_types)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected a struct as Result Type: " - << spvOpcodeString(opcode); + case spv::Op::OpSMulExtended: + return ValidateExtendedCarry(_, inst); + case spv::Op::OpCooperativeMatrixMulAddNV: + return ValidateCooperativeMatrixMulAddNV(_, inst); + case spv::Op::OpCooperativeMatrixMulAddKHR: + return ValidateCooperativeMatrixMulAddKHR(_, inst); + case spv::Op::OpCooperativeMatrixReduceNV: + return ValidateCooperativeMatrixReduceNV(_, inst); - if (result_types.size() != 2) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type struct to have two members: " - << spvOpcodeString(opcode); - - if (opcode == spv::Op::OpSMulExtended) { - if (!_.IsIntScalarType(result_types[0]) && - !_.IsIntVectorType(result_types[0])) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type struct member types to be integer " - "scalar " - << "or vector: " << spvOpcodeString(opcode); - } else { - if (!_.IsUnsignedIntScalarType(result_types[0]) && - !_.IsUnsignedIntVectorType(result_types[0])) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type struct member types to be unsigned " - << "integer scalar or vector: " << spvOpcodeString(opcode); - } - - if (result_types[0] != result_types[1]) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type struct member types to be identical: " - << spvOpcodeString(opcode); - - const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); - - if (left_type_id != result_types[0] || right_type_id != result_types[0]) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected both operands to be of Result Type member type: " - << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpCooperativeMatrixMulAddNV: { - const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); - const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); - const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); - - if (!_.IsCooperativeMatrixNVType(A_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix type as A Type: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixNVType(B_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix type as B Type: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixNVType(C_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix type as C Type: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixNVType(D_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix type as Result Type: " - << spvOpcodeString(opcode); - } - - const auto A = _.FindDef(A_type_id); - const auto B = _.FindDef(B_type_id); - const auto C = _.FindDef(C_type_id); - const auto D = _.FindDef(D_type_id); - - std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, - A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; - - A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2)); - B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2)); - C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2)); - D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2)); - - A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3)); - B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3)); - C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3)); - D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3)); - - A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4)); - B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4)); - C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4)); - D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4)); - - const auto notEqual = [](std::tuple<bool, bool, uint32_t> X, - std::tuple<bool, bool, uint32_t> Y) { - return (std::get<1>(X) && std::get<1>(Y) && - std::get<2>(X) != std::get<2>(Y)); - }; - - if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || - notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || - notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix scopes must match: " - << spvOpcodeString(opcode); - } - - if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || - notEqual(C_rows, D_rows)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix 'M' mismatch: " - << spvOpcodeString(opcode); - } - - if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || - notEqual(C_cols, D_cols)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix 'N' mismatch: " - << spvOpcodeString(opcode); - } - - if (notEqual(A_cols, B_rows)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix 'K' mismatch: " - << spvOpcodeString(opcode); + case spv::Op::OpSpecConstantOp: { + switch (inst->GetOperandAs<spv::Op>(2u)) { + case spv::Op::OpFAdd: + case spv::Op::OpFSub: + case spv::Op::OpFMul: + case spv::Op::OpFDiv: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpFNegate: + return ValidateFloat(_, inst, 3); + case spv::Op::OpUDiv: + case spv::Op::OpUMod: + return ValidateUnsignedInt(_, inst, 3); + case spv::Op::OpISub: + case spv::Op::OpIAdd: + case spv::Op::OpIMul: + case spv::Op::OpSDiv: + case spv::Op::OpSMod: + case spv::Op::OpSRem: + case spv::Op::OpSNegate: + return ValidateSignedInt(_, inst, 3); + default: + break; } break; } - - case spv::Op::OpCooperativeMatrixMulAddKHR: { - const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); - const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); - const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); - const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); - - if (!_.IsCooperativeMatrixAType(A_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix type must be A Type: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixBType(B_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix type must be B Type: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixAccType(C_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix type must be Accumulator Type: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixKHRType(D_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix type as Result Type: " - << spvOpcodeString(opcode); - } - - const auto A = _.FindDef(A_type_id); - const auto B = _.FindDef(B_type_id); - const auto C = _.FindDef(C_type_id); - const auto D = _.FindDef(D_type_id); - - std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, - A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; - - A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2)); - B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2)); - C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2)); - D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2)); - - A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3)); - B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3)); - C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3)); - D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3)); - - A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4)); - B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4)); - C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4)); - D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4)); - - const auto notEqual = [](std::tuple<bool, bool, uint32_t> X, - std::tuple<bool, bool, uint32_t> Y) { - return (std::get<1>(X) && std::get<1>(Y) && - std::get<2>(X) != std::get<2>(Y)); - }; - - if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || - notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || - notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix scopes must match: " - << spvOpcodeString(opcode); - } - - if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || - notEqual(C_rows, D_rows)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix 'M' mismatch: " - << spvOpcodeString(opcode); - } - - if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || - notEqual(C_cols, D_cols)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix 'N' mismatch: " - << spvOpcodeString(opcode); - } - - if (notEqual(A_cols, B_rows)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix 'K' mismatch: " - << spvOpcodeString(opcode); - } - break; - } - - case spv::Op::OpCooperativeMatrixReduceNV: { - if (!_.IsCooperativeMatrixKHRType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Result Type must be a cooperative matrix type: " - << spvOpcodeString(opcode); - } - - const auto result_comp_type_id = - _.FindDef(result_type)->GetOperandAs<uint32_t>(1); - - const auto matrix_id = inst->GetOperandAs<uint32_t>(2); - const auto matrix = _.FindDef(matrix_id); - const auto matrix_type_id = matrix->type_id(); - if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Matrix must have a cooperative matrix type: " - << spvOpcodeString(opcode); - } - const auto matrix_type = _.FindDef(matrix_type_id); - const auto matrix_comp_type_id = matrix_type->GetOperandAs<uint32_t>(1); - if (matrix_comp_type_id != result_comp_type_id) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Result Type and Matrix type must have the same component " - "type: " - << spvOpcodeString(opcode); - } - if (_.FindDef(result_type)->GetOperandAs<uint32_t>(2) != - matrix_type->GetOperandAs<uint32_t>(2)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Result Type and Matrix type must have the same scope: " - << spvOpcodeString(opcode); - } - - if (!_.IsCooperativeMatrixAccType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Result Type must have UseAccumulator: " - << spvOpcodeString(opcode); - } - if (!_.IsCooperativeMatrixAccType(matrix_type_id)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Matrix type must have UseAccumulator: " - << spvOpcodeString(opcode); - } - - const auto reduce_value = inst->GetOperandAs<uint32_t>(3); - - if ((reduce_value & - uint32_t( - spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) && - (reduce_value & uint32_t(spv::CooperativeMatrixReduceMask::Row | - spv::CooperativeMatrixReduceMask::Column))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Reduce 2x2 must not be used with Row/Column: " - << spvOpcodeString(opcode); - } - - std::tuple<bool, bool, uint32_t> result_rows, result_cols, matrix_rows, - matrix_cols; - result_rows = - _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(3)); - result_cols = - _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(4)); - matrix_rows = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(3)); - matrix_cols = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(4)); - - if (reduce_value & - uint32_t( - spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) { - if (std::get<1>(result_rows) && std::get<1>(result_cols) && - std::get<1>(matrix_rows) && std::get<1>(matrix_cols) && - (std::get<2>(result_rows) != std::get<2>(matrix_rows) / 2 || - std::get<2>(result_cols) != std::get<2>(matrix_cols) / 2)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For Reduce2x2, result rows/cols must be half of matrix " - "rows/cols: " - << spvOpcodeString(opcode); - } - } - if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Row)) { - if (std::get<1>(result_rows) && std::get<1>(matrix_rows) && - std::get<2>(result_rows) != std::get<2>(matrix_rows)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For ReduceRow, result rows must match matrix rows: " - << spvOpcodeString(opcode); - } - } - if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Column)) { - if (std::get<1>(result_cols) && std::get<1>(matrix_cols) && - std::get<2>(result_cols) != std::get<2>(matrix_cols)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For ReduceColumn, result cols must match matrix cols: " - << spvOpcodeString(opcode); - } - } - - const auto combine_func_id = inst->GetOperandAs<uint32_t>(4); - const auto combine_func = _.FindDef(combine_func_id); - if (!combine_func || combine_func->opcode() != spv::Op::OpFunction) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "CombineFunc must be a function: " << spvOpcodeString(opcode); - } - const auto function_type_id = combine_func->GetOperandAs<uint32_t>(3); - const auto function_type = _.FindDef(function_type_id); - if (function_type->operands().size() != 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "CombineFunc must have two parameters: " - << spvOpcodeString(opcode); - } - for (uint32_t i = 0; i < 3; ++i) { - // checks return type and two params - const auto param_type_id = function_type->GetOperandAs<uint32_t>(i + 1); - if (param_type_id != matrix_comp_type_id) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "CombineFunc return type and parameters must match matrix " - "component type: " - << spvOpcodeString(opcode); - } - } - - break; - } - default: break; }
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index 990ed31..443e970 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp
@@ -224,7 +224,7 @@ } // Can't use result_type because OpAtomicStore doesn't have a result - if (_.IsIntScalarType(data_type) && _.GetBitWidth(data_type) == 64 && + if (_.IsIntScalarType(data_type, 64) && !_.HasCapability(spv::Capability::Int64Atomics)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) @@ -235,7 +235,9 @@ if (!IsStorageClassAllowedByUniversalRules(storage_class)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": storage class forbidden by universal validation rules."; + << ": Can not be used with storage class " + << spvtools::StorageClassToString(storage_class) + << " by universal validation rules"; } // Then Shader rules @@ -249,8 +251,10 @@ (storage_class != spv::StorageClass::PhysicalStorageBuffer) && (storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4686) << spvOpcodeString(opcode) - << ": Vulkan spec only allows storage classes for atomic to " + << _.VkErrorID(4686) << spvOpcodeString(opcode) << ": " + << spvtools::StorageClassToString(storage_class) + << " is not allowed, the Vulkan spec only allows storage " + "classes for atomic to " "be: Uniform, Workgroup, Image, StorageBuffer, " "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT."; } @@ -335,8 +339,9 @@ (storage_class != spv::StorageClass::CrossWorkgroup) && (storage_class != spv::StorageClass::Generic)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": storage class must be Function, Workgroup, " + << spvOpcodeString(opcode) << ": storage class is " + << spvtools::StorageClassToString(storage_class) + << ", but must be Function, Workgroup, " "CrossWorkGroup or Generic in the OpenCL environment."; } @@ -352,7 +357,7 @@ // If result and pointer type are different, need to do special check here if (opcode == spv::Op::OpAtomicFlagTestAndSet || opcode == spv::Op::OpAtomicFlagClear) { - if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) { + if (!_.IsIntScalarType(data_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) << ": expected Pointer to point to a value of 32-bit integer " @@ -388,27 +393,6 @@ if (auto error = ValidateMemorySemantics( _, inst, unequal_semantics_index, memory_scope)) return error; - - // Volatile bits must match for equal and unequal semantics. Previous - // checks guarantee they are 32-bit constants, but we need to recheck - // whether they are evaluatable constants. - bool is_int32 = false; - bool is_equal_const = false; - bool is_unequal_const = false; - uint32_t equal_value = 0; - uint32_t unequal_value = 0; - std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst( - inst->GetOperandAs<uint32_t>(equal_semantics_index)); - std::tie(is_int32, is_unequal_const, unequal_value) = - _.EvalInt32IfConst( - inst->GetOperandAs<uint32_t>(unequal_semantics_index)); - if (is_equal_const && is_unequal_const && - ((equal_value & uint32_t(spv::MemorySemanticsMask::Volatile)) ^ - (unequal_value & uint32_t(spv::MemorySemanticsMask::Volatile)))) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Volatile mask setting must match for Equal and Unequal " - "memory semantics"; - } } if (opcode == spv::Op::OpAtomicStore) {
diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp index 0abd5c8..43a2e86 100644 --- a/source/val/validate_barriers.cpp +++ b/source/val/validate_barriers.cpp
@@ -45,10 +45,10 @@ model != spv::ExecutionModel::MeshNV) { if (message) { *message = - "OpControlBarrier requires one of the following " - "Execution " - "Models: TessellationControl, GLCompute, Kernel, " - "MeshNV or TaskNV"; + "In SPIR-V 1.2 or earlier, OpControlBarrier requires " + "one of the following " + "Execution Models: TessellationControl, GLCompute, " + "Kernel, MeshNV or TaskNV"; } return false; } @@ -94,8 +94,7 @@ } const uint32_t subgroup_count_type = _.GetOperandTypeId(inst, 2); - if (!_.IsIntScalarType(subgroup_count_type) || - _.GetBitWidth(subgroup_count_type) != 32) { + if (!_.IsIntScalarType(subgroup_count_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) << ": expected Subgroup Count to be a 32-bit int";
diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp index d8d9958..f038617 100644 --- a/source/val/validate_bitwise.cpp +++ b/source/val/validate_bitwise.cpp
@@ -30,18 +30,20 @@ if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4781) << "Expected int scalar or vector type for Base operand: " << spvOpcodeString(opcode); } // Vulkan has a restriction to 32 bit for base if (spvIsVulkanEnv(_.context()->target_env)) { - if (_.GetBitWidth(base_type) != 32) { + if (_.GetBitWidth(base_type) != 32 && + !_.options()->allow_vulkan_32_bit_bitwise) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4781) + << _.VkErrorID(10824) << "Expected 32-bit int type for Base operand: " - << spvOpcodeString(opcode); + << spvOpcodeString(opcode) + << _.MissingFeature("maintenance9 feature", + "--allow-vulkan-32-bit-bitwise", false); } } @@ -55,169 +57,211 @@ return SPV_SUCCESS; } -// Validates correctness of bitwise instructions. -spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { +spv_result_t ValidateShift(ValidationState_t& _, const Instruction* inst, + uint32_t starting_index = 2) { const spv::Op opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); - switch (opcode) { + const uint32_t result_dimension = _.GetDimension(result_type); + const uint32_t base_type = _.GetOperandTypeId(inst, starting_index); + const uint32_t shift_type = _.GetOperandTypeId(inst, starting_index + 1); + + if (!base_type || + (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type) && + !_.IsIntCooperativeVectorNVType(base_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.GetDimension(base_type) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(base_type) != _.GetBitWidth(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode); + + if (!shift_type || + (!_.IsIntScalarType(shift_type) && !_.IsIntVectorType(shift_type) && + !_.IsIntCooperativeVectorNVType(shift_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Shift to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.GetDimension(shift_type) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Shift to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateBitwise(ValidationState_t& _, const Instruction* inst, + uint32_t starting_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t result_dimension = _.GetDimension(result_type); + const uint32_t result_bit_width = _.GetBitWidth(result_type); + + for (size_t operand_index = starting_index; + operand_index < inst->operands().size(); ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!type_id || + (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && + !_.IsIntCooperativeVectorNVType(type_id))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector as operand: " + << spvOpcodeString(opcode) << " operand index " << operand_index; + + if (_.GetDimension(type_id) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + if (_.GetBitWidth(type_id) != result_bit_width) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateBitFieldInsert(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t insert_type = _.GetOperandTypeId(inst, 3); + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t count_type = _.GetOperandTypeId(inst, 5); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + if (insert_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Insert Type to be equal to Result Type: " + << spvOpcodeString(opcode); + + if (!offset_type || !_.IsIntScalarType(offset_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Offset Type to be int scalar: " + << spvOpcodeString(opcode); + + if (!count_type || !_.IsIntScalarType(count_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Count Type to be int scalar: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateBitFieldExtract(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t offset_type = _.GetOperandTypeId(inst, 3); + const uint32_t count_type = _.GetOperandTypeId(inst, 4); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + if (!offset_type || !_.IsIntScalarType(offset_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Offset Type to be int scalar: " + << spvOpcodeString(opcode); + + if (!count_type || !_.IsIntScalarType(count_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Count Type to be int scalar: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateBitReverse(ValidationState_t& _, const Instruction* inst) { + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateBitCount(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + const uint32_t base_dimension = _.GetDimension(base_type); + const uint32_t result_dimension = _.GetDimension(result_type); + + if (base_dimension != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base dimension to be equal to Result Type " + "dimension: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +// Validates correctness of bitwise instructions. +spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { case spv::Op::OpShiftRightLogical: case spv::Op::OpShiftRightArithmetic: - case spv::Op::OpShiftLeftLogical: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t result_dimension = _.GetDimension(result_type); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - const uint32_t shift_type = _.GetOperandTypeId(inst, 3); - - if (!base_type || - (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base to be int scalar or vector: " - << spvOpcodeString(opcode); - - if (_.GetDimension(base_type) != result_dimension) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base to have the same dimension " - << "as Result Type: " << spvOpcodeString(opcode); - - if (_.GetBitWidth(base_type) != _.GetBitWidth(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base to have the same bit width " - << "as Result Type: " << spvOpcodeString(opcode); - - if (!shift_type || - (!_.IsIntScalarType(shift_type) && !_.IsIntVectorType(shift_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Shift to be int scalar or vector: " - << spvOpcodeString(opcode); - - if (_.GetDimension(shift_type) != result_dimension) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Shift to have the same dimension " - << "as Result Type: " << spvOpcodeString(opcode); - break; - } - + case spv::Op::OpShiftLeftLogical: + return ValidateShift(_, inst); case spv::Op::OpBitwiseOr: case spv::Op::OpBitwiseXor: case spv::Op::OpBitwiseAnd: - case spv::Op::OpNot: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t result_dimension = _.GetDimension(result_type); - const uint32_t result_bit_width = _.GetBitWidth(result_type); - - for (size_t operand_index = 2; operand_index < inst->operands().size(); - ++operand_index) { - const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); - if (!type_id || - (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector as operand: " - << spvOpcodeString(opcode) << " operand index " - << operand_index; - - if (_.GetDimension(type_id) != result_dimension) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to have the same dimension " - << "as Result Type: " << spvOpcodeString(opcode) - << " operand index " << operand_index; - - if (_.GetBitWidth(type_id) != result_bit_width) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to have the same bit width " - << "as Result Type: " << spvOpcodeString(opcode) - << " operand index " << operand_index; - } - break; - } - - case spv::Op::OpBitFieldInsert: { - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - const uint32_t insert_type = _.GetOperandTypeId(inst, 3); - const uint32_t offset_type = _.GetOperandTypeId(inst, 4); - const uint32_t count_type = _.GetOperandTypeId(inst, 5); - - if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { - return error; - } - - if (insert_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Insert Type to be equal to Result Type: " - << spvOpcodeString(opcode); - - if (!offset_type || !_.IsIntScalarType(offset_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Offset Type to be int scalar: " - << spvOpcodeString(opcode); - - if (!count_type || !_.IsIntScalarType(count_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Count Type to be int scalar: " - << spvOpcodeString(opcode); - break; - } - + case spv::Op::OpNot: + return ValidateBitwise(_, inst); + case spv::Op::OpBitFieldInsert: + return ValidateBitFieldInsert(_, inst); case spv::Op::OpBitFieldSExtract: - case spv::Op::OpBitFieldUExtract: { - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - const uint32_t offset_type = _.GetOperandTypeId(inst, 3); - const uint32_t count_type = _.GetOperandTypeId(inst, 4); + case spv::Op::OpBitFieldUExtract: + return ValidateBitFieldExtract(_, inst); + case spv::Op::OpBitReverse: + return ValidateBitReverse(_, inst); + case spv::Op::OpBitCount: + return ValidateBitCount(_, inst); - if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { - return error; + case spv::Op::OpSpecConstantOp: { + switch (inst->GetOperandAs<spv::Op>(2u)) { + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + return ValidateShift(_, inst, 3); + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + return ValidateBitwise(_, inst, 3); + default: + break; } - - if (!offset_type || !_.IsIntScalarType(offset_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Offset Type to be int scalar: " - << spvOpcodeString(opcode); - - if (!count_type || !_.IsIntScalarType(count_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Count Type to be int scalar: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpBitReverse: { - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - - if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { - return error; - } - - break; - } - - case spv::Op::OpBitCount: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - - if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { - return error; - } - - const uint32_t base_dimension = _.GetDimension(base_type); - const uint32_t result_dimension = _.GetDimension(result_type); - - if (base_dimension != result_dimension) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base dimension to be equal to Result Type " - "dimension: " - << spvOpcodeString(opcode); break; }
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 1305dc1..5e913d4 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp
@@ -17,21 +17,22 @@ // Validates correctness of built-in variables. #include <array> +#include <cstdint> #include <functional> #include <list> #include <map> #include <set> #include <sstream> -#include <stack> #include <string> +#include <unordered_map> #include <vector> #include "source/opcode.h" #include "source/spirv_target_env.h" -#include "source/util/bitutils.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" +#include "spirv/unified1/spirv.hpp11" namespace spvtools { namespace val { @@ -122,7 +123,7 @@ VUIDErrorMax, } VUIDError; -const static uint32_t NumVUIDBuiltins = 39; +const static uint32_t NumVUIDBuiltins = 42; typedef struct { spv::BuiltIn builtIn; @@ -169,9 +170,13 @@ {spv::BuiltIn::CullMaskKHR, {6735, 6736, 6737}}, {spv::BuiltIn::BaryCoordKHR, {4154, 4155, 4156}}, {spv::BuiltIn::BaryCoordNoPerspKHR, {4160, 4161, 4162}}, + {spv::BuiltIn::LocalInvocationIndex, {4284, 4285, 4286}}, {spv::BuiltIn::PrimitivePointIndicesEXT, {7041, 7043, 7044}}, {spv::BuiltIn::PrimitiveLineIndicesEXT, {7047, 7049, 7050}}, {spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}}, + {spv::BuiltIn::CullPrimitiveEXT, {7034, 7035, 7036}}, + {spv::BuiltIn::HitTriangleVertexPositionsKHR, {8747, 8748, 8749}}, + // clang-format on }}; @@ -192,6 +197,7 @@ switch (builtin) { case spv::BuiltIn::HitKindKHR: case spv::BuiltIn::HitTNV: + case spv::BuiltIn::HitTriangleVertexPositionsKHR: if (stage == spv::ExecutionModel::AnyHitKHR || stage == spv::ExecutionModel::ClosestHitKHR) { return true; @@ -269,6 +275,9 @@ // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed. spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateSingleBuiltInAtDefinitionVulkan( + const Decoration& decoration, const Instruction& inst, + const spv::BuiltIn label); // The following section contains functions which are called when id defined // by |inst| is decorated with BuiltIn |decoration|. @@ -362,12 +371,27 @@ spv_result_t ValidateShadingRateAtDefinition(const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateDescriptorHeapAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateRayTracingBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst); spv_result_t ValidateMeshShadingEXTBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst); + // Used as a common method for validating MeshEXT builtins + spv_result_t ValidateMeshBuiltinInterfaceRules( + const Decoration& decoration, const Instruction& inst, + spv::Op scalar_type, const Instruction& referenced_from_inst); + spv_result_t ValidatePrimitiveShadingRateInterfaceRules( + const Decoration& decoration, const Instruction& inst, + const Instruction& referenced_from_inst); + // Builtin that needs check incase **not** used with MeshEXT + spv_result_t ValidateNonMeshInterfaceRules( + const Decoration& decoration, const Instruction& inst, + const Instruction& referenced_from_inst); + // The following section contains functions which are called when id defined // by |referenced_inst| is // 1. referenced by |referenced_from_inst| @@ -585,6 +609,10 @@ spv_result_t ValidateBool( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag); + spv_result_t ValidateBlockTypeOrArrayedType( + const Decoration& decoration, const Instruction& inst, + bool& present_in_block, spv::Op expected_scalar_type, + const std::function<spv_result_t(const std::string& message)>& diag); spv_result_t ValidateI( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag); @@ -627,6 +655,10 @@ const Decoration& decoration, const Instruction& inst, uint32_t num_components, const std::function<spv_result_t(const std::string& message)>& diag); + spv_result_t ValidateArrayedF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, uint32_t array_length, + const std::function<spv_result_t(const std::string& message)>& diag); spv_result_t ValidateF32VecHelper( const Decoration& decoration, const Instruction& inst, uint32_t num_components, @@ -667,10 +699,75 @@ // UniformConstant". std::string GetStorageClassDesc(const Instruction& inst) const; + uint64_t GetArrayLength(uint32_t interface_var_id); + // Updates inner working of the class. Is called sequentially for every // instruction. void Update(const Instruction& inst); + bool IsBulitinInEntryPoint(const Instruction& inst, uint32_t entry_point) { + auto get_underlying_type_id = [&](const Instruction* ifx_var) { + auto pointer_type_inst = _.FindDef(ifx_var->type_id()); + auto type_inst = _.FindDef(pointer_type_inst->GetOperandAs<uint32_t>(2)); + while (type_inst->opcode() == spv::Op::OpTypeArray) { + type_inst = _.FindDef(type_inst->GetOperandAs<uint32_t>(1)); + }; + return type_inst->id(); + }; + + for (const auto& desc : _.entry_point_descriptions(entry_point)) { + for (auto interface : desc.interfaces) { + if (inst.opcode() == spv::Op::OpTypeStruct) { + auto varInst = _.FindDef(interface); + if (inst.id() == get_underlying_type_id(varInst)) { + return true; + } + } else if (inst.id() == interface) { + return true; + } + } + } + return false; + } + + // Check if "inst" is an interface variable or type of a interface varibale + // of any mesh entry point. Populate entry_point_interface_id with all + // entry points and interface variables that refer to the "inst" + bool IsMeshInterfaceVar( + const Instruction& inst, + std::map<uint32_t, uint32_t>& entry_point_interface_id) { + auto get_underlying_type_id = [&](const Instruction* ifx_var) { + auto pointer_type_inst = _.FindDef(ifx_var->type_id()); + auto type_inst = _.FindDef(pointer_type_inst->GetOperandAs<uint32_t>(2)); + while (type_inst->opcode() == spv::Op::OpTypeArray) { + type_inst = _.FindDef(type_inst->GetOperandAs<uint32_t>(1)); + }; + return type_inst->id(); + }; + + for (const uint32_t entry_point : _.entry_points()) { + const auto* models = _.GetExecutionModels(entry_point); + if (models->find(spv::ExecutionModel::MeshEXT) != models->end() || + models->find(spv::ExecutionModel::MeshNV) != models->end()) { + for (const auto& desc : _.entry_point_descriptions(entry_point)) { + for (auto interface : desc.interfaces) { + if (inst.opcode() == spv::Op::OpTypeStruct) { + auto varInst = _.FindDef(interface); + if (inst.id() == get_underlying_type_id(varInst)) { + entry_point_interface_id[entry_point] = interface; + break; + } + } else if (inst.id() == interface) { + entry_point_interface_id[entry_point] = interface; + break; + } + } + } + } + } + return !entry_point_interface_id.empty(); + } + ValidationState_t& _; // Mapping id -> list of rules which validate instruction referencing the @@ -691,6 +788,10 @@ // Execution models with which the current function can be called. std::set<spv::ExecutionModel> execution_models_; + + // For Builtin that can only be declared once in an entry point, keep track if + // the entry point has it already + std::set<uint32_t> cull_primitive_entry_points_; }; void BuiltInsValidator::Update(const Instruction& inst) { @@ -768,6 +869,29 @@ return ss.str(); } +uint64_t BuiltInsValidator::GetArrayLength(uint32_t interface_var_id) { + uint32_t underlying_type; + spv::StorageClass storage_class; + uint64_t array_len = -1; + const Instruction* inst = _.FindDef(interface_var_id); + if (inst->opcode() != spv::Op::OpVariable) { + return -1; + } + + if (!_.GetPointerTypeInfo(inst->type_id(), &underlying_type, + &storage_class)) { + return 0; + } + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + // Get the array length + const auto length_id = _.FindDef(underlying_type)->word(3u); + if (!_.EvalConstantValUint64(length_id, &array_len)) { + return 0; + } + } + return array_len; +} + spv_result_t BuiltInsValidator::ValidateBool( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag) { @@ -784,6 +908,55 @@ return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateBlockTypeOrArrayedType( + const Decoration& decoration, const Instruction& inst, bool& isBlock, + spv::Op expected_scalar_type, + const std::function<spv_result_t(const std::string& message)>& diag) { + uint32_t underlying_type = 0; + int64_t array_len = -1; + isBlock = true; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + // Get the array length + const auto length_id = _.FindDef(underlying_type)->word(3u); + if (!_.EvalConstantValInt64(length_id, &array_len)) { + return diag(GetDefinitionDesc(decoration, inst) + + " Failed to find the array length."); + } + underlying_type = _.FindDef(underlying_type)->word(2u); + isBlock = false; + } else if (!_.HasDecoration(inst.id(), spv::Decoration::Block)) { + // If not in array, and bool is in a struct, must be in a Block struct + return diag(GetDefinitionDesc(decoration, inst) + + " Scalar boolean must be in a Block."); + } + + switch (expected_scalar_type) { + case spv::Op::OpTypeBool: + if (!_.IsBoolScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " is not a bool scalar."); + } + break; + case spv::Op::OpTypeInt: + if (!_.IsIntScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " is not an integer scalar."); + } + break; + default: + assert(0 && "Unhandled scalar type"); + return diag(GetDefinitionDesc(decoration, inst) + + " is not a recognized scalar type."); + } + + return SPV_SUCCESS; +} + spv_result_t BuiltInsValidator::ValidateI( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag) { @@ -988,6 +1161,38 @@ underlying_type); } +spv_result_t BuiltInsValidator::ValidateArrayedF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, uint32_t array_length, + const std::function<spv_result_t(const std::string& message)>& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + if (_.GetIdOpcode(underlying_type) != spv::Op::OpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t length_id = _.FindDef(underlying_type)->word(3u); + uint64_t found_length = 0; + if (!_.EvalConstantValUint64(length_id, &found_length)) { + return diag(GetDefinitionDesc(decoration, inst) + + " array has a non constant length."); + } + + if (array_length != found_length) { + return diag(GetDefinitionDesc(decoration, inst) + " array length must be " + + std::to_string(array_length)); + } + + underlying_type = _.FindDef(underlying_type)->word(2u); + + return ValidateF32VecHelper(decoration, inst, num_components, diag, + underlying_type); +} + spv_result_t BuiltInsValidator::ValidateF32Vec( const Decoration& decoration, const Instruction& inst, uint32_t num_components, @@ -2125,38 +2330,6 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanEnv(_.context()->target_env)) { - // PrimitiveId can be a per-primitive variable for mesh shader stage. - // In such cases variable will have an array of 32-bit integers. - if (decoration.struct_member_index() != Decoration::kInvalidMember) { - // This must be a 32-bit int scalar. - if (spv_result_t error = ValidateI32( - decoration, inst, - [this, &inst](const std::string& message) -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(4337) - << "According to the Vulkan spec BuiltIn PrimitiveId " - "variable needs to be a 32-bit int scalar. " - << message; - })) { - return error; - } - } else { - if (spv_result_t error = ValidateOptionalArrayedI32( - decoration, inst, - [this, &inst](const std::string& message) -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(4337) - << "According to the Vulkan spec BuiltIn PrimitiveId " - "variable needs to be a 32-bit int scalar. " - << message; - })) { - return error; - } - } - } - - // Seed at reference checks with this built-in. return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst); } @@ -2223,6 +2396,27 @@ referenced_from_inst, std::placeholders::_1)); } + if (!_.HasCapability(spv::Capability::MeshShadingEXT) && + !_.HasCapability(spv::Capability::MeshShadingNV) && + !_.HasCapability(spv::Capability::Geometry) && + !_.HasCapability(spv::Capability::Tessellation)) { + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4333, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables in the Fragment execution model unless it declares " + "Geometry, Tessellation, or MeshShader capabilities.", + spv::ExecutionModel::Fragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, decoration, + built_in_inst, spv::Op::OpTypeInt, std::placeholders::_1)); + + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNonMeshInterfaceRules, this, + decoration, built_in_inst, std::placeholders::_1)); + for (const spv::ExecutionModel execution_model : execution_models_) { switch (execution_model) { case spv::ExecutionModel::Fragment: @@ -2519,6 +2713,13 @@ })) { return error; } + + if (!_.HasDecoration(inst.id(), spv::Decoration::Patch)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(10880) + << "BuiltIn TessLevelOuter variable needs to also have a Patch " + "decoration."; + } } // Seed at reference checks with this built-in. @@ -2533,13 +2734,20 @@ [this, &inst](const std::string& message) -> spv_result_t { return _.diag(SPV_ERROR_INVALID_DATA, &inst) << _.VkErrorID(4397) - << "According to the Vulkan spec BuiltIn TessLevelOuter " + << "According to the Vulkan spec BuiltIn TessLevelInner " "variable needs to be a 2-component 32-bit float " "array. " << message; })) { return error; } + + if (!_.HasDecoration(inst.id(), spv::Decoration::Patch)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(10880) + << "BuiltIn TessLevelInner variable needs to also have a Patch " + "decoration."; + } } // Seed at reference checks with this built-in. @@ -2663,14 +2871,69 @@ spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition( const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin( + spv::BuiltIn::LocalInvocationIndex, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + "LocalInvocationIndex variable needs to be a 32-bit " + "int scalar. " + << message; + })) { + return error; + } + } + // Seed at reference checks with this built-in. return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst); } spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference( const Decoration& decoration, const Instruction& built_in_inst, - const Instruction&, + const Instruction& referenced_inst, const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = + GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex, + VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << "Vulkan spec allows BuiltIn LocalInvocationIndex to be only " + "used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + bool has_vulkan_model = + execution_model == spv::ExecutionModel::GLCompute || + execution_model == spv::ExecutionModel::TaskNV || + execution_model == spv::ExecutionModel::MeshNV || + execution_model == spv::ExecutionModel::TaskEXT || + execution_model == spv::ExecutionModel::MeshEXT; + + if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) { + uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex, + VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << "Vulkan spec allows BuiltIn LocalInvocationIndex to be used " + "only with GLCompute, MeshNV, TaskNV, MeshEXT or" + << " TaskEXT execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + if (function_id_ == 0) { // Propagate this rule to all dependant ids in the global scope. id_to_at_reference_checks_[referenced_from_inst.id()].push_back( @@ -2722,52 +2985,270 @@ return SPV_SUCCESS; } -spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition( - const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanEnv(_.context()->target_env)) { - // This can be a per-primitive variable for mesh shader stage. - // In such cases variable will have an array of 32-bit integers. - if (decoration.struct_member_index() != Decoration::kInvalidMember) { - // This must be a 32-bit int scalar. - if (spv_result_t error = ValidateI32( - decoration, inst, - [this, &decoration, - &inst](const std::string& message) -> spv_result_t { - uint32_t vuid = - (decoration.builtin() == spv::BuiltIn::Layer) ? 4276 : 4408; +typedef struct { + uint32_t array_type; + uint32_t array_size; + uint32_t block_array_size; + uint32_t perprim_deco; +} MeshBuiltinVUIDs; + +spv_result_t BuiltInsValidator::ValidateMeshBuiltinInterfaceRules( + const Decoration& decoration, const Instruction& inst, spv::Op scalar_type, + const Instruction& referenced_from_inst) { + if (function_id_) { + if (!execution_models_.count(spv::ExecutionModel::MeshEXT)) { + return SPV_SUCCESS; + } + + const spv::BuiltIn builtin = decoration.builtin(); + const bool is_topology = + builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT || + builtin == spv::BuiltIn::PrimitiveLineIndicesEXT || + builtin == spv::BuiltIn::PrimitivePointIndicesEXT; + + // These builtin have the ability to be an array with MeshEXT + // When an array, we need to make sure the array size lines up + std::map<uint32_t, uint32_t> entry_interface_id_map; + const bool is_interface_var = + IsMeshInterfaceVar(inst, entry_interface_id_map); + + if (!is_topology) { + bool is_block = false; + + static const std::unordered_map<spv::BuiltIn, MeshBuiltinVUIDs> + mesh_vuid_map = {{ + {spv::BuiltIn::CullPrimitiveEXT, {7036, 10589, 10590, 7038}}, + {spv::BuiltIn::PrimitiveId, {10595, 10596, 10597, 7040}}, + {spv::BuiltIn::Layer, {10592, 10593, 10594, 7039}}, + {spv::BuiltIn::ViewportIndex, {10601, 10602, 10603, 7060}}, + {spv::BuiltIn::PrimitiveShadingRateKHR, + {10598, 10599, 10600, 7059}}, + }}; + const MeshBuiltinVUIDs& vuids = mesh_vuid_map.at(builtin); + if (spv_result_t error = ValidateBlockTypeOrArrayedType( + decoration, inst, is_block, scalar_type, + [this, &inst, &builtin, &scalar_type, + &vuids](const std::string& message) -> spv_result_t { return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(vuid) - << "According to the Vulkan spec BuiltIn " + << _.VkErrorID(vuids.array_type) + << "According to the Vulkan specspec BuiltIn " << _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_BUILT_IN, - (uint32_t)decoration.builtin()) - << "variable needs to be a 32-bit int scalar. " - << message; + SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin) + << " variable needs to be a either a " + << spvOpcodeString(scalar_type) + << " or an " + "array of " + << spvOpcodeString(scalar_type) << ". " << message; })) { return error; } - } else { - if (spv_result_t error = ValidateOptionalArrayedI32( + + if (!_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuids.perprim_deco) + << "According to the Vulkan spec the variable decorated with " + "Builtin " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)builtin) + << " within the MeshEXT Execution Model must also be " + << "decorated with the PerPrimitiveEXT decoration. "; + } + if (is_interface_var) { + for (const auto& id : entry_interface_id_map) { + uint32_t entry_point_id = id.first; + uint32_t interface_var_id = id.second; + + const uint64_t interface_size = GetArrayLength(interface_var_id); + const uint32_t output_prim_size = + _.GetOutputPrimitivesEXT(entry_point_id); + if (interface_size != output_prim_size) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(is_block ? vuids.block_array_size + : vuids.array_size) + << " The size of the array decorated with " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)builtin) + << " (" << interface_size + << ") must match the value specified by OutputPrimitivesEXT " + "(" + << output_prim_size << "). "; + } + } + } + } + + if (is_interface_var && is_topology) { + for (const auto& id : entry_interface_id_map) { + uint32_t entry_point_id = id.first; + + uint64_t max_output_primitives = + _.GetOutputPrimitivesEXT(entry_point_id); + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + uint64_t primitive_array_dim = 0; + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(3u); + if (!_.EvalConstantValUint64(underlying_type, &primitive_array_dim)) { + assert(0 && "Array type definition is corrupt"); + } + } + + const auto* modes = _.GetExecutionModes(entry_point_id); + if (builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT) { + if (!modes || !modes->count(spv::ExecutionMode::OutputTrianglesEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7054) + << "The PrimitiveTriangleIndicesEXT decoration must be used " + "with the OutputTrianglesEXT Execution Mode. "; + } + if (primitive_array_dim && + primitive_array_dim != max_output_primitives) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7058) + << "The size of the array decorated with " + "PrimitiveTriangleIndicesEXT (" + << primitive_array_dim + << ") must match the value specified " + "by OutputPrimitivesEXT (" + << max_output_primitives << "). "; + } + } else if (builtin == spv::BuiltIn::PrimitiveLineIndicesEXT) { + if (!modes || !modes->count(spv::ExecutionMode::OutputLinesEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7048) + << "The PrimitiveLineIndicesEXT decoration must be used " + "with the OutputLinesEXT Execution Mode. "; + } + if (primitive_array_dim && + primitive_array_dim != max_output_primitives) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7052) + << "The size of the array decorated with " + "PrimitiveLineIndicesEXT (" + << primitive_array_dim + << ") must match the value specified " + "by OutputPrimitivesEXT (" + << max_output_primitives << "). "; + } + + } else if (builtin == spv::BuiltIn::PrimitivePointIndicesEXT) { + if (!modes || !modes->count(spv::ExecutionMode::OutputPoints)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7042) + << "The PrimitivePointIndicesEXT decoration must be used " + "with the OutputPoints Execution Mode. "; + } + if (primitive_array_dim && + primitive_array_dim != max_output_primitives) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7046) + << "The size of the array decorated with " + "PrimitivePointIndicesEXT (" + << primitive_array_dim + << ") must match the value specified " + "by OutputPrimitivesEXT (" + << max_output_primitives << "). "; + } + } + } + } + } else { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, + decoration, inst, scalar_type, std::placeholders::_1)); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateInterfaceRules( + const Decoration& decoration, const Instruction& inst, + const Instruction& referenced_from_inst) { + if (function_id_) { + if (!execution_models_.count(spv::ExecutionModel::MeshEXT)) { + if (spv_result_t error = ValidateI32( decoration, inst, - [this, &decoration, - &inst](const std::string& message) -> spv_result_t { - uint32_t vuid = - (decoration.builtin() == spv::BuiltIn::Layer) ? 4276 : 4408; + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(vuid) + << _.VkErrorID(4486) << "According to the Vulkan spec BuiltIn " << _.grammar().lookupOperandName( SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)decoration.builtin()) - << "variable needs to be a 32-bit int scalar. " + << " variable needs to be a 32-bit int scalar. " << message; })) { return error; } } + } else { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePrimitiveShadingRateInterfaceRules, this, + decoration, inst, std::placeholders::_1)); } + return SPV_SUCCESS; +} - // Seed at reference checks with this built-in. +// For Layer, ViewportIndex, and PrimitiveId +spv_result_t BuiltInsValidator::ValidateNonMeshInterfaceRules( + const Decoration& decoration, const Instruction& inst, + const Instruction& referenced_from_inst) { + if (function_id_) { + // This can be a per-primitive variable for NV mesh shader stage. + // In such cases variable will have an array of 32-bit integers. + if (!execution_models_.count(spv::ExecutionModel::MeshEXT)) { + const spv::BuiltIn builtin = decoration.builtin(); + const uint32_t vuid = (builtin == spv::BuiltIn::Layer) ? 4276 + : (builtin == spv::BuiltIn::ViewportIndex) ? 4408 + : 4337; + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &vuid, builtin, + &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin) + << "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } else if (spv_result_t error = ValidateOptionalArrayedI32( + decoration, inst, + [this, &vuid, builtin, + &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)builtin) + << "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + } else { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNonMeshInterfaceRules, this, + decoration, inst, std::placeholders::_1)); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst); } @@ -2825,6 +3306,14 @@ referenced_from_inst, std::placeholders::_1)); } + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, decoration, + built_in_inst, spv::Op::OpTypeInt, std::placeholders::_1)); + + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNonMeshInterfaceRules, this, + decoration, built_in_inst, std::placeholders::_1)); + for (const spv::ExecutionModel execution_model : execution_models_) { switch (execution_model) { case spv::ExecutionModel::Geometry: @@ -3215,16 +3704,8 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition( const Decoration& decoration, const Instruction& inst) { + // Vulkan requires 32-bit int, but Universal has no restrictions if (spvIsVulkanEnv(_.context()->target_env)) { - if (spvIsVulkanEnv(_.context()->target_env) && - !spvOpcodeIsConstant(inst.opcode())) { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(4426) - << "Vulkan spec requires BuiltIn WorkgroupSize to be a " - "constant. " - << GetIdDesc(inst) << " is not a constant."; - } - if (spv_result_t error = ValidateI32Vec( decoration, inst, 3, [this, &inst](const std::string& message) -> spv_result_t { @@ -3239,6 +3720,69 @@ } } + if (!spvOpcodeIsConstant(inst.opcode())) { + if (spvIsVulkanEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4426) + << "Vulkan spec requires BuiltIn WorkgroupSize to be a " + "constant. " + << GetIdDesc(inst) << " is not a constant."; + } + } else if (inst.opcode() == spv::Op::OpConstantComposite) { + // can only validate product if static and not spec constant + if (_.FindDef(inst.word(3))->opcode() == spv::Op::OpConstant && + _.FindDef(inst.word(4))->opcode() == spv::Op::OpConstant && + _.FindDef(inst.word(5))->opcode() == spv::Op::OpConstant) { + uint64_t x_size, y_size, z_size; + // ValidateI32Vec above confirms there will be 3 words to read + bool static_x = _.EvalConstantValUint64(inst.word(3), &x_size); + bool static_y = _.EvalConstantValUint64(inst.word(4), &y_size); + bool static_z = _.EvalConstantValUint64(inst.word(5), &z_size); + if (static_x && static_y && static_z) { + const uint64_t product_size = x_size * y_size * z_size; + if (product_size == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "WorkgroupSize decorations must not have a static " + "product of zero (X = " + << x_size << ", Y = " << y_size << ", Z = " << z_size << ")."; + } + + // If there is a known static workgroup size, all entrypoints with + // explicit derivative execution modes can be validated. These are only + // found in execution models that support explicit workgroup sizes + for (const uint32_t entry_point : _.entry_points()) { + const auto* modes = _.GetExecutionModes(entry_point); + if (!modes) continue; + if (modes->count(spv::ExecutionMode::DerivativeGroupQuadsKHR)) { + if (x_size % 2 != 0 || y_size % 2 != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(10151) + << "WorkgroupSize decorations has a static dimensions of " + "(X = " + << x_size << ", Y = " << y_size << ") but Entry Point id " + << entry_point + << " has an DerivativeGroupQuadsKHR execution mode, so " + "both dimensions must be a multiple of 2"; + } + } + if (modes->count(spv::ExecutionMode::DerivativeGroupLinearKHR)) { + if (product_size % 4 != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(10152) + << "WorkgroupSize decorations has a static dimensions of " + "(X = " + << x_size << ", Y = " << y_size << ", Z = " << z_size + << ") but Entry Point id " << entry_point + << " has an DerivativeGroupLinearKHR execution mode, so " + "the product (" + << product_size << ") must be a multiple of 4"; + } + } + } + } + } + } + // Seed at reference checks with this built-in. return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst); } @@ -3877,25 +4421,6 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanEnv(_.context()->target_env)) { - if (spv_result_t error = ValidateI32( - decoration, inst, - [this, &inst, - &decoration](const std::string& message) -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(4486) - << "According to the Vulkan spec BuiltIn " - << _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_BUILT_IN, - (uint32_t)decoration.builtin()) - << " variable needs to be a 32-bit int scalar. " - << message; - })) { - return error; - } - } - - // Seed at reference checks with this built-in. return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst); } @@ -3917,6 +4442,14 @@ << " " << GetStorageClassDesc(referenced_from_inst); } + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, decoration, + built_in_inst, spv::Op::OpTypeInt, std::placeholders::_1)); + + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePrimitiveShadingRateInterfaceRules, this, + decoration, built_in_inst, std::placeholders::_1)); + for (const spv::ExecutionModel execution_model : execution_models_) { switch (execution_model) { case spv::ExecutionModel::Vertex: @@ -3930,7 +4463,7 @@ << _.grammar().lookupOperandName( SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)decoration.builtin()) - << " to be used only with Vertex, Geometry, or MeshNV " + << " to be used only with Vertex, Geometry, MeshNV or MeshEXT " "execution models. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); @@ -4015,6 +4548,18 @@ return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateDescriptorHeapAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " cannot be used as a member decoration "; + } + return SPV_SUCCESS; +} + spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { @@ -4126,6 +4671,25 @@ return error; } break; + case spv::BuiltIn::HitTriangleVertexPositionsKHR: + // array[3] of f32 vec3 + if (spv_result_t error = ValidateArrayedF32Vec( + decoration, inst, 3, 3, + [this, &inst, + builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be an array of 3, 32-bit float " + "3-component vectors. " + << message; + })) { + return error; + } + break; default: assert(0 && "Unexpected ray tracing builtin"); break; @@ -4190,63 +4754,104 @@ if (spvIsVulkanEnv(_.context()->target_env)) { const spv::BuiltIn builtin = decoration.builtin(); uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); - if (builtin == spv::BuiltIn::PrimitivePointIndicesEXT) { - if (spv_result_t error = ValidateI32Arr( - decoration, inst, - [this, &inst, &decoration, - &vuid](const std::string& message) -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(vuid) << "According to the " - << spvLogStringForEnv(_.context()->target_env) - << " spec BuiltIn " - << _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_BUILT_IN, - (uint32_t)decoration.builtin()) - << " variable needs to be a 32-bit int array." - << message; - })) { - return error; + switch (builtin) { + case spv::BuiltIn::PrimitivePointIndicesEXT: + if (spv_result_t error = ValidateI32Arr( + decoration, inst, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int array." + << message; + })) { + return error; + } + break; + case spv::BuiltIn::PrimitiveLineIndicesEXT: + if (spv_result_t error = ValidateArrayedI32Vec( + decoration, inst, 2, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 2-component 32-bit int " + "array." + << message; + })) { + return error; + } + break; + case spv::BuiltIn::PrimitiveTriangleIndicesEXT: + if (spv_result_t error = ValidateArrayedI32Vec( + decoration, inst, 3, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 3-component 32-bit int " + "array." + << message; + })) { + return error; + } + break; + case spv::BuiltIn::CullPrimitiveEXT: { + for (const uint32_t entry_point : _.entry_points()) { + auto* models = _.GetExecutionModels(entry_point); + if (models->find(spv::ExecutionModel::MeshEXT) == models->end() && + models->find(spv::ExecutionModel::MeshNV) == models->end()) { + continue; + } + + if (IsBulitinInEntryPoint(inst, entry_point)) { + if (cull_primitive_entry_points_.find(entry_point) != + cull_primitive_entry_points_.end()) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(10591) + << "There must be only one declaration of the " + "CullPrimitiveEXT associated in entry point's " + "interface. " + << GetIdDesc(*_.FindDef(entry_point)); + } else { + cull_primitive_entry_points_.insert(entry_point); + } + } + } + + break; } + default: + assert(0 && "Unexpected mesh EXT builtin"); } - if (builtin == spv::BuiltIn::PrimitiveLineIndicesEXT) { - if (spv_result_t error = ValidateArrayedI32Vec( - decoration, inst, 2, - [this, &inst, &decoration, - &vuid](const std::string& message) -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(vuid) << "According to the " - << spvLogStringForEnv(_.context()->target_env) - << " spec BuiltIn " - << _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_BUILT_IN, - (uint32_t)decoration.builtin()) - << " variable needs to be a 2-component 32-bit int " - "array." - << message; - })) { - return error; - } - } - if (builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT) { - if (spv_result_t error = ValidateArrayedI32Vec( - decoration, inst, 3, - [this, &inst, &decoration, - &vuid](const std::string& message) -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << _.VkErrorID(vuid) << "According to the " - << spvLogStringForEnv(_.context()->target_env) - << " spec BuiltIn " - << _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_BUILT_IN, - (uint32_t)decoration.builtin()) - << " variable needs to be a 3-component 32-bit int " - "array." - << message; - })) { - return error; - } + + // - We know this only allowed for Mesh Execution Model. + // - The Scalar type is is boolean for CullPrimitiveEXT, the other 3 builtin + // (topology) don't need this type. + // - It is possible to have multiple mesh + // shaders (https://github.com/KhronosGroup/SPIRV-Tools/issues/6320) and we + // need to validate these at reference time. + if (spv_result_t error = ValidateMeshBuiltinInterfaceRules( + decoration, inst, spv::Op::OpTypeBool, inst)) { + return error; } } + // Seed at reference checks with this built-in. return ValidateMeshShadingEXTBuiltinsAtReference(decoration, inst, inst, inst); @@ -4273,7 +4878,6 @@ referenced_from_inst) << " " << GetStorageClassDesc(referenced_from_inst); } - for (const spv::ExecutionModel execution_model : execution_models_) { if (execution_model != spv::ExecutionModel::MeshEXT) { uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); @@ -4304,18 +4908,20 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( const Decoration& decoration, const Instruction& inst) { const spv::BuiltIn label = decoration.builtin(); - - if (!spvIsVulkanEnv(_.context()->target_env)) { - // Early return. All currently implemented rules are based on Vulkan spec. - // - // TODO: If you are adding validation rules for environments other than - // Vulkan (or general rules which are not environment independent), then - // you need to modify or remove this condition. Consider also adding early - // returns into BuiltIn-specific rules, so that the system doesn't spawn new - // rules which don't do anything. - return SPV_SUCCESS; + // Universial checks + if (label == spv::BuiltIn::WorkgroupSize) { + return ValidateWorkgroupSizeAtDefinition(decoration, inst); } + if (spvIsVulkanEnv(_.context()->target_env)) { + return ValidateSingleBuiltInAtDefinitionVulkan(decoration, inst, label); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinitionVulkan( + const Decoration& decoration, const Instruction& inst, + const spv::BuiltIn label) { // If you are adding a new BuiltIn enum, please register it here. // If the newly added enum has validation rules associated with it // consider leaving a TODO and/or creating an issue. @@ -4407,9 +5013,6 @@ case spv::BuiltIn::VertexIndex: { return ValidateVertexIndexAtDefinition(decoration, inst); } - case spv::BuiltIn::WorkgroupSize: { - return ValidateWorkgroupSizeAtDefinition(decoration, inst); - } case spv::BuiltIn::VertexId: { return ValidateVertexIdAtDefinition(decoration, inst); } @@ -4455,27 +5058,28 @@ return ValidateFullyCoveredAtDefinition(decoration, inst); } // Ray tracing builtins - case spv::BuiltIn::HitKindKHR: // alias spv::BuiltIn::HitKindNV + case spv::BuiltIn::HitKindKHR: // alias HitKindNV case spv::BuiltIn::HitTNV: // NOT present in KHR case spv::BuiltIn::InstanceId: - case spv::BuiltIn::LaunchIdKHR: // alias spv::BuiltIn::LaunchIdNV - case spv::BuiltIn::LaunchSizeKHR: // alias spv::BuiltIn::LaunchSizeNV - case spv::BuiltIn::WorldRayOriginKHR: // alias spv::BuiltIn::WorldRayOriginNV - case spv::BuiltIn::WorldRayDirectionKHR: // alias spv::BuiltIn::WorldRayDirectionNV - case spv::BuiltIn::ObjectRayOriginKHR: // alias spv::BuiltIn::ObjectRayOriginNV - case spv::BuiltIn::ObjectRayDirectionKHR: // alias - // spv::BuiltIn::ObjectRayDirectionNV - case spv::BuiltIn::RayTminKHR: // alias spv::BuiltIn::RayTminNV - case spv::BuiltIn::RayTmaxKHR: // alias spv::BuiltIn::RayTmaxNV - case spv::BuiltIn::InstanceCustomIndexKHR: // alias - // spv::BuiltIn::InstanceCustomIndexNV - case spv::BuiltIn::ObjectToWorldKHR: // alias spv::BuiltIn::ObjectToWorldNV - case spv::BuiltIn::WorldToObjectKHR: // alias spv::BuiltIn::WorldToObjectNV - case spv::BuiltIn::IncomingRayFlagsKHR: // alias spv::BuiltIn::IncomingRayFlagsNV + case spv::BuiltIn::LaunchIdKHR: // alias LaunchIdNV + case spv::BuiltIn::LaunchSizeKHR: // alias LaunchSizeNV + case spv::BuiltIn::WorldRayOriginKHR: // alias WorldRayOriginNV + case spv::BuiltIn::WorldRayDirectionKHR: // alias WorldRayDirectionNV + case spv::BuiltIn::ObjectRayOriginKHR: // alias ObjectRayOriginNV + case spv::BuiltIn::ObjectRayDirectionKHR: // alias ObjectRayDirectionNV + case spv::BuiltIn::RayTminKHR: // alias RayTminNV + case spv::BuiltIn::RayTmaxKHR: // alias RayTmaxNV + case spv::BuiltIn::InstanceCustomIndexKHR: // alias InstanceCustomIndexNV + case spv::BuiltIn::ObjectToWorldKHR: // alias ObjectToWorldNV + case spv::BuiltIn::WorldToObjectKHR: // alias WorldToObjectNV + case spv::BuiltIn::IncomingRayFlagsKHR: // alias IncomingRayFlagsNV case spv::BuiltIn::RayGeometryIndexKHR: // NOT present in NV + case spv::BuiltIn::HitTriangleVertexPositionsKHR: case spv::BuiltIn::CullMaskKHR: { return ValidateRayTracingBuiltinsAtDefinition(decoration, inst); } + // These are only for Mesh, not Task execution model + case spv::BuiltIn::CullPrimitiveEXT: case spv::BuiltIn::PrimitivePointIndicesEXT: case spv::BuiltIn::PrimitiveLineIndicesEXT: case spv::BuiltIn::PrimitiveTriangleIndicesEXT: { @@ -4487,6 +5091,10 @@ case spv::BuiltIn::ShadingRateKHR: { return ValidateShadingRateAtDefinition(decoration, inst); } + case spv::BuiltIn::SamplerHeapEXT: + case spv::BuiltIn::ResourceHeapEXT: { + return ValidateDescriptorHeapAtDefinition(decoration, inst); + } default: // No validation rules (for the moment). break;
diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp index 81d2ad5..aae9dd9 100644 --- a/source/val/validate_capability.cpp +++ b/source/val/validate_capability.cpp
@@ -18,6 +18,7 @@ #include <string> #include "source/opcode.h" +#include "source/table2.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" @@ -67,6 +68,50 @@ return false; } +bool IsSupportGuaranteedVulkan_1_3(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_2(capability)) return true; + switch (spv::Capability(capability)) { + case spv::Capability::DotProduct: + case spv::Capability::DotProductInputAll: + case spv::Capability::DotProductInput4x8Bit: + case spv::Capability::DotProductInput4x8BitPacked: + case spv::Capability::VulkanMemoryModel: + case spv::Capability::VulkanMemoryModelDeviceScope: + case spv::Capability::PhysicalStorageBufferAddresses: + case spv::Capability::DemoteToHelperInvocation: + return true; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_4(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_3(capability)) return true; + switch (spv::Capability(capability)) { + case spv::Capability::UniformBufferArrayDynamicIndexing: + case spv::Capability::SampledImageArrayDynamicIndexing: + case spv::Capability::StorageBufferArrayDynamicIndexing: + case spv::Capability::StorageImageArrayDynamicIndexing: + case spv::Capability::Int16: + case spv::Capability::StorageBuffer16BitAccess: + case spv::Capability::VariablePointers: + case spv::Capability::VariablePointersStorageBuffer: + case spv::Capability::UniformTexelBufferArrayDynamicIndexing: + case spv::Capability::StorageTexelBufferArrayDynamicIndexing: + case spv::Capability::Int8: + case spv::Capability::StorageBuffer8BitAccess: + case spv::Capability::FloatControls2: + case spv::Capability::SampleRateShading: + case spv::Capability::StorageImageExtendedFormats: + case spv::Capability::ImageGatherExtended: + return true; + default: + break; + } + return false; +} + bool IsSupportOptionalVulkan_1_0(uint32_t capability) { switch (spv::Capability(capability)) { case spv::Capability::Geometry: @@ -100,6 +145,9 @@ case spv::Capability::GeometryStreams: case spv::Capability::Float16: case spv::Capability::Int8: + case spv::Capability::BFloat16TypeKHR: + case spv::Capability::Float8EXT: + case spv::Capability::PushConstantBanksNV: return true; default: break; @@ -164,6 +212,8 @@ case spv::Capability::InputAttachmentArrayNonUniformIndexing: case spv::Capability::UniformTexelBufferArrayNonUniformIndexing: case spv::Capability::StorageTexelBufferArrayNonUniformIndexing: + case spv::Capability::InputAttachmentArrayDynamicIndexing: + case spv::Capability::ShaderNonUniform: return true; default: break; @@ -171,6 +221,16 @@ return false; } +// Vulkan 1.3 only added required features. +bool IsSupportOptionalVulkan_1_3(uint32_t capability) { + return IsSupportOptionalVulkan_1_2(capability); +} + +// Vulkan 1.4 only added required features. +bool IsSupportOptionalVulkan_1_4(uint32_t capability) { + return IsSupportOptionalVulkan_1_3(capability); +} + bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) { switch (spv::Capability(capability)) { case spv::Capability::Addresses: @@ -221,6 +281,7 @@ switch (spv::Capability(capability)) { case spv::Capability::ImageBasic: case spv::Capability::Float64: + case spv::Capability::Float16: return true; default: break; @@ -230,16 +291,16 @@ // Checks if |capability| was enabled by extension. bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) { - spv_operand_desc operand_desc = nullptr; - _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, - &operand_desc); + const spvtools::OperandDesc* operand_desc = nullptr; + spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &operand_desc); // operand_desc is expected to be not null, otherwise validator would have // failed at an earlier stage. This 'assert' is 'just in case'. assert(operand_desc); - ExtensionSet operand_exts(operand_desc->numExtensions, - operand_desc->extensions); + ExtensionSet operand_exts(operand_desc->extensions_range.count(), + operand_desc->extensions().data()); if (operand_exts.empty()) return false; return _.HasAnyOfExtensions(operand_exts); @@ -287,24 +348,31 @@ // Validates that capability declarations use operands allowed in the current // context. spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) { - if (inst->opcode() != spv::Op::OpCapability) return SPV_SUCCESS; + if (inst->opcode() != spv::Op::OpCapability && + inst->opcode() != spv::Op::OpConditionalCapabilityINTEL) + return SPV_SUCCESS; - assert(inst->operands().size() == 1); + assert(!((inst->opcode() == spv::Op::OpCapability) ^ + (inst->operands().size() == 1))); + assert(!((inst->opcode() == spv::Op::OpConditionalCapabilityINTEL) ^ + (inst->operands().size() == 2))); - const spv_parsed_operand_t& operand = inst->operand(0); + const uint32_t i_cap = + inst->opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0; + const spv_parsed_operand_t& operand = inst->operand(i_cap); assert(operand.num_words == 1); assert(operand.offset < inst->words().size()); const uint32_t capability = inst->word(operand.offset); - const auto capability_str = [&_, capability]() { - spv_operand_desc desc = nullptr; - if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, - &desc) != SPV_SUCCESS || + const auto capability_str = [capability]() { + const spvtools::OperandDesc* desc = nullptr; + if (spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &desc) != SPV_SUCCESS || !desc) { return std::string("Unknown"); } - return std::string(desc->name); + return std::string(desc->name().data()); }; const auto env = _.context()->target_env; @@ -340,6 +408,24 @@ << " is not allowed by Vulkan 1.2 specification" << " (or requires extension)"; } + } else if (env == SPV_ENV_VULKAN_1_3) { + if (!IsSupportGuaranteedVulkan_1_3(capability) && + !IsSupportOptionalVulkan_1_3(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.3 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_4) { + if (!IsSupportGuaranteedVulkan_1_4(capability) && + !IsSupportOptionalVulkan_1_4(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.4 specification" + << " (or requires extension)"; + } } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) { if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) && !IsSupportOptionalOpenCL_1_2(capability) &&
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index df127f9..57681c2 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp
@@ -353,6 +353,34 @@ return SPV_SUCCESS; } +spv_result_t ValidateLifetime(ValidationState_t& _, const Instruction* inst) { + const uint32_t pointer_id = _.GetOperandTypeId(inst, 0); + const Instruction* pointer_inst = _.FindDef(pointer_id); + if (pointer_inst->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Op" << spvOpcodeString(inst->opcode()) + << " pointer operand type must be a OpTypePointer."; + } else if (pointer_inst->GetOperandAs<spv::StorageClass>(1) != + spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Op" << spvOpcodeString(inst->opcode()) + << " pointer operand must be in the Function storage class."; + } + + const uint32_t size = inst->GetOperandAs<uint32_t>(1); + if (size != 0) { + if (!_.HasCapability(spv::Capability::Addresses)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Op" << spvOpcodeString(inst->opcode()) + << " size is non-zero, but the Addresses Capability is not " + "declared."; + } + // TODO - "Size must be 0 if Pointer is a pointer to a non-void type" + } + + return SPV_SUCCESS; +} + } // namespace void printDominatorList(const BasicBlock& b) { @@ -1178,6 +1206,7 @@ case spv::Op::OpIgnoreIntersectionKHR: case spv::Op::OpTerminateRayKHR: case spv::Op::OpEmitMeshTasksEXT: + case spv::Op::OpAbortKHR: _.current_function().RegisterBlockEnd(std::vector<uint32_t>()); // Ops with dedicated passes check for the Execution Model there if (opcode == spv::Op::OpKill) { @@ -1269,6 +1298,10 @@ case spv::Op::OpLoopMerge: if (auto error = ValidateLoopMerge(_, inst)) return error; break; + case spv::Op::OpLifetimeStart: + case spv::Op::OpLifetimeStop: + if (auto error = ValidateLifetime(_, inst)) return error; + break; default: break; }
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp index 26486da..d656d61 100644 --- a/source/val/validate_composites.cpp +++ b/source/val/validate_composites.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,6 +16,9 @@ // Validates correctness of composite SPIR-V instructions. +#include <climits> +#include <cstdint> + #include "source/opcode.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" @@ -32,14 +37,11 @@ // deep). spv_result_t GetExtractInsertValueType(ValidationState_t& _, const Instruction* inst, - uint32_t* member_type) { - const spv::Op opcode = inst->opcode(); - assert(opcode == spv::Op::OpCompositeExtract || - opcode == spv::Op::OpCompositeInsert); - uint32_t word_index = opcode == spv::Op::OpCompositeExtract ? 4 : 5; - const uint32_t num_words = static_cast<uint32_t>(inst->words().size()); - const uint32_t composite_id_index = word_index - 1; - const uint32_t num_indices = num_words - word_index; + uint32_t* member_type, + uint32_t composite_id_index) { + const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size()); + const uint32_t first_literal_index = composite_id_index + 1; + const uint32_t num_indices = num_operands - first_literal_index; const uint32_t kCompositeExtractInsertMaxNumIndices = 255; if (num_indices == 0) { @@ -49,19 +51,21 @@ } else if (num_indices > kCompositeExtractInsertMaxNumIndices) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The number of indexes in Op" << spvOpcodeString(opcode) + << "The number of indexes in Op" << spvOpcodeString(inst->opcode()) << " may not exceed " << kCompositeExtractInsertMaxNumIndices << ". Found " << num_indices << " indexes."; } - *member_type = _.GetTypeId(inst->word(composite_id_index)); + *member_type = _.GetOperandTypeId(inst, composite_id_index); if (*member_type == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Composite to be an object of composite type"; } - for (; word_index < num_words; ++word_index) { - const uint32_t component_index = inst->word(word_index); + for (uint32_t operand_index = first_literal_index; + operand_index < num_operands; ++operand_index) { + const uint32_t component_index = + inst->GetOperandAs<uint32_t>(operand_index); const Instruction* const type_inst = _.FindDef(*member_type); assert(type_inst); switch (type_inst->opcode()) { @@ -104,7 +108,8 @@ } break; } - case spv::Op::OpTypeRuntimeArray: { + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeNodePayloadArrayAMDX: { *member_type = type_inst->word(2); // Array size is unknown. break; @@ -122,6 +127,7 @@ *member_type = type_inst->word(component_index + 2); break; } + case spv::Op::OpTypeVectorIdEXT: case spv::Op::OpTypeCooperativeMatrixKHR: case spv::Op::OpTypeCooperativeMatrixNV: { *member_type = type_inst->word(2); @@ -148,7 +154,8 @@ const uint32_t vector_type = _.GetOperandTypeId(inst, 2); const spv::Op vector_opcode = _.GetIdOpcode(vector_type); - if (vector_opcode != spv::Op::OpTypeVector) { + if (vector_opcode != spv::Op::OpTypeVector && + vector_opcode != spv::Op::OpTypeVectorIdEXT) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Vector type to be OpTypeVector"; } @@ -176,7 +183,8 @@ const Instruction* inst) { const uint32_t result_type = inst->type_id(); const spv::Op result_opcode = _.GetIdOpcode(result_type); - if (result_opcode != spv::Op::OpTypeVector) { + if (result_opcode != spv::Op::OpTypeVector && + result_opcode != spv::Op::OpTypeVectorIdEXT) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be OpTypeVector"; } @@ -214,14 +222,25 @@ const uint32_t result_type = inst->type_id(); const spv::Op result_opcode = _.GetIdOpcode(result_type); switch (result_opcode) { - case spv::Op::OpTypeVector: { - const uint32_t num_result_components = _.GetDimension(result_type); + case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: { + uint32_t num_result_components = _.GetDimension(result_type); const uint32_t result_component_type = _.GetComponentType(result_type); uint32_t given_component_count = 0; - if (num_operands <= 3) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected number of constituents to be at least 2"; + bool comp_is_int32 = true, comp_is_const_int32 = true; + + if (result_opcode == spv::Op::OpTypeVector) { + if (num_operands <= 3 && + !_.HasCapability(spv::Capability::LongVectorEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of constituents to be at least 2"; + } + } else { + uint32_t comp_count_id = + _.FindDef(result_type)->GetOperandAs<uint32_t>(2); + std::tie(comp_is_int32, comp_is_const_int32, num_result_components) = + _.EvalInt32IfConst(comp_count_id); } for (uint32_t operand_index = 2; operand_index < num_operands; @@ -241,7 +260,8 @@ } } - if (num_result_components != given_component_count) { + if (comp_is_const_int32 && + num_result_components != given_component_count) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected total number of given components to be equal " << "to the size of Result Type vector"; @@ -389,10 +409,74 @@ return SPV_SUCCESS; } +spv_result_t ValidateCompositeConstructReplicate(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + + switch (result_type->opcode()) { + case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeMatrixNV: { + const auto element_type = result_type->GetOperandAs<uint32_t>(1); + if (operand_type != element_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Value type to be equal to the " + << "result's element type"; + } + break; + } + case spv::Op::OpTypeStruct: { + for (uint32_t operand_index = 1; + operand_index < result_type->operands().size(); ++operand_index) { + const uint32_t member_type = + result_type->GetOperandAs<uint32_t>(operand_index); + if (operand_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Value type to be equal to the " + << "corresponding member type of the result"; + } + } + break; + } + case spv::Op::OpTypeTensorARM: { + const uint32_t component_type = result_type->GetOperandAs<uint32_t>(1); + if (operand_type != component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Value type to be equal to the result's element " + "type"; + } + if (result_type->operands().size() <= 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result tensor type is not a composite type because it lacks " + "a shape operand"; + } + break; + } + default: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a composite type"; + } + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot create a composite containing 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + spv_result_t ValidateCompositeExtract(ValidationState_t& _, - const Instruction* inst) { + const Instruction* inst, + uint32_t operand_index = 2) { uint32_t member_type = 0; - if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { + + if (spv_result_t error = + GetExtractInsertValueType(_, inst, &member_type, operand_index)) { return error; } @@ -415,9 +499,10 @@ } spv_result_t ValidateCompositeInsert(ValidationState_t& _, - const Instruction* inst) { - const uint32_t object_type = _.GetOperandTypeId(inst, 2); - const uint32_t composite_type = _.GetOperandTypeId(inst, 3); + const Instruction* inst, + uint32_t operand_index = 2) { + const uint32_t object_type = _.GetOperandTypeId(inst, operand_index); + const uint32_t composite_type = _.GetOperandTypeId(inst, operand_index + 1); const uint32_t result_type = inst->type_id(); if (result_type != composite_type) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -427,7 +512,8 @@ } uint32_t member_type = 0; - if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { + if (spv_result_t error = + GetExtractInsertValueType(_, inst, &member_type, operand_index + 1)) { return error; } @@ -508,59 +594,58 @@ } spv_result_t ValidateVectorShuffle(ValidationState_t& _, - const Instruction* inst) { - auto resultType = _.FindDef(inst->type_id()); - if (!resultType || resultType->opcode() != spv::Op::OpTypeVector) { + const Instruction* inst, + uint32_t operand_index = 2) { + auto result_type = _.FindDef(inst->type_id()); + if (!_.IsVectorType(result_type->id())) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "The Result Type of OpVectorShuffle must be" - << " OpTypeVector. Found Op" - << spvOpcodeString(static_cast<spv::Op>(resultType->opcode())) - << "."; + << " a vector type. Found Op" + << spvOpcodeString(result_type->opcode()) << "."; } // The number of components in Result Type must be the same as the number of // Component operands. - auto componentCount = inst->operands().size() - 4; - auto resultVectorDimension = resultType->GetOperandAs<uint32_t>(2); - if (componentCount != resultVectorDimension) { + uint32_t first_literal_index = operand_index + 2; + uint32_t component_count = + static_cast<uint32_t>(inst->operands().size()) - first_literal_index; + auto result_vec_dimension = _.GetDimension(result_type->id()); + if (result_vec_dimension > 0 && component_count != result_vec_dimension) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpVectorShuffle component literals count does not match " "Result Type <id> " - << _.getIdName(resultType->id()) << "s vector component count."; + << _.getIdName(result_type->id()) << "s vector component count."; } // Vector 1 and Vector 2 must both have vector types, with the same Component // Type as Result Type. - auto vector1Object = _.FindDef(inst->GetOperandAs<uint32_t>(2)); - auto vector1Type = _.FindDef(vector1Object->type_id()); - auto vector2Object = _.FindDef(inst->GetOperandAs<uint32_t>(3)); - auto vector2Type = _.FindDef(vector2Object->type_id()); - if (!vector1Type || vector1Type->opcode() != spv::Op::OpTypeVector) { + auto vec1_type = _.FindDef(_.GetOperandTypeId(inst, operand_index)); + auto vec2_type = _.FindDef(_.GetOperandTypeId(inst, operand_index + 1)); + if (!vec1_type || !_.IsVectorType(vec1_type->id())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The type of Vector 1 must be OpTypeVector."; + << "The type of Vector 1 must be a vector type."; } - if (!vector2Type || vector2Type->opcode() != spv::Op::OpTypeVector) { + if (!vec2_type || !_.IsVectorType(vec2_type->id())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The type of Vector 2 must be OpTypeVector."; + << "The type of Vector 2 must be a vector type."; } - auto resultComponentType = resultType->GetOperandAs<uint32_t>(1); - if (vector1Type->GetOperandAs<uint32_t>(1) != resultComponentType) { + uint32_t result_component_type = result_type->GetOperandAs<uint32_t>(1); + if (vec1_type->GetOperandAs<uint32_t>(1) != result_component_type) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "The Component Type of Vector 1 must be the same as ResultType."; } - if (vector2Type->GetOperandAs<uint32_t>(1) != resultComponentType) { + if (vec2_type->GetOperandAs<uint32_t>(1) != result_component_type) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "The Component Type of Vector 2 must be the same as ResultType."; } // All Component literals must either be FFFFFFFF or in [0, N - 1]. - auto vector1ComponentCount = vector1Type->GetOperandAs<uint32_t>(2); - auto vector2ComponentCount = vector2Type->GetOperandAs<uint32_t>(2); - auto N = vector1ComponentCount + vector2ComponentCount; - auto firstLiteralIndex = 4; - for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) { - auto literal = inst->GetOperandAs<uint32_t>(i); + uint32_t vec1_component_count = vec1_type->GetOperandAs<uint32_t>(2); + uint32_t vec2_component_count = vec2_type->GetOperandAs<uint32_t>(2); + uint32_t N = vec1_component_count + vec2_component_count; + for (size_t i = first_literal_index; i < inst->operands().size(); ++i) { + uint32_t literal = inst->GetOperandAs<uint32_t>(i); if (literal != 0xFFFFFFFF && literal >= N) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Component index " << literal << " is out of bounds for " @@ -601,8 +686,464 @@ return SPV_SUCCESS; } -} // anonymous namespace +spv_result_t ValidateCompositeConstructCoopMatQCOM(ValidationState_t& _, + const Instruction* inst) { + // Is the result of coop mat ? + const auto result_type_inst = _.FindDef(inst->type_id()); + if (!result_type_inst || + result_type_inst->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the result type be OpTypeCooperativeMatrixKHR"; + } + const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); + const auto source_type_inst = _.FindDef(source->type_id()); + + if (!source_type_inst || source_type_inst->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the input operand be an OpTypeArray."; + } + + // Is the scope Subgrouop ? + { + unsigned scope = UINT_MAX; + unsigned scope_id = result_type_inst->GetOperandAs<unsigned>(2u); + bool status = _.GetConstantValueAs<unsigned>(scope_id, scope); + bool is_scope_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(scope_id)->opcode()); + if (!is_scope_spec_const && + (!status || scope != static_cast<uint64_t>(spv::Scope::Subgroup))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the result type's scope be Subgroup."; + } + } + + unsigned ar_len = UINT_MAX; + unsigned src_arr_len_id = source_type_inst->GetOperandAs<unsigned>(2u); + bool ar_len_status = _.GetConstantValueAs<unsigned>(src_arr_len_id, ar_len); + bool is_src_arr_len_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(src_arr_len_id)->opcode()); + + const auto source_elt_type = _.GetComponentType(source_type_inst->id()); + const auto result_elt_type = result_type_inst->GetOperandAs<uint32_t>(1u); + + if ((source_elt_type != result_elt_type) && + !(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(source_elt_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires ether the input element type is equal to the result " + "element type or it is the unsigned 32-bit integer."; + } + + unsigned res_row_id = result_type_inst->GetOperandAs<unsigned>(3u); + unsigned res_col_id = result_type_inst->GetOperandAs<unsigned>(4u); + unsigned res_use_id = result_type_inst->GetOperandAs<unsigned>(5u); + + unsigned cm_use = UINT_MAX; + bool cm_use_status = _.GetConstantValueAs<unsigned>(res_use_id, cm_use); + + switch (static_cast<spv::CooperativeMatrixUse>(cm_use)) { + case spv::CooperativeMatrixUse::MatrixAKHR: { + // result coopmat component type check + if (!_.IsIntNOrFP32OrFP16<8>(result_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the result element type is one of 8-bit OpTypeInt " + "signed/unsigned, 16- or 32-bit OpTypeFloat" + << " when result coopmat's use is MatrixAKHR"; + } + + // result coopmat column length check + unsigned n_cols = UINT_MAX; + bool status = _.GetConstantValueAs<unsigned>(res_col_id, n_cols); + bool is_res_col_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(res_col_id)->opcode()); + if (!is_res_col_spec_const && + (!status || (!(_.ContainsSizedIntOrFloatType(result_elt_type, + spv::Op::OpTypeInt, 8) && + n_cols == 32) && + !(_.ContainsSizedIntOrFloatType( + result_elt_type, spv::Op::OpTypeFloat, 16) && + n_cols == 16) && + !(_.ContainsSizedIntOrFloatType( + result_elt_type, spv::Op::OpTypeFloat, 32) && + n_cols == 8)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the columns of the result coopmat have the bit " + "length of 256" + << " when result coopmat's use is MatrixAKHR"; + } + // source array length check + if (!is_src_arr_len_spec_const && + (!ar_len_status || + (!(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(source_elt_type) && (ar_len == 8)) && + !(n_cols == ar_len)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source array length be 8 if its elt type is " + "32-bit unsigned OpTypeInt and be the result's number of " + "columns, otherwise" + << " when result coopmat's use is MatrixAKHR"; + } + break; + } + case spv::CooperativeMatrixUse::MatrixBKHR: { + // result coopmat component type check + if (!_.IsIntNOrFP32OrFP16<8>(result_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the result element type is one of 8-bit OpTypeInt " + "signed/unsigned, 16- or 32-bit OpTypeFloat" + << " when result coopmat's use is MatrixBKHR"; + } + + // result coopmat row length check + unsigned n_rows = UINT_MAX; + bool status = _.GetConstantValueAs<unsigned>(res_row_id, n_rows); + bool is_res_row_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(res_row_id)->opcode()); + if (!is_res_row_spec_const && + (!status || (!(_.ContainsSizedIntOrFloatType(result_elt_type, + spv::Op::OpTypeInt, 8) && + n_rows == 32) && + !(_.ContainsSizedIntOrFloatType( + result_elt_type, spv::Op::OpTypeFloat, 16) && + n_rows == 16) && + !(_.ContainsSizedIntOrFloatType( + result_elt_type, spv::Op::OpTypeFloat, 32) && + n_rows == 8)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the rows of the result operand have the bit " + "length of 256" + << " when result coopmat's use is MatrixBKHR"; + } + // source array length check + if (!is_src_arr_len_spec_const && + (!ar_len_status || + (!(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(source_elt_type) && (ar_len == 8)) && + !(n_rows == ar_len)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source array length be 8 if its elt type is " + "32-bit unsigned OpTypeInt and be the result's number of " + "rows, otherwise" + << " when result coopmat's use is MatrixBKHR"; + } + break; + } + case spv::CooperativeMatrixUse::MatrixAccumulatorKHR: { + // result coopmat component type check + if (!_.IsIntNOrFP32OrFP16<32>(result_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the result element type is one of 32-bit " + "OpTypeInt signed/unsigned, 16- or 32-bit OpTypeFloat" + << " when result coopmat's use is MatrixAccumulatorKHR"; + } + + // source array length check + unsigned n_cols = UINT_MAX; + bool status = _.GetConstantValueAs<unsigned>(res_col_id, n_cols); + bool is_res_col_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(res_col_id)->opcode()); + if (!is_res_col_spec_const && !is_src_arr_len_spec_const && + (!status || !ar_len_status || + (!(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(source_elt_type) && + (_.ContainsSizedIntOrFloatType(result_elt_type, + spv::Op::OpTypeFloat, 16) + ? (n_cols / 2 == ar_len) + : n_cols == ar_len)) && + (n_cols != ar_len)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source array length be a half of the number " + "of columns of the resulting cooerative matrix if the " + "matrix's componet type is 16-bit OpTypeFloat and be equal " + "to the number of columns, otherwise," + << " when result coopmat's use is MatrixAccumulatorKHR"; + } + break; + } + default: { + bool is_cm_use_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(res_use_id)->opcode()); + if (!is_cm_use_spec_const || !cm_use_status) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the the resulting cooerative matrix's use be " + << " one of MatrixAKHR (== 0), MatrixBKHR (== 1), and " + "MatrixAccumulatorKHR (== 2)"; + } + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeExtractCoopMatQCOM(ValidationState_t& _, + const Instruction* inst) { + const auto result_type_inst = _.FindDef(inst->type_id()); + if (!result_type_inst || result_type_inst->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the input operand be an OpTypeArray."; + } + + const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); + const auto source_type_inst = _.FindDef(source->type_id()); + + // Is the source of coop mat ? + if (!source_type_inst || + source_type_inst->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source type be OpTypeCooperativeMatrixKHR"; + } + + // Is the scope Subgrouop ? + { + unsigned scope = UINT_MAX; + unsigned scope_id = source_type_inst->GetOperandAs<unsigned>(2u); + bool status = _.GetConstantValueAs<unsigned>(scope_id, scope); + bool is_scope_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(scope_id)->opcode()); + if (!is_scope_spec_const && + (!status || scope != static_cast<uint64_t>(spv::Scope::Subgroup))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source type's scope be Subgroup."; + } + } + + unsigned ar_len = UINT_MAX; + unsigned res_arr_len_id = result_type_inst->GetOperandAs<unsigned>(2u); + bool ar_len_status = _.GetConstantValueAs<unsigned>(res_arr_len_id, ar_len); + bool is_res_arr_len_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(res_arr_len_id)->opcode()); + + const auto source_elt_type = _.GetComponentType(source_type_inst->id()); + const auto result_elt_type = result_type_inst->GetOperandAs<uint32_t>(1u); + + unsigned src_row_id = source_type_inst->GetOperandAs<unsigned>(3u); + unsigned src_col_id = source_type_inst->GetOperandAs<unsigned>(4u); + unsigned src_use_id = source_type_inst->GetOperandAs<unsigned>(5u); + + unsigned cm_use = UINT_MAX; + bool cm_use_status = _.GetConstantValueAs<unsigned>(src_use_id, cm_use); + + switch (static_cast<spv::CooperativeMatrixUse>(cm_use)) { + case spv::CooperativeMatrixUse::MatrixAKHR: { + // source coopmat component type check + if (!_.IsIntNOrFP32OrFP16<8>(source_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source element type be one of 8-bit OpTypeInt " + "signed/unsigned, 16- or 32-bit OpTypeFloat" + << " when source coopmat's use is MatrixAKHR"; + } + + // source coopmat column length check + unsigned n_cols = UINT_MAX; + bool status = _.GetConstantValueAs<unsigned>(src_col_id, n_cols); + bool is_src_col_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(src_col_id)->opcode()); + if (!is_src_col_spec_const && + (!status || (!(_.ContainsSizedIntOrFloatType(source_elt_type, + spv::Op::OpTypeInt, 8) && + n_cols == 32) && + !(_.ContainsSizedIntOrFloatType( + source_elt_type, spv::Op::OpTypeFloat, 16) && + n_cols == 16) && + !(_.ContainsSizedIntOrFloatType( + source_elt_type, spv::Op::OpTypeFloat, 32) && + n_cols == 8)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the columns of the source coopmat have the bit " + "length of 256" + << " when source coopmat's use is MatrixAKHR"; + } + // result type check + if (!is_res_arr_len_spec_const && + !(source_elt_type == result_elt_type && (n_cols == ar_len)) && + !(_.ContainsSizedIntOrFloatType(result_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(result_elt_type) && (ar_len == 8))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires either the result element type be the same as the " + "source cooperative matrix's component type" + << " and its length be the same as the number of columns of the " + "matrix or the result element type be" + << " unsigned 32-bit OpTypeInt and the length be 8" + << " when source coopmat's use is MatrixAKHR"; + } + break; + } + case spv::CooperativeMatrixUse::MatrixBKHR: { + // source coopmat component type check + if (!_.IsIntNOrFP32OrFP16<8>(source_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source element type be one of 8-bit OpTypeInt " + "signed/unsigned, 16- or 32-bit OpTypeFloat" + << " when source coopmat's use is MatrixBKHR"; + } + + // source coopmat row length check + unsigned n_rows = UINT_MAX; + bool status = _.GetConstantValueAs<unsigned>(src_row_id, n_rows); + bool is_src_row_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(src_row_id)->opcode()); + if (!is_src_row_spec_const && + (!status || (!(_.ContainsSizedIntOrFloatType(source_elt_type, + spv::Op::OpTypeInt, 8) && + n_rows == 32) && + !(_.ContainsSizedIntOrFloatType( + source_elt_type, spv::Op::OpTypeFloat, 16) && + n_rows == 16) && + !(_.ContainsSizedIntOrFloatType( + source_elt_type, spv::Op::OpTypeFloat, 32) && + n_rows == 8)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the rows of the source coopmat have the bit " + "length of 256" + << " when source coopmat's use is MatrixBKHR"; + } + // result type check + if (!is_res_arr_len_spec_const && + !(source_elt_type == result_elt_type && (n_rows == ar_len)) && + !(_.ContainsSizedIntOrFloatType(result_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(result_elt_type) && (ar_len == 8))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires either the result element type be the same as the " + "source cooperative matrix's component type" + << " and its length be the same as the number of rows of the " + "matrix or the result element type be" + << " unsigned 32-bit OpTypeInt and the length be 8" + << " when source coopmat's use is MatrixBKHR"; + } + break; + } + case spv::CooperativeMatrixUse::MatrixAccumulatorKHR: { + // source coopmat component type check + if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the source element type be one of 32-bit " + "OpTypeInt signed/unsigned, 16- or 32-bit OpTypeFloat" + << " when source coopmat's use is MatrixAccumulatorKHR"; + } + + // result type check + unsigned n_cols = UINT_MAX; + bool status = _.GetConstantValueAs<unsigned>(src_col_id, n_cols); + bool is_src_col_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(src_col_id)->opcode()); + if (!is_src_col_spec_const && !is_res_arr_len_spec_const && + (!status || !ar_len_status || + (!(source_elt_type == result_elt_type && (n_cols == ar_len)) && + !(_.ContainsSizedIntOrFloatType(result_elt_type, spv::Op::OpTypeInt, + 32) && + _.IsUnsignedIntScalarType(result_elt_type) && + (_.ContainsSizedIntOrFloatType(source_elt_type, + spv::Op::OpTypeFloat, 16) + ? (n_cols / 2 == ar_len) + : (n_cols == ar_len)))))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires either the result element type be the same as the " + "source cooperative matrix's component type" + << " and its length be the same as the number of columns of the " + "matrix or the result element type be" + << " unsigned 32-bit OpTypeInt and the length be the number of " + "the columns of the matrix if its component" + << " type is 32-bit OpTypeFloat and be a half of the number of " + "the columns of the matrix if its component" + << " type is 16-bit OpTypeFloat" + << " when source coopmat's use is MatrixAccumulatorKHR"; + } + break; + } + default: { + bool is_cm_use_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(src_use_id)->opcode()); + if (!is_cm_use_spec_const || !cm_use_status) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the the source cooerative matrix's use be " + << " one of MatrixAKHR (== 0), MatrixBKHR (== 1), and " + "MatrixAccumulatorKHR (== 2)"; + } + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtractSubArrayQCOM(ValidationState_t& _, + const Instruction* inst) { + const auto result_type_inst = _.FindDef(inst->type_id()); + const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); + const auto source_type_inst = _.FindDef(source->type_id()); + + // Are the input and the result arrays? + if (result_type_inst->opcode() != spv::Op::OpTypeArray || + source_type_inst->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires OpTypeArray operands for the input and the result."; + } + + const auto source_elt_type = _.GetComponentType(source_type_inst->id()); + const auto result_elt_type = _.GetComponentType(result_type_inst->id()); + + // Do the input and result element types match? + if (source_elt_type != result_elt_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the input and result element types match."; + } + + // Elt type must be one of int32_t/uint32_t/float32/float16 + if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the element type be one of 32-bit OpTypeInt " + "(signed/unsigned), 32-bit OpTypeFloat and 16-bit OpTypeFloat"; + } + + const auto start_index = _.FindDef(inst->GetOperandAs<uint32_t>(3u)); + if (!start_index || !_.ContainsSizedIntOrFloatType(start_index->type_id(), + spv::Op::OpTypeInt, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(inst->opcode()) + << " requires the type of the start index operand be 32-bit " + "OpTypeInt"; + } + + return SPV_SUCCESS; +} + +} // anonymous namespace // Validates correctness of composite instructions. spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) { switch (inst->opcode()) { @@ -614,6 +1155,8 @@ return ValidateVectorShuffle(_, inst); case spv::Op::OpCompositeConstruct: return ValidateCompositeConstruct(_, inst); + case spv::Op::OpCompositeConstructReplicateEXT: + return ValidateCompositeConstructReplicate(_, inst); case spv::Op::OpCompositeExtract: return ValidateCompositeExtract(_, inst); case spv::Op::OpCompositeInsert: @@ -624,6 +1167,26 @@ return ValidateTranspose(_, inst); case spv::Op::OpCopyLogical: return ValidateCopyLogical(_, inst); + case spv::Op::OpCompositeConstructCoopMatQCOM: + return ValidateCompositeConstructCoopMatQCOM(_, inst); + case spv::Op::OpCompositeExtractCoopMatQCOM: + return ValidateCompositeExtractCoopMatQCOM(_, inst); + case spv::Op::OpExtractSubArrayQCOM: + return ValidateExtractSubArrayQCOM(_, inst); + + case spv::Op::OpSpecConstantOp: { + switch (inst->GetOperandAs<spv::Op>(2u)) { + case spv::Op::OpVectorShuffle: + return ValidateVectorShuffle(_, inst, 3); + case spv::Op::OpCompositeExtract: + return ValidateCompositeExtract(_, inst, 3); + case spv::Op::OpCompositeInsert: + return ValidateCompositeInsert(_, inst, 3); + default: + break; + } + } + default: break; }
diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp index 1d40eed..54941e2 100644 --- a/source/val/validate_constants.cpp +++ b/source/val/validate_constants.cpp
@@ -33,22 +33,62 @@ return SPV_SUCCESS; } +bool isCompositeType(const Instruction* inst) { + bool is_tensor = inst->opcode() == spv::Op::OpTypeTensorARM; + bool tensor_is_shaped = inst->words().size() == 5; + return spvOpcodeIsComposite(inst->opcode()) || + (is_tensor && tensor_is_shaped); +} + +spv_result_t ValidateConstantOperand(ValidationState_t& _, + const Instruction* inst, size_t operand) { + std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); + + const auto operand_id = inst->GetOperandAs<uint32_t>(operand); + const bool inst_is_spec_constant = spvOpcodeIsSpecConstant(inst->opcode()); + const auto operand_opcode = _.GetIdOpcode(operand_id); + const bool is_constant = spvOpcodeIsConstantOrUndef(operand_opcode); + const bool is_spec_constant = spvOpcodeIsSpecConstant(operand_opcode); + if (!is_constant) { + // All operands must be constant or undef. + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " must only have constant or undef operands: <id> " + << _.getIdName(operand_id); + } else if (!inst_is_spec_constant && is_spec_constant) { + // Spec constants are only allowed for spec constant opcodes. + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " must not have spec constant operands: <id> " + << _.getIdName(operand_id); + } + + return SPV_SUCCESS; +} + spv_result_t ValidateConstantComposite(ValidationState_t& _, const Instruction* inst) { std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); const auto result_type = _.FindDef(inst->type_id()); - if (!result_type || !spvOpcodeIsComposite(result_type->opcode())) { + if (!result_type || !isCompositeType(result_type)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Result Type <id> " << _.getIdName(inst->type_id()) << " is not a composite type."; } - const auto constituent_count = inst->words().size() - 3; + const auto constituent_count = inst->operands().size() - 2; switch (result_type->opcode()) { - case spv::Op::OpTypeVector: { - const auto component_count = result_type->GetOperandAs<uint32_t>(2); - if (component_count != constituent_count) { + case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: { + uint32_t num_result_components = _.GetDimension(result_type->id()); + bool comp_is_int32 = true, comp_is_const_int32 = true; + + if (result_type->opcode() == spv::Op::OpTypeVectorIdEXT) { + uint32_t comp_count_id = result_type->GetOperandAs<uint32_t>(2); + std::tie(comp_is_int32, comp_is_const_int32, num_result_components) = + _.EvalInt32IfConst(comp_count_id); + } + + if (comp_is_const_int32 && num_result_components != constituent_count) { // TODO: Output ID's on diagnostic return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name @@ -67,13 +107,6 @@ const auto constituent_id = inst->GetOperandAs<uint32_t>(constituent_index); const auto constituent = _.FindDef(constituent_id); - if (!constituent || - !spvOpcodeIsConstantOrUndef(constituent->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent <id> " - << _.getIdName(constituent_id) - << " is not a constant or undef."; - } const auto constituent_result_type = _.FindDef(constituent->type_id()); if (!constituent_result_type || component_type->id() != constituent_result_type->id()) { @@ -96,7 +129,8 @@ << _.getIdName(result_type->id()) << "s matrix column count."; } - const auto column_type = _.FindDef(result_type->words()[2]); + const auto column_type = + _.FindDef(result_type->GetOperandAs<uint32_t>(1)); if (!column_type) { return _.diag(SPV_ERROR_INVALID_ID, result_type) << "Column type is not defined."; @@ -114,15 +148,6 @@ const auto constituent_id = inst->GetOperandAs<uint32_t>(constituent_index); const auto constituent = _.FindDef(constituent_id); - if (!constituent || - !spvOpcodeIsConstantOrUndef(constituent->opcode())) { - // The message says "... or undef" because the spec does not say - // undef is a constant. - return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent <id> " - << _.getIdName(constituent_id) - << " is not a constant or undef."; - } const auto vector = _.FindDef(constituent->type_id()); if (!vector) { return _.diag(SPV_ERROR_INVALID_ID, constituent) @@ -145,7 +170,7 @@ << _.getIdName(result_type->id()) << "s matrix column component type."; } - if (component_count != vector->words()[3]) { + if (component_count != vector->GetOperandAs<uint32_t>(2)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Constituent <id> " << _.getIdName(constituent_id) @@ -182,13 +207,6 @@ const auto constituent_id = inst->GetOperandAs<uint32_t>(constituent_index); const auto constituent = _.FindDef(constituent_id); - if (!constituent || - !spvOpcodeIsConstantOrUndef(constituent->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent <id> " - << _.getIdName(constituent_id) - << " is not a constant or undef."; - } const auto constituent_type = _.FindDef(constituent->type_id()); if (!constituent_type) { return _.diag(SPV_ERROR_INVALID_ID, constituent) @@ -204,7 +222,7 @@ } } break; case spv::Op::OpTypeStruct: { - const auto member_count = result_type->words().size() - 2; + const auto member_count = result_type->operands().size() - 1; if (member_count != constituent_count) { return _.diag(SPV_ERROR_INVALID_ID, inst) << opcode_name << " Constituent <id> " @@ -218,13 +236,6 @@ const auto constituent_id = inst->GetOperandAs<uint32_t>(constituent_index); const auto constituent = _.FindDef(constituent_id); - if (!constituent || - !spvOpcodeIsConstantOrUndef(constituent->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent <id> " - << _.getIdName(constituent_id) - << " is not a constant or undef."; - } const auto constituent_type = _.FindDef(constituent->type_id()); if (!constituent_type) { return _.diag(SPV_ERROR_INVALID_ID, constituent) @@ -252,11 +263,6 @@ } const auto constituent_id = inst->GetOperandAs<uint32_t>(2); const auto constituent = _.FindDef(constituent_id); - if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << opcode_name << " Constituent <id> " - << _.getIdName(constituent_id) << " is not a constant or undef."; - } const auto constituent_type = _.FindDef(constituent->type_id()); if (!constituent_type) { return _.diag(SPV_ERROR_INVALID_ID, constituent) @@ -273,12 +279,201 @@ << _.getIdName(result_type->id()) << "s component type."; } } break; + case spv::Op::OpTypeTensorARM: { + auto inst_element_type = + _.FindDef(result_type->GetOperandAs<uint32_t>(1)); + if (!inst_element_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Element type is not defined."; + } + const auto inst_rank = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); + if (!inst_rank) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Rank is not defined."; + } + const auto inst_shape = _.FindDef(result_type->GetOperandAs<uint32_t>(3)); + if (!inst_shape) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Shape is not defined."; + } + + uint64_t rank = 0; + _.EvalConstantValUint64(inst_rank->id(), &rank); + + uint64_t outermost_shape = 0; + if (_.EvalConstantValUint64(inst_shape->GetOperandAs<uint32_t>(2), + &outermost_shape) && + (outermost_shape != constituent_count)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "the shape of Result Type <id> " + << _.getIdName(result_type->id()) + << " along its outermost dimension, " << "expected " + << outermost_shape << " but got " << constituent_count << "."; + } + + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs<uint32_t>(constituent_index); + const auto constituent = _.FindDef(constituent_id); + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Type of Constituent " << constituent_index - 2 + << " is not defined."; + } + + if (rank == 0) { + // The rank of the returned tensor constant is not known. + // Skip rank-dependent validation. + continue; + } + + if (rank == 1) { + if (inst_element_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << " type does not match the element type of the tensor (" + << _.getIdName(result_type->id()) << ")."; + } + } else { + if (constituent_type->opcode() != spv::Op::OpTypeTensorARM) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << " must be an OpTypeTensorARM."; + } + auto inst_constituent_element_type = + _.FindDef(constituent_type->GetOperandAs<uint32_t>(1)); + if (!inst_constituent_element_type || + inst_constituent_element_type->id() != inst_element_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << " must have the same Element Type as Result Type <id> " + << _.getIdName(result_type->id()) << "."; + } + auto inst_constituent_rank = + _.FindDef(constituent_type->GetOperandAs<uint32_t>(2)); + uint64_t constituent_rank; + if (inst_constituent_rank && + _.EvalConstantValUint64(inst_constituent_rank->id(), + &constituent_rank) && + (constituent_rank != rank - 1)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << " must have a Rank that is 1 less than the Rank of " + "Result Type <id> " + << _.getIdName(result_type->id()) << ", expected " + << rank - 1 << " but got " << constituent_rank << "."; + } + + auto inst_constituent_shape = + _.FindDef(constituent_type->GetOperandAs<uint32_t>(3)); + if (!inst_constituent_shape) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Shape of Constituent " << constituent_index - 2 + << " is not defined."; + } + for (size_t constituent_shape_index = 2; + constituent_shape_index < + inst_constituent_shape->operands().size(); + constituent_shape_index++) { + size_t shape_index = constituent_shape_index + 1; + uint64_t constituent_shape = 0, shape = 1; + if (_.EvalConstantValUint64( + inst_constituent_shape->GetOperandAs<uint32_t>( + constituent_shape_index), + &constituent_shape) && + _.EvalConstantValUint64( + inst_shape->GetOperandAs<uint32_t>(shape_index), &shape) && + (constituent_shape != shape)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << " must have a Shape that matches that of Result Type " + "<id> " + << _.getIdName(result_type->id()) + << " along all inner dimensions of Result Type, expected " + << shape << " for dimension " + << constituent_shape_index - 2 + << " of Constituent but got " << constituent_shape << "."; + } + } + } + } + } break; default: break; } + + for (size_t i = 2; i < inst->operands().size(); i++) { + if (auto error = ValidateConstantOperand(_, inst, i)) { + return error; + } + } + return SPV_SUCCESS; } +spv_result_t ValidateConstantCompositeReplicate(ValidationState_t& _, + const Instruction* inst) { + std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); + + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || !isCompositeType(result_type)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Result Type <id> " + << _.getIdName(inst->type_id()) << " is not a composite type."; + } + + const auto constituent_id = inst->GetOperandAs<uint32_t>(2); + const auto constituent = _.FindDef(constituent_id); + switch (result_type->opcode()) { + case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeTensorARM: { + const auto component_type = result_type->GetOperandAs<uint32_t>(1); + if (component_type != constituent->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << "s type does not match Result Type <id> " + << _.getIdName(result_type->id()) << "s element type."; + } + break; + } + case spv::Op::OpTypeStruct: { + const auto member_count = result_type->operands().size() - 1; + for (uint32_t member_index = 1; member_index <= member_count; + member_index++) { + const auto member_type_id = + result_type->GetOperandAs<uint32_t>(member_index); + if (member_type_id != constituent->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent <id> " + << _.getIdName(constituent_id) + << " type does not match the Result Type <id> " + << _.getIdName(result_type->id()) << "s member type."; + } + } + break; + } + default: + break; + } + + return ValidateConstantOperand(_, inst, 2); +} + spv_result_t ValidateConstantSampler(ValidationState_t& _, const Instruction* inst) { const auto result_type = _.FindDef(inst->type_id()); @@ -312,6 +507,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: case spv::Op::OpTypeVector: { auto base_type = _.FindDef(instruction[2]); return base_type && IsTypeNullable(base_type->words(), _); @@ -331,6 +527,11 @@ return false; } return true; + case spv::Op::OpTypeTensorARM: { + auto elem_type = _.FindDef(instruction[2]); + return (instruction.size() > 4) && elem_type && + IsTypeNullable(elem_type->words(), _); + } default: return false; } @@ -348,6 +549,28 @@ return SPV_SUCCESS; } +spv_result_t ValidateConstantSizeOfEXT(ValidationState_t& _, + const Instruction* inst) { + const Instruction* result_type = _.FindDef(inst->type_id()); + const uint32_t bit_width = result_type->GetOperandAs<uint32_t>(1); + // VVL will validate the SPV_EXT_shader_64bit_indexing interaction + if (result_type->opcode() != spv::Op::OpTypeInt || + (bit_width != 64 && bit_width != 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For OpConstantSizeOfEXT instruction, its result type " + << "must be a 32-bit or 64-bit integer type scalar." + << " (OpCapability Int64 is required for 64-bit)"; + } + + const uint32_t type_operand = inst->GetOperandAs<uint32_t>(2); + if (!_.IsDescriptorType(type_operand)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For OpConstantSizeOfEXT instruction, its Type operand <Id> " + << _.getIdName(type_operand) << " must be a Descriptor type."; + } + return SPV_SUCCESS; +} + // Validates that OpSpecConstant specializes to either int or float type. spv_result_t ValidateSpecConstant(ValidationState_t& _, const Instruction* inst) { @@ -424,6 +647,95 @@ return SPV_SUCCESS; } +spv_result_t ValidateConstantFunctionPointerINTEL(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + // Result Type must be a pointer type + if (result_type->opcode() != spv::Op::OpTypePointer && + result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpConstantFunctionPointerINTEL Result Type <id> " + << _.getIdName(inst->type_id()) << " is not a pointer type"; + } + + // For typed pointers, check that pointee is a function type + const Instruction* pointee_type = nullptr; + if (result_type->opcode() == spv::Op::OpTypePointer) { + pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); + if (pointee_type->opcode() != spv::Op::OpTypeFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpConstantFunctionPointerINTEL Result Type <id> " + << _.getIdName(inst->type_id()) + << " must be a pointer to function type"; + } + } + + // Validate that the function operand refers to an OpFunction + const uint32_t function_id = inst->GetOperandAs<uint32_t>(2); + const auto function_inst = _.FindDef(function_id); + if (function_inst->opcode() != spv::Op::OpFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpConstantFunctionPointerINTEL Function operand <id> " + << _.getIdName(function_id) << " is not an OpFunction"; + } + + // For typed pointers, validate that function type matches pointee type + if (pointee_type) { + const uint32_t function_type_id = function_inst->GetOperandAs<uint32_t>(3); + if (function_type_id != pointee_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpConstantFunctionPointerINTEL Function operand <id> " + << _.getIdName(function_id) + << " type does not match the pointer's function type"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateConstantData(ValidationState_t& _, + const Instruction* inst) { + const auto array_inst = _.FindDef(inst->type_id()); + if (array_inst->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type must be an array."; + } + + const auto element_type_inst = + _.FindDef(array_inst->GetOperandAs<uint32_t>(1)); + if (!_.IsIntScalarType(element_type_inst->id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type must be an array of integer scalar type."; + } + + const uint32_t int_width = element_type_inst->word(2); + const uint32_t data_words = static_cast<uint32_t>(inst->words().size() - 3); + + if (data_words == 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "There must be at least 1 literal integer (because an array of " + "zero is not allowed)."; + } + + uint64_t array_length = 0; + if (!_.EvalConstantValUint64(array_inst->GetOperandAs<uint32_t>(2), + &array_length)) { + // The length could be a SpecConstant, will need to be frozen to validate + return SPV_SUCCESS; + } + + const uint32_t words_needed = + (((int_width / 8) * static_cast<uint32_t>(array_length) + 3) & ~3) / 4; + if (data_words != words_needed) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "contains " << data_words << " words of data, but needs to have " + << words_needed << " words to match the array of " << array_length + << " of " << int_width << "-bit ints."; + } + + return SPV_SUCCESS; +} + } // namespace spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) { @@ -438,6 +750,11 @@ case spv::Op::OpSpecConstantComposite: if (auto error = ValidateConstantComposite(_, inst)) return error; break; + case spv::Op::OpConstantCompositeReplicateEXT: + case spv::Op::OpSpecConstantCompositeReplicateEXT: + if (auto error = ValidateConstantCompositeReplicate(_, inst)) + return error; + break; case spv::Op::OpConstantSampler: if (auto error = ValidateConstantSampler(_, inst)) return error; break; @@ -450,6 +767,16 @@ case spv::Op::OpSpecConstantOp: if (auto error = ValidateSpecConstantOp(_, inst)) return error; break; + case spv::Op::OpConstantSizeOfEXT: + if (auto error = ValidateConstantSizeOfEXT(_, inst)) return error; + break; + case spv::Op::OpConstantFunctionPointerINTEL: + if (auto error = ValidateConstantFunctionPointerINTEL(_, inst)) + return error; + break; + case spv::Op::OpConstantDataKHR: + if (auto error = ValidateConstantData(_, inst)) return error; + break; default: break; }
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp index 770b8e2..639be6c 100644 --- a/source/val/validate_conversion.cpp +++ b/source/val/validate_conversion.cpp
@@ -14,6 +14,8 @@ // Validates correctness of conversion instructions. +#include <climits> + #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" @@ -24,610 +26,944 @@ namespace spvtools { namespace val { -// Validates correctness of conversion instructions. -spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { +spv_result_t ValidateShaderBitWidth(ValidationState_t& _, + const Instruction* inst) { + if (_.HasCapability(spv::Capability::Shader)) { + if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) || + _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "8- or 16-bit types can only be used with width-only " + "conversions"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateConvertFToU(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { const spv::Op opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !_.IsUnsignedIntCooperativeMatrixType(result_type) && + !_.IsUnsignedIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); - switch (opcode) { - case spv::Op::OpConvertFToU: { - if (!_.IsUnsignedIntScalarType(result_type) && - !_.IsUnsignedIntVectorType(result_type) && - !_.IsUnsignedIntCooperativeMatrixType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected unsigned int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type || + (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type) && + !_.IsFloatCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || (!_.IsFloatScalarType(input_type) && - !_.IsFloatVectorType(input_type) && - !_.IsFloatCooperativeMatrixType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be float scalar or vector: " - << spvOpcodeString(opcode); - - if (_.IsCooperativeMatrixType(result_type) || - _.IsCooperativeMatrixType(input_type)) { - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); - if (ret != SPV_SUCCESS) return ret; - } else { - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - } - - break; - } - - case spv::Op::OpConvertFToS: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && - !_.IsIntCooperativeMatrixType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || (!_.IsFloatScalarType(input_type) && - !_.IsFloatVectorType(input_type) && - !_.IsFloatCooperativeMatrixType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be float scalar or vector: " - << spvOpcodeString(opcode); - - if (_.IsCooperativeMatrixType(result_type) || - _.IsCooperativeMatrixType(input_type)) { - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); - if (ret != SPV_SUCCESS) return ret; - } else { - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - } - - break; - } - - case spv::Op::OpConvertSToF: - case spv::Op::OpConvertUToF: { - if (!_.IsFloatScalarType(result_type) && - !_.IsFloatVectorType(result_type) && - !_.IsFloatCooperativeMatrixType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || - (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && - !_.IsIntCooperativeMatrixType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be int scalar or vector: " - << spvOpcodeString(opcode); - - if (_.IsCooperativeMatrixType(result_type) || - _.IsCooperativeMatrixType(input_type)) { - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); - if (ret != SPV_SUCCESS) return ret; - } else { - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - } - - break; - } - - case spv::Op::OpUConvert: { - if (!_.IsUnsignedIntScalarType(result_type) && - !_.IsUnsignedIntVectorType(result_type) && - !_.IsUnsignedIntCooperativeMatrixType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected unsigned int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || - (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && - !_.IsIntCooperativeMatrixType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be int scalar or vector: " - << spvOpcodeString(opcode); - - if (_.IsCooperativeMatrixType(result_type) || - _.IsCooperativeMatrixType(input_type)) { - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); - if (ret != SPV_SUCCESS) return ret; - } else { - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - } - - if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have different bit width from Result " - "Type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpSConvert: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && - !_.IsIntCooperativeMatrixType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || - (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && - !_.IsIntCooperativeMatrixType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be int scalar or vector: " - << spvOpcodeString(opcode); - - if (_.IsCooperativeMatrixType(result_type) || - _.IsCooperativeMatrixType(input_type)) { - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); - if (ret != SPV_SUCCESS) return ret; - } else { - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - } - - if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have different bit width from Result " - "Type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpFConvert: { - if (!_.IsFloatScalarType(result_type) && - !_.IsFloatVectorType(result_type) && - !_.IsFloatCooperativeMatrixType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected float scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || (!_.IsFloatScalarType(input_type) && - !_.IsFloatVectorType(input_type) && - !_.IsFloatCooperativeMatrixType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be float scalar or vector: " - << spvOpcodeString(opcode); - - if (_.IsCooperativeMatrixType(result_type) || - _.IsCooperativeMatrixType(input_type)) { - spv_result_t ret = - _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); - if (ret != SPV_SUCCESS) return ret; - } else { - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - } - - if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have different bit width from Result " - "Type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpQuantizeToF16: { - if ((!_.IsFloatScalarType(result_type) && - !_.IsFloatVectorType(result_type)) || - _.GetBitWidth(result_type) != 32) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected 32-bit float scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (input_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input type to be equal to Result Type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpConvertPtrToU: { - if (!_.IsUnsignedIntScalarType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected unsigned int scalar type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!_.IsPointerType(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer: " << spvOpcodeString(opcode); - - if (_.addressing_model() == spv::AddressingModel::Logical) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Logical addressing not supported: " - << spvOpcodeString(opcode); - - if (_.addressing_model() == - spv::AddressingModel::PhysicalStorageBuffer64) { - spv::StorageClass input_storage_class; - uint32_t input_data_type = 0; - _.GetPointerTypeInfo(input_type, &input_data_type, - &input_storage_class); - if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer storage class must be PhysicalStorageBuffer: " - << spvOpcodeString(opcode); - - if (spvIsVulkanEnv(_.context()->target_env)) { - if (_.GetBitWidth(result_type) != 64) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4710) - << "PhysicalStorageBuffer64 addressing mode requires the " - "result integer type to have a 64-bit width for Vulkan " - "environment."; - } - } - } - break; - } - - case spv::Op::OpSatConvertSToU: - case spv::Op::OpSatConvertUToS: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || - (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector as input: " - << spvOpcodeString(opcode); - - if (_.GetDimension(result_type) != _.GetDimension(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same dimension as Result Type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpConvertUToPtr: { - if (!_.IsPointerType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be a pointer: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || !_.IsIntScalarType(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar as input: " << spvOpcodeString(opcode); - - if (_.addressing_model() == spv::AddressingModel::Logical) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Logical addressing not supported: " - << spvOpcodeString(opcode); - - if (_.addressing_model() == - spv::AddressingModel::PhysicalStorageBuffer64) { - spv::StorageClass result_storage_class; - uint32_t result_data_type = 0; - _.GetPointerTypeInfo(result_type, &result_data_type, - &result_storage_class); - if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer storage class must be PhysicalStorageBuffer: " - << spvOpcodeString(opcode); - - if (spvIsVulkanEnv(_.context()->target_env)) { - if (_.GetBitWidth(input_type) != 64) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4710) - << "PhysicalStorageBuffer64 addressing mode requires the " - "input integer to have a 64-bit width for Vulkan " - "environment."; - } - } - } - break; - } - - case spv::Op::OpPtrCastToGeneric: { - spv::StorageClass result_storage_class; - uint32_t result_data_type = 0; - if (!_.GetPointerTypeInfo(result_type, &result_data_type, - &result_storage_class)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be a pointer: " - << spvOpcodeString(opcode); - - if (result_storage_class != spv::StorageClass::Generic) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to have storage class Generic: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - spv::StorageClass input_storage_class; - uint32_t input_data_type = 0; - if (!_.GetPointerTypeInfo(input_type, &input_data_type, - &input_storage_class)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer: " << spvOpcodeString(opcode); - - if (input_storage_class != spv::StorageClass::Workgroup && - input_storage_class != spv::StorageClass::CrossWorkgroup && - input_storage_class != spv::StorageClass::Function) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have storage class Workgroup, " - << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); - - if (result_data_type != input_data_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input and Result Type to point to the same type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpGenericCastToPtr: { - spv::StorageClass result_storage_class; - uint32_t result_data_type = 0; - if (!_.GetPointerTypeInfo(result_type, &result_data_type, - &result_storage_class)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be a pointer: " - << spvOpcodeString(opcode); - - if (result_storage_class != spv::StorageClass::Workgroup && - result_storage_class != spv::StorageClass::CrossWorkgroup && - result_storage_class != spv::StorageClass::Function) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to have storage class Workgroup, " - << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - spv::StorageClass input_storage_class; - uint32_t input_data_type = 0; - if (!_.GetPointerTypeInfo(input_type, &input_data_type, - &input_storage_class)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer: " << spvOpcodeString(opcode); - - if (input_storage_class != spv::StorageClass::Generic) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have storage class Generic: " - << spvOpcodeString(opcode); - - if (result_data_type != input_data_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input and Result Type to point to the same type: " - << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpGenericCastToPtrExplicit: { - spv::StorageClass result_storage_class; - uint32_t result_data_type = 0; - if (!_.GetPointerTypeInfo(result_type, &result_data_type, - &result_storage_class)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be a pointer: " - << spvOpcodeString(opcode); - - const auto target_storage_class = - inst->GetOperandAs<spv::StorageClass>(3); - if (result_storage_class != target_storage_class) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be of target storage class: " - << spvOpcodeString(opcode); - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - spv::StorageClass input_storage_class; - uint32_t input_data_type = 0; - if (!_.GetPointerTypeInfo(input_type, &input_data_type, - &input_storage_class)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer: " << spvOpcodeString(opcode); - - if (input_storage_class != spv::StorageClass::Generic) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have storage class Generic: " - << spvOpcodeString(opcode); - - if (result_data_type != input_data_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input and Result Type to point to the same type: " - << spvOpcodeString(opcode); - - if (target_storage_class != spv::StorageClass::Workgroup && - target_storage_class != spv::StorageClass::CrossWorkgroup && - target_storage_class != spv::StorageClass::Function) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected target storage class to be Workgroup, " - << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); - break; - } - - case spv::Op::OpBitcast: { - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have a type: " << spvOpcodeString(opcode); - - const bool result_is_pointer = _.IsPointerType(result_type); - const bool result_is_int_scalar = _.IsIntScalarType(result_type); - const bool input_is_pointer = _.IsPointerType(input_type); - const bool input_is_int_scalar = _.IsIntScalarType(input_type); - - const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type); - const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type); - - if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat && - !_.IsIntVectorType(result_type) && - !_.IsFloatScalarType(result_type) && - !_.IsFloatVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be a pointer or int or float vector " - << "or scalar type: " << spvOpcodeString(opcode); - - if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat && - !_.IsIntVectorType(input_type) && !_.IsFloatScalarType(input_type) && - !_.IsFloatVectorType(input_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer or int or float vector " - << "or scalar: " << spvOpcodeString(opcode); - - if (result_is_coopmat != input_is_coopmat) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cooperative matrix can only be cast to another cooperative " - << "matrix: " << spvOpcodeString(opcode); - - if (result_is_coopmat) { - spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type, - input_type, false); - if (ret != SPV_SUCCESS) return ret; - } - - if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) || - _.HasExtension(kSPV_KHR_physical_storage_buffer)) { - const bool result_is_int_vector = _.IsIntVectorType(result_type); - const bool result_has_int32 = - _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32); - const bool input_is_int_vector = _.IsIntVectorType(input_type); - const bool input_has_int32 = - _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32); - if (result_is_pointer && !input_is_pointer && !input_is_int_scalar && - !(input_is_int_vector && input_has_int32)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer, int scalar or 32-bit int " - "vector if Result Type is pointer: " - << spvOpcodeString(opcode); - - if (input_is_pointer && !result_is_pointer && !result_is_int_scalar && - !(result_is_int_vector && result_has_int32)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer can only be converted to another pointer, int " - "scalar or 32-bit int vector: " - << spvOpcodeString(opcode); - } else { - if (result_is_pointer && !input_is_pointer && !input_is_int_scalar) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to be a pointer or int scalar if Result " - "Type is pointer: " - << spvOpcodeString(opcode); - - if (input_is_pointer && !result_is_pointer && !result_is_int_scalar) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer can only be converted to another pointer or int " - "scalar: " - << spvOpcodeString(opcode); - } - - if (!result_is_pointer && !input_is_pointer) { - const uint32_t result_size = - _.GetBitWidth(result_type) * _.GetDimension(result_type); - const uint32_t input_size = - _.GetBitWidth(input_type) * _.GetDimension(input_type); - if (result_size != input_size) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected input to have the same total bit width as " - << "Result Type: " << spvOpcodeString(opcode); - } - break; - } - - case spv::Op::OpConvertUToAccelerationStructureKHR: { - if (!_.IsAccelerationStructureType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Result Type to be a Acceleration Structure: " - << spvOpcodeString(opcode); - } - - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!input_type || !_.IsUnsigned64BitHandle(input_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected 64-bit uint scalar or 2-component 32-bit uint " - "vector as input: " - << spvOpcodeString(opcode); - } - - break; - } - - case spv::Op::OpCooperativeMatrixConvertNV: - case spv::Op::OpCooperativeMatrixTransposeNV: { - if (!_.IsCooperativeMatrixType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix Result Type: " - << spvOpcodeString(opcode); - } - const uint32_t input_type = _.GetOperandTypeId(inst, 2); - if (!_.IsCooperativeMatrixType(input_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected cooperative matrix type for Matrix input: " - << spvOpcodeString(opcode); - } - - bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV); - if (auto error = _.CooperativeMatrixShapesMatch( - inst, result_type, input_type, true, swap_row_col)) - return error; - - if (opcode == spv::Op::OpCooperativeMatrixConvertNV) { - if (_.FindDef(result_type)->GetOperandAs<uint32_t>(1) != - _.FindDef(input_type)->GetOperandAs<uint32_t>(1)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Result Type and Matrix component types mismatch: " - << spvOpcodeString(opcode); - } - } - - if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) { - if (!_.IsCooperativeMatrixBType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Result Type must have UseB: " << spvOpcodeString(opcode); - } - } - break; - } - - default: - break; + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); } - if (_.HasCapability(spv::Capability::Shader)) { - switch (inst->opcode()) { - case spv::Op::OpConvertFToU: - case spv::Op::OpConvertFToS: - case spv::Op::OpConvertSToF: - case spv::Op::OpConvertUToF: - case spv::Op::OpBitcast: - if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) || - _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "8- or 16-bit types can only be used with width-only " - "conversions"; - } - break; - default: - break; + if (auto error = ValidateShaderBitWidth(_, inst)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateConvertFToS(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeMatrixType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type || + (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type) && + !_.IsFloatCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (auto error = ValidateShaderBitWidth(_, inst)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateConvertIntToF(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeMatrixType(result_type) && + !_.IsFloatCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type) && + !_.IsIntCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (auto error = ValidateShaderBitWidth(_, inst)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateUConvert(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !_.IsUnsignedIntCooperativeMatrixType(result_type) && + !_.IsUnsignedIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type) && + !_.IsIntCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateSConvert(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeMatrixType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type) && + !_.IsIntCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateFConvert(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeMatrixType(result_type) && + !_.IsFloatCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type || + (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type) && + !_.IsFloatCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + // Scalar type + const uint32_t resScalarType = _.GetComponentType(result_type); + const uint32_t inputScalartype = _.GetComponentType(input_type); + if (resScalarType == inputScalartype) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component type of Value to be different from " + "component type of Result Type: " + << spvOpcodeString(opcode); + } + return SPV_SUCCESS; +} + +spv_result_t ValidateQuantizeToF16(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if ((!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type)) || + _.GetBitWidth(result_type) != 32) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (input_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input type to be equal to Result Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateConvertPtrToU(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const bool has_masked_gather_scatter = + _.HasCapability(spv::Capability::MaskedGatherScatterINTEL); + + bool valid_result_type = _.IsUnsignedIntScalarType(result_type); + if (!valid_result_type && has_masked_gather_scatter) { + valid_result_type = _.IsUnsignedIntVectorType(result_type); + } + + if (!valid_result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar type as Result Type" + << (has_masked_gather_scatter ? " (or vector of unsigned int with " + "MaskedGatherScatterINTEL)" + : "") + << ": " << spvOpcodeString(opcode); + } + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + + bool valid_input_type = _.IsPointerType(input_type); + if (!valid_input_type && has_masked_gather_scatter && input_type) { + if (_.IsVectorType(input_type)) { + const uint32_t component_type = _.GetComponentType(input_type); + valid_input_type = _.IsPointerType(component_type); } } + if (!valid_input_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer" + << (has_masked_gather_scatter + ? " (or vector of pointers with MaskedGatherScatterINTEL)" + : "") + << ": " << spvOpcodeString(opcode); + } + + if (has_masked_gather_scatter && _.IsVectorType(result_type)) { + if (!_.IsVectorType(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a vector when Result Type is a vector: " + << spvOpcodeString(opcode); + } + if (_.GetDimension(result_type) != _.GetDimension(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + } + + if (_.addressing_model() == spv::AddressingModel::Logical) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Logical addressing not supported: " << spvOpcodeString(opcode); + + if (_.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) { + uint32_t ptr_type = input_type; + if (_.IsVectorType(input_type)) { + ptr_type = _.GetComponentType(input_type); + } + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + _.GetPointerTypeInfo(ptr_type, &input_data_type, &input_storage_class); + if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer storage class must be PhysicalStorageBuffer: " + << spvOpcodeString(opcode); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(result_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4710) + << "PhysicalStorageBuffer64 addressing mode requires the " + "result integer type to have a 64-bit width for Vulkan " + "environment."; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateSatConvertInt(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector as input: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateConvertUToPtr(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const bool has_masked_gather_scatter = + _.HasCapability(spv::Capability::MaskedGatherScatterINTEL); + + bool valid_result_type = _.IsPointerType(result_type); + if (!valid_result_type && has_masked_gather_scatter) { + if (_.IsVectorType(result_type)) { + const uint32_t component_type = _.GetComponentType(result_type); + valid_result_type = _.IsPointerType(component_type); + } + } + + if (!valid_result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer" + << (has_masked_gather_scatter + ? " (or vector of pointers with MaskedGatherScatterINTEL)" + : "") + << ": " << spvOpcodeString(opcode); + } + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + + bool valid_input_type = input_type && _.IsIntScalarType(input_type); + if (!valid_input_type && has_masked_gather_scatter && input_type) { + valid_input_type = _.IsIntVectorType(input_type); + } + + if (!valid_input_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar as input" + << (has_masked_gather_scatter + ? " (or vector of int with MaskedGatherScatterINTEL)" + : "") + << ": " << spvOpcodeString(opcode); + } + + if (has_masked_gather_scatter && _.IsVectorType(result_type)) { + if (!_.IsVectorType(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a vector when Result Type is a vector: " + << spvOpcodeString(opcode); + } + if (_.GetDimension(result_type) != _.GetDimension(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + } + + if (_.addressing_model() == spv::AddressingModel::Logical) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Logical addressing not supported: " << spvOpcodeString(opcode); + + if (_.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) { + uint32_t ptr_type = result_type; + if (_.IsVectorType(result_type)) { + ptr_type = _.GetComponentType(result_type); + } + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + _.GetPointerTypeInfo(ptr_type, &result_data_type, &result_storage_class); + if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer storage class must be PhysicalStorageBuffer: " + << spvOpcodeString(opcode); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(input_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4710) + << "PhysicalStorageBuffer64 addressing mode requires the " + "input integer to have a 64-bit width for Vulkan " + "environment."; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidatePtrCastToGeneric(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + if (result_storage_class != spv::StorageClass::Generic) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have storage class Generic: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != spv::StorageClass::Workgroup && + input_storage_class != spv::StorageClass::CrossWorkgroup && + input_storage_class != spv::StorageClass::Function) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateGenericCastToPtr(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + if (result_storage_class != spv::StorageClass::Workgroup && + result_storage_class != spv::StorageClass::CrossWorkgroup && + result_storage_class != spv::StorageClass::Function) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have storage class Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != spv::StorageClass::Generic) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Generic: " + << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateGenericCastToPtrExplicit(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + const auto target_storage_class = inst->GetOperandAs<spv::StorageClass>(3); + if (result_storage_class != target_storage_class) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be of target storage class: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != spv::StorageClass::Generic) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Generic: " + << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + + if (target_storage_class != spv::StorageClass::Workgroup && + target_storage_class != spv::StorageClass::CrossWorkgroup && + target_storage_class != spv::StorageClass::Function) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected target storage class to be Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateBitcast(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); + if (!input_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have a type: " << spvOpcodeString(opcode); + + const bool result_is_pointer = _.IsPointerType(result_type); + const bool result_is_int_scalar = _.IsIntScalarType(result_type); + const bool input_is_pointer = _.IsPointerType(input_type); + const bool input_is_int_scalar = _.IsIntScalarType(input_type); + + const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type); + const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type); + const bool result_is_coopvec = _.IsCooperativeVectorNVType(result_type); + const bool input_is_coopvec = _.IsCooperativeVectorNVType(input_type); + + if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat && + !result_is_coopvec && !_.IsIntVectorType(result_type) && + !_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer or int or float vector " + << "or scalar type: " << spvOpcodeString(opcode); + + if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat && + !input_is_coopvec && !_.IsIntVectorType(input_type) && + !_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer or int or float vector " + << "or scalar: " << spvOpcodeString(opcode); + + // NV_cooperative_vector doesn't allow bitcasting between vec<->coopvec, + // but long_vector does. + if (result_is_coopvec != input_is_coopvec && + !_.HasCapability(spv::Capability::LongVectorEXT)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative vector can only be cast to another cooperative " + << "vector: " << spvOpcodeString(opcode); + + if (result_is_coopmat != input_is_coopmat) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix can only be cast to another cooperative " + << "matrix: " << spvOpcodeString(opcode); + + if (result_is_coopvec && input_is_coopvec && + !_.HasCapability(spv::Capability::LongVectorEXT)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } + + if (result_is_coopmat) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, false); + if (ret != SPV_SUCCESS) return ret; + } + + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) || + _.HasExtension(kSPV_KHR_physical_storage_buffer)) { + const bool result_is_int_vector = _.IsIntVectorType(result_type); + const bool result_has_int32 = + _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32); + const bool input_is_int_vector = _.IsIntVectorType(input_type); + const bool input_has_int32 = + _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32); + if (result_is_pointer && !input_is_pointer && !input_is_int_scalar && + !(input_is_int_vector && input_has_int32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In SPIR-V 1.5 or later (or with " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer, " + "int scalar or 32-bit int " + "vector if Result Type is pointer: " + << spvOpcodeString(opcode); + + if (input_is_pointer && !result_is_pointer && !result_is_int_scalar && + !(result_is_int_vector && result_has_int32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In SPIR-V 1.5 or later (or with " + "SPV_KHR_physical_storage_buffer), pointer can only be " + "converted to " + "another pointer, int " + "scalar or 32-bit int vector: " + << spvOpcodeString(opcode); + } else { + if (result_is_pointer && !input_is_pointer && !input_is_int_scalar) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In SPIR-V 1.4 or earlier (and without " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer " + "or int scalar if Result " + "Type is pointer: " + << spvOpcodeString(opcode); + + if (input_is_pointer && !result_is_pointer && !result_is_int_scalar) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In SPIR-V 1.4 or earlier (and without " + "SPV_KHR_physical_storage_buffer), pointer can only be " + "converted " + "to another pointer or int " + "scalar: " + << spvOpcodeString(opcode); + } + + if (!result_is_pointer && !input_is_pointer) { + const uint32_t result_size = + _.GetBitWidth(result_type) * _.GetDimension(result_type); + const uint32_t input_size = + _.GetBitWidth(input_type) * _.GetDimension(input_type); + if (result_size != input_size) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same total bit width as " + << "Result Type: " << spvOpcodeString(opcode); + } + + if (auto error = ValidateShaderBitWidth(_, inst)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateConvertUToAccelerationStructure(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsAccelerationStructureType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a Acceleration Structure: " + << spvOpcodeString(opcode); + } + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || !_.IsUnsigned64BitHandle(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 64-bit uint scalar or 2-component 32-bit uint " + "vector as input: " + << spvOpcodeString(opcode); + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrix(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsCooperativeMatrixType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix Result Type: " + << spvOpcodeString(opcode); + } + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!_.IsCooperativeMatrixType(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type for Matrix input: " + << spvOpcodeString(opcode); + } + + bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV); + if (auto error = _.CooperativeMatrixShapesMatch(inst, result_type, input_type, + true, swap_row_col)) + return error; + + if (opcode == spv::Op::OpCooperativeMatrixConvertNV) { + if (_.FindDef(result_type)->GetOperandAs<uint32_t>(1) != + _.FindDef(input_type)->GetOperandAs<uint32_t>(1)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type and Matrix component types mismatch: " + << spvOpcodeString(opcode); + } + } + + if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) { + if (!_.IsCooperativeMatrixBType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must have UseB: " << spvOpcodeString(opcode); + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateBitCastArray(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + const auto result_type_inst = _.FindDef(result_type); + const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); + const auto source_type_inst = _.FindDef(source->type_id()); + + // Are the input and the result arrays? + if (result_type_inst->opcode() != spv::Op::OpTypeArray || + source_type_inst->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(opcode) + << " requires OpTypeArray operands for the input and the " + "result."; + } + + const auto source_elt_type = _.GetComponentType(source_type_inst->id()); + const auto result_elt_type = _.GetComponentType(result_type_inst->id()); + + if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(opcode) + << " requires the source element type be one of 32-bit " + "OpTypeInt " + "(signed/unsigned), 32-bit OpTypeFloat and 16-bit " + "OpTypeFloat"; + } + + if (!_.IsIntNOrFP32OrFP16<32>(result_elt_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(opcode) + << " requires the result element type be one of 32-bit " + "OpTypeInt " + "(signed/unsigned), 32-bit OpTypeFloat and 16-bit " + "OpTypeFloat"; + } + + unsigned src_arr_len_id = source_type_inst->GetOperandAs<unsigned>(2u); + unsigned res_arr_len_id = result_type_inst->GetOperandAs<unsigned>(2u); + + // Are the input and result element types compatible? + unsigned src_arr_len = UINT_MAX, res_arr_len = UINT_MAX; + bool src_arr_len_status = + _.GetConstantValueAs<unsigned>(src_arr_len_id, src_arr_len); + bool res_arr_len_status = + _.GetConstantValueAs<unsigned>(res_arr_len_id, res_arr_len); + + bool is_src_arr_len_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(src_arr_len_id)->opcode()); + bool is_res_arr_len_spec_const = + spvOpcodeIsSpecConstant(_.FindDef(res_arr_len_id)->opcode()); + + unsigned source_bitlen = _.GetBitWidth(source_elt_type) * src_arr_len; + unsigned result_bitlen = _.GetBitWidth(result_elt_type) * res_arr_len; + if (!is_src_arr_len_spec_const && !is_res_arr_len_spec_const && + (!src_arr_len_status || !res_arr_len_status || + source_bitlen != result_bitlen)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Opcode " << spvOpcodeString(opcode) + << " requires source and result types be compatible for " + "conversion."; + } + return SPV_SUCCESS; +} + +// Validates correctness of conversion instructions. +spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpConvertFToU: + return ValidateConvertFToU(_, inst); + case spv::Op::OpConvertFToS: + return ValidateConvertFToS(_, inst); + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + return ValidateConvertIntToF(_, inst); + case spv::Op::OpUConvert: + return ValidateUConvert(_, inst); + case spv::Op::OpSConvert: + return ValidateSConvert(_, inst); + case spv::Op::OpFConvert: + return ValidateFConvert(_, inst); + case spv::Op::OpQuantizeToF16: + return ValidateQuantizeToF16(_, inst); + case spv::Op::OpConvertPtrToU: + return ValidateConvertPtrToU(_, inst); + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: + return ValidateSatConvertInt(_, inst); + case spv::Op::OpConvertUToPtr: + return ValidateConvertUToPtr(_, inst); + case spv::Op::OpPtrCastToGeneric: + return ValidatePtrCastToGeneric(_, inst); + case spv::Op::OpGenericCastToPtr: + return ValidateGenericCastToPtr(_, inst); + case spv::Op::OpGenericCastToPtrExplicit: + return ValidateGenericCastToPtrExplicit(_, inst); + case spv::Op::OpBitcast: + return ValidateBitcast(_, inst); + case spv::Op::OpConvertUToAccelerationStructureKHR: + return ValidateConvertUToAccelerationStructure(_, inst); + case spv::Op::OpCooperativeMatrixConvertNV: + case spv::Op::OpCooperativeMatrixTransposeNV: + return ValidateCooperativeMatrix(_, inst); + case spv::Op::OpBitCastArrayQCOM: + return ValidateBitCastArray(_, inst); + + case spv::Op::OpSpecConstantOp: { + switch (inst->GetOperandAs<spv::Op>(2u)) { + case spv::Op::OpUConvert: + return ValidateUConvert(_, inst, 3); + case spv::Op::OpSConvert: + return ValidateSConvert(_, inst, 3); + case spv::Op::OpFConvert: + return ValidateFConvert(_, inst, 3); + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + return ValidateConvertIntToF(_, inst, 3); + case spv::Op::OpConvertFToS: + return ValidateConvertFToS(_, inst, 3); + case spv::Op::OpConvertFToU: + return ValidateConvertFToU(_, inst, 3); + case spv::Op::OpQuantizeToF16: + return ValidateQuantizeToF16(_, inst, 3); + case spv::Op::OpConvertPtrToU: + return ValidateConvertPtrToU(_, inst, 3); + case spv::Op::OpConvertUToPtr: + return ValidateConvertUToPtr(_, inst, 3); + case spv::Op::OpGenericCastToPtr: + return ValidateGenericCastToPtr(_, inst, 3); + case spv::Op::OpPtrCastToGeneric: + return ValidatePtrCastToGeneric(_, inst, 3); + case spv::Op::OpBitcast: + return ValidateBitcast(_, inst, 3); + default: + break; + } + break; + } + default: + break; + } + return SPV_SUCCESS; }
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index e680bd6..406a5e6 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp
@@ -122,7 +122,8 @@ hasOffset.resize(struct_members.size(), false); for (auto& decoration : vstate.id_decorations(struct_id)) { - if (spv::Decoration::Offset == decoration.dec_type() && + if ((spv::Decoration::Offset == decoration.dec_type() || + spv::Decoration::OffsetIdEXT == 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; @@ -170,6 +171,10 @@ case spv::Op::OpTypeImage: if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) return vstate.samplerimage_variable_address_mode() / 8; + // SPV_EXT_descriptor_heap provides a way to access opaque images, we + // assume alignment is validated at runtime as it is determined by the + // client API + if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1; assert(0); return 0; case spv::Op::OpTypeInt: @@ -182,7 +187,19 @@ const auto componentAlignment = getBaseAlignment( componentId, roundUp, inherited, constraints, vstate); baseAlignment = - componentAlignment * (numComponents == 3 ? 4 : numComponents); + componentAlignment * + ((numComponents == 3 || numComponents > 4) ? 4 : numComponents); + break; + } + case spv::Op::OpTypeVectorIdEXT: { + const auto componentId = words[2]; + const auto numComponents = vstate.GetDimension(inst->id()); + assert(numComponents != 0); + const auto componentAlignment = getBaseAlignment( + componentId, roundUp, inherited, constraints, vstate); + baseAlignment = + componentAlignment * + ((numComponents == 3 || numComponents > 4) ? 4 : numComponents); break; } case spv::Op::OpTypeMatrix: { @@ -245,12 +262,17 @@ case spv::Op::OpTypeImage: if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) return vstate.samplerimage_variable_address_mode() / 8; + // SPV_EXT_descriptor_heap provides a way to access opaque images, we + // assume alignment is validated at runtime as it is determined by the + // client API + if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1; assert(0); return 0; case spv::Op::OpTypeInt: case spv::Op::OpTypeFloat: return words[2] / 8; case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: case spv::Op::OpTypeMatrix: case spv::Op::OpTypeArray: case spv::Op::OpTypeRuntimeArray: { @@ -293,6 +315,10 @@ case spv::Op::OpTypeImage: if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) return vstate.samplerimage_variable_address_mode() / 8; + // SPV_EXT_descriptor_heap provides a way to access opaque images, we + // assume alignment is validated at runtime as it is determined by the + // client API + if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1; assert(0); return 0; case spv::Op::OpTypeInt: @@ -306,6 +332,15 @@ const auto size = componentSize * numComponents; return size; } + case spv::Op::OpTypeVectorIdEXT: { + const auto componentId = words[2]; + const auto numComponents = vstate.GetDimension(inst->id()); + assert(numComponents != 0); + const auto componentSize = + getSize(componentId, inherited, constraints, vstate); + const auto size = componentSize * numComponents; + return size; + } case spv::Op::OpTypeArray: { const auto sizeInst = vstate.FindDef(words[3]); if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0; @@ -402,10 +437,9 @@ // Block or BufferBlocks in Vulkan. Otherwise emits a diagnostic and returns // something other than SPV_SUCCESS. Matrices inherit the specified column // or row major-ness. -spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, +spv_result_t checkLayout(uint32_t struct_id, spv::StorageClass storage_class, const char* decoration_str, bool blockRules, - bool scalar_block_layout, - uint32_t incoming_offset, + bool scalar_block_layout, uint32_t incoming_offset, MemberConstraints& constraints, ValidationState_t& vstate) { if (vstate.options()->skip_block_layout) return SPV_SUCCESS; @@ -419,22 +453,48 @@ // is more permissive than relaxed layout. const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout(); - auto fail = [&vstate, struct_id, storage_class_str, decoration_str, - blockRules, relaxed_block_layout, + auto fail = [&vstate, struct_id, storage_class, decoration_str, blockRules, + relaxed_block_layout, scalar_block_layout](uint32_t member_idx) -> DiagnosticStream { - DiagnosticStream ds = - std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id)) - << "Structure id " << struct_id << " decorated as " - << decoration_str << " for variable in " << storage_class_str - << " storage class must follow " - << (scalar_block_layout - ? "scalar " - : (relaxed_block_layout ? "relaxed " : "standard ")) - << (blockRules ? "uniform buffer" : "storage buffer") - << " layout rules: member " << member_idx << " "); + DiagnosticStream ds = std::move( + vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id)) + << "Structure id " << struct_id << " decorated as " << decoration_str + << " for variable in " << StorageClassToString(storage_class) + << " storage class must follow " + << (scalar_block_layout + ? "scalar " + : (relaxed_block_layout ? "relaxed " : "standard ")) + << (blockRules ? "uniform buffer" : "storage buffer") + << " layout rules: member " << member_idx << " "); return ds; }; + // People often use spirv-val from Vulkan Validation Layers, it ends up + // mapping the various block layout rules from the enabled feature. This + // offers a hint to help the user understand possbily why things are not + // working when the shader itself "seems" valid, but just was a lack of adding + // a supported feature + auto extra = [&vstate, scalar_block_layout, storage_class, + relaxed_block_layout, blockRules]() { + if (!scalar_block_layout) { + if (storage_class == spv::StorageClass::Workgroup) { + return vstate.MissingFeature( + "workgroupMemoryExplicitLayoutScalarBlockLayout feature", + "--workgroup-scalar-block-layout", true); + } else if (!relaxed_block_layout) { + return vstate.MissingFeature("VK_KHR_relaxed_block_layout extension", + "--relax-block-layout", true); + } else if (blockRules) { + return vstate.MissingFeature("uniformBufferStandardLayout feature", + "--uniform-buffer-standard-layout", true); + } else { + return vstate.MissingFeature("scalarBlockLayout feature", + "--scalar-block-layout", true); + } + } + return std::string(""); + }; + // If we are checking the layout of untyped pointers or physical storage // buffer pointers, we may not actually have a struct here. Instead, pretend // we have a struct with a single member at offset 0. @@ -508,50 +568,63 @@ const auto size = getSize(id, constraint, constraints, vstate); // Check offset. if (offset == 0xffffffff) - return fail(memberIdx) << "is missing an Offset decoration"; + return fail(memberIdx) << "is missing an Offset decoration" << extra(); + + if (opcode == spv::Op::OpTypeRuntimeArray && + ordered_member_idx != member_offsets.size() - 1) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id)) + << vstate.VkErrorID(4680) << "Structure id " << struct_id + << " has a runtime array at offset " << offset + << ", but other members at larger offsets"; + } + if (!scalar_block_layout && relaxed_block_layout && - opcode == spv::Op::OpTypeVector) { + (opcode == spv::Op::OpTypeVector || + opcode == spv::Op::OpTypeVectorIdEXT)) { // In relaxed block layout, the vector offset must be aligned to the // vector's scalar element type. const auto componentId = inst->words()[2]; const auto scalar_alignment = getScalarAlignment(componentId, vstate); if (!IsAlignedTo(offset, scalar_alignment)) { - return fail(memberIdx) - << "at offset " << offset - << " is not aligned to scalar element size " << scalar_alignment; + return fail(memberIdx) << "at offset " << offset + << " is not aligned to scalar element size " + << scalar_alignment << extra(); } } else { // Without relaxed block layout, the offset must be divisible by the // alignment requirement. if (!IsAlignedTo(offset, alignment)) { - return fail(memberIdx) - << "at offset " << offset << " is not aligned to " << alignment; + return fail(memberIdx) << "at offset " << offset + << " is not aligned to " << alignment << extra(); } } if (offset < nextValidOffset) return fail(memberIdx) << "at offset " << offset << " overlaps previous member ending at offset " - << nextValidOffset - 1; + << nextValidOffset - 1 << extra(); if (!scalar_block_layout && relaxed_block_layout) { // Check improper straddle of vectors. - if (spv::Op::OpTypeVector == opcode && + if ((spv::Op::OpTypeVector == opcode || + spv::Op::OpTypeVectorIdEXT == opcode) && hasImproperStraddle(id, offset, constraint, constraints, vstate)) return fail(memberIdx) - << "is an improperly straddling vector at offset " << offset; + << "is an improperly straddling vector at offset " << offset + << extra(); } // Check struct members recursively. spv_result_t recursive_status = SPV_SUCCESS; if (spv::Op::OpTypeStruct == opcode && SPV_SUCCESS != (recursive_status = checkLayout( - id, storage_class_str, decoration_str, blockRules, + id, storage_class, decoration_str, blockRules, scalar_block_layout, offset, constraints, vstate))) return recursive_status; // Check matrix stride. if (spv::Op::OpTypeMatrix == opcode) { const auto stride = constraint.matrix_stride; if (!IsAlignedTo(stride, alignment)) { - return fail(memberIdx) << "is a matrix with stride " << stride - << " not satisfying alignment to " << alignment; + return fail(memberIdx) + << "is a matrix with stride " << stride + << " not satisfying alignment to " << alignment << extra(); } } @@ -568,12 +641,13 @@ if (spv::Decoration::ArrayStride == decoration.dec_type()) { array_stride = decoration.params()[0]; if (array_stride == 0) { - return fail(memberIdx) << "contains an array with stride 0"; + return fail(memberIdx) + << "contains an array with stride 0" << extra(); } if (!IsAlignedTo(array_stride, array_alignment)) return fail(memberIdx) << "contains an array with stride " << decoration.params()[0] - << " not satisfying alignment to " << alignment; + << " not satisfying alignment to " << alignment << extra(); } } @@ -600,7 +674,7 @@ if (SPV_SUCCESS != (recursive_status = checkLayout( - typeId, storage_class_str, decoration_str, blockRules, + typeId, storage_class, decoration_str, blockRules, scalar_block_layout, next_offset, constraints, vstate))) return recursive_status; @@ -612,7 +686,7 @@ if (!IsAlignedTo(stride, alignment)) { return fail(memberIdx) << "is a matrix with stride " << stride - << " not satisfying alignment to " << alignment; + << " not satisfying alignment to " << alignment << extra(); } } @@ -628,7 +702,7 @@ if (element_size > array_stride) { return fail(memberIdx) << "contains an array with stride " << array_stride - << ", but with an element size of " << element_size; + << ", but with an element size of " << element_size << extra(); } } nextValidOffset = offset + size; @@ -767,6 +841,7 @@ int num_workgroup_variables = 0; int num_workgroup_variables_with_block = 0; int num_workgroup_variables_with_aliased = 0; + bool has_task_payload = false; for (const auto& desc : descs) { std::unordered_set<Instruction*> seen_vars; std::unordered_set<spv::BuiltIn> input_var_builtin; @@ -787,24 +862,40 @@ const spv::StorageClass storage_class = var_instr->GetOperandAs<spv::StorageClass>(sc_index); if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // SPV_EXT_mesh_shader, at most one task payload is permitted + // per entry point + if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) { + if (has_task_payload) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "There can be at most one " + "OpVariable with storage " + "class TaskPayloadWorkgroupEXT associated with " + "an OpEntryPoint"; + } + has_task_payload = true; + } + // Starting in 1.4, OpEntryPoint must list all global variables // it statically uses and those interfaces must be unique. if (storage_class == spv::StorageClass::Function) { return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) - << "OpEntryPoint interfaces should only list global " + << "In SPIR-V 1.4 or later, OpEntryPoint interfaces should " + "only list global " "variables"; } if (!seen_vars.insert(var_instr).second) { return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) - << "Non-unique OpEntryPoint interface " + << "In SPIR-V 1.4 or later, non-unique OpEntryPoint " + "interface " << vstate.getIdName(interface) << " is disallowed"; } } else { if (storage_class != spv::StorageClass::Input && storage_class != spv::StorageClass::Output) { return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) - << "OpEntryPoint interfaces must be OpVariables with " + << "In SPIR-V 1.3 or earlier, OpEntryPoint interfaces must " + "be OpVariables with " "Storage Class of Input(1) or Output(3). Found Storage " "Class " << uint32_t(storage_class) << " for Entry Point id " @@ -812,6 +903,11 @@ } } + // Descriptor heap's base variables have no data type in declaration. + if (untyped_pointers && var_instr->words().size() < 5 && + vstate.IsDescriptorHeapBaseVariable(var_instr)) + continue; + // It is guaranteed (by validator ID checks) that ptr_instr is // OpTypePointer. Word 3 of this instruction is the type being pointed // to. For untyped variables, the pointee type comes from the data type @@ -944,8 +1040,7 @@ hasDecoration(var_instr->id(), spv::Decoration::Flat, vstate); if (has_frag && storage_class == spv::StorageClass::Input && !has_flat && - ((vstate.IsFloatScalarType(type_id) && - vstate.GetBitWidth(type_id) == 64) || + (vstate.IsFloatScalarType(type_id, 64) || vstate.IsIntScalarOrVectorType(type_id))) { return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) << vstate.VkErrorID(4744) @@ -1000,7 +1095,7 @@ } if (num_workgroup_variables_with_block > 1 && num_workgroup_variables_with_block != - num_workgroup_variables_with_aliased) { + num_workgroup_variables_with_aliased) { return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "When declaring WorkgroupMemoryExplicitLayoutKHR, " @@ -1107,6 +1202,59 @@ } } +spv_result_t CheckDecorationsOfVariables(ValidationState_t& vstate) { + if (!spvIsVulkanEnv(vstate.context()->target_env)) { + return SPV_SUCCESS; + } + for (const auto& inst : vstate.ordered_instructions()) { + if ((spv::Op::OpVariable == inst.opcode()) || + (spv::Op::OpUntypedVariableKHR == inst.opcode())) { + const auto var_id = inst.id(); + const auto storageClass = inst.GetOperandAs<spv::StorageClass>(2); + const bool uniform = storageClass == spv::StorageClass::Uniform; + const bool uniform_constant = + storageClass == spv::StorageClass::UniformConstant; + const bool storage_buffer = + storageClass == spv::StorageClass::StorageBuffer; + + const char* sc_str = uniform ? "Uniform" + : uniform_constant ? "UniformConstant" + : "StorageBuffer"; + // Check variables in the UniformConstant, StorageBuffer, and Uniform + // storage classes are decorated with DescriptorSet and Binding + // (VUID-06677). + if (uniform_constant || storage_buffer || uniform) { + if (vstate.IsDescriptorHeapBaseVariable(&inst)) { + continue; + } + // Skip validation if the variable is not used and we're looking + // at a module coming from HLSL that has not been legalized yet. + if (vstate.options()->before_hlsl_legalization && + vstate.EntryPointReferences(var_id).empty()) { + continue; + } + if (!hasDecoration(var_id, spv::Decoration::DescriptorSet, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id + << "' is missing DescriptorSet decoration.\n" + << "From Vulkan spec:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + if (!hasDecoration(var_id, spv::Decoration::Binding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id + << "' is missing Binding decoration.\n" + << "From Vulkan spec:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + } + } + } + return SPV_SUCCESS; +} + spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { // Set of entry points that are known to use a push constant. std::unordered_set<uint32_t> uses_push_constant; @@ -1126,8 +1274,6 @@ const auto storageClassVal = words[3]; const auto storageClass = spv::StorageClass(storageClassVal); const bool uniform = storageClass == spv::StorageClass::Uniform; - const bool uniform_constant = - storageClass == spv::StorageClass::UniformConstant; const bool push_constant = storageClass == spv::StorageClass::PushConstant; const bool storage_buffer = @@ -1136,7 +1282,8 @@ if (spvIsVulkanEnv(vstate.context()->target_env)) { // Vulkan: There must be no more than one PushConstant block per entry // point. - if (push_constant) { + if (push_constant && + !(vstate.HasCapability(spv::Capability::PushConstantBanksNV))) { auto entry_points = vstate.EntryPointReferences(var_id); for (auto ep_id : entry_points) { const bool already_used = !uses_push_constant.insert(ep_id).second; @@ -1150,29 +1297,6 @@ } } } - // Vulkan: Check DescriptorSet and Binding decoration for - // UniformConstant which cannot be a struct. - if (uniform_constant) { - auto entry_points = vstate.EntryPointReferences(var_id); - if (!entry_points.empty() && - !hasDecoration(var_id, spv::Decoration::DescriptorSet, vstate)) { - return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id - << "' is missing DescriptorSet decoration.\n" - << "From Vulkan spec:\n" - << "These variables must have DescriptorSet and Binding " - "decorations specified"; - } - if (!entry_points.empty() && - !hasDecoration(var_id, spv::Decoration::Binding, vstate)) { - return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id - << "' is missing Binding decoration.\n" - << "From Vulkan spec:\n" - << "These variables must have DescriptorSet and Binding " - "decorations specified"; - } - } } if (spvIsOpenGLEnv(vstate.context()->target_env)) { @@ -1185,8 +1309,8 @@ if (!entry_points.empty() && !hasDecoration(var_id, spv::Decoration::Binding, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << (uniform ? "Uniform" : "Storage Buffer") << " id '" - << var_id << "' is missing Binding decoration.\n" + << StorageClassToString(storageClass) << " id '" << var_id + << "' is missing Binding decoration.\n" << "From ARB_gl_spirv extension:\n" << "Uniform and shader storage block variables must " << "also be decorated with a *Binding*."; @@ -1200,6 +1324,24 @@ storageClass == spv::StorageClass::Workgroup && vstate.HasCapability( spv::Capability::WorkgroupMemoryExplicitLayoutKHR); + + if (spvIsVulkanEnv(vstate.context()->target_env) && + inst.opcode() == spv::Op::OpUntypedVariableKHR && + storageClass != spv::StorageClass::UniformConstant && + vstate.IsDescriptorHeapBaseVariable(&inst)) { + if (vstate.IsBuiltin(inst.id(), spv::BuiltIn::ResourceHeapEXT)) { + return vstate.diag(SPV_ERROR_INVALID_DATA, &inst) + << vstate.VkErrorID(11241) + << "The variable decorated with ResourceHeapEXT must be " + << "declared using the UniformConstant storage class."; + } + if (vstate.IsBuiltin(inst.id(), spv::BuiltIn::SamplerHeapEXT)) { + return vstate.diag(SPV_ERROR_INVALID_DATA, &inst) + << vstate.VkErrorID(11239) + << "The variable decorated with SamplerHeapEXT must be " + << "declared using the UniformConstant storage class."; + } + } if (uniform || push_constant || storage_buffer || phys_storage_buffer || workgroup) { const auto ptrInst = vstate.FindDef(words[1]); @@ -1221,12 +1363,6 @@ ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(), vstate); } - // Prepare for messages - const char* sc_str = - uniform ? "Uniform" - : (push_constant ? "PushConstant" - : (workgroup ? "Workgroup" - : "StorageBuffer")); if (spvIsVulkanEnv(vstate.context()->target_env)) { const bool block = hasDecoration(id, spv::Decoration::Block, vstate); @@ -1264,30 +1400,6 @@ << "Such variables must be identified with a Block or " "BufferBlock decoration"; } - // Vulkan: Check DescriptorSet and Binding decoration for - // Uniform and StorageBuffer variables. - if (uniform || storage_buffer) { - auto entry_points = vstate.EntryPointReferences(var_id); - if (!entry_points.empty() && - !hasDecoration(var_id, spv::Decoration::DescriptorSet, - vstate)) { - return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << vstate.VkErrorID(6677) << sc_str << " id '" << var_id - << "' is missing DescriptorSet decoration.\n" - << "From Vulkan spec:\n" - << "These variables must have DescriptorSet and Binding " - "decorations specified"; - } - if (!entry_points.empty() && - !hasDecoration(var_id, spv::Decoration::Binding, vstate)) { - return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << vstate.VkErrorID(6677) << sc_str << " id '" << var_id - << "' is missing Binding decoration.\n" - << "From Vulkan spec:\n" - << "These variables must have DescriptorSet and Binding " - "decorations specified"; - } - } } if (id != 0) { @@ -1327,12 +1439,14 @@ if (!checkForRequiredDecoration( id, [](spv::Decoration d) { - return d == spv::Decoration::ArrayStride; + return d == spv::Decoration::ArrayStride || + d == spv::Decoration::ArrayStrideIdEXT; }, spv::Op::OpTypeArray, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "Structure id " << id << " decorated as " << deco_str - << " must be explicitly laid out with ArrayStride " + << " must be explicitly laid out with ArrayStride or " + "ArrayStrideIdEXT " "decorations."; } @@ -1364,14 +1478,14 @@ if (spvIsVulkanEnv(vstate.context()->target_env)) { if (blockRules && (SPV_SUCCESS != - (recursive_status = checkLayout(id, sc_str, deco_str, true, - scalar_block_layout, 0, - constraints, vstate)))) { + (recursive_status = checkLayout( + id, storageClass, deco_str, true, scalar_block_layout, + 0, constraints, vstate)))) { return recursive_status; } else if (bufferRules && (SPV_SUCCESS != (recursive_status = checkLayout( - id, sc_str, deco_str, false, - scalar_block_layout, 0, + id, storageClass, deco_str, + false, scalar_block_layout, 0, constraints, vstate)))) { return recursive_status; } @@ -1391,9 +1505,9 @@ ComputeMemberConstraintsForStruct(&constraints, pointee_type_id, LayoutConstraints(), vstate); } - if (auto res = checkLayout(pointee_type_id, "PhysicalStorageBuffer", - "Block", !buffer, scalar_block_layout, 0, - constraints, vstate)) { + if (auto res = checkLayout( + pointee_type_id, spv::StorageClass::PhysicalStorageBuffer, + "Block", !buffer, scalar_block_layout, 0, constraints, vstate)) { return res; } } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) && @@ -1442,19 +1556,29 @@ const auto sc = vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1); - const char* sc_str = - sc == spv::StorageClass::Uniform - ? "Uniform" - : (sc == spv::StorageClass::PushConstant - ? "PushConstant" - : (sc == spv::StorageClass::Workgroup ? "Workgroup" - : "StorageBuffer")); - - const auto data_type = vstate.FindDef(data_type_id); + auto data_type = vstate.FindDef(data_type_id); scalar_block_layout = sc == spv::StorageClass::Workgroup ? vstate.options()->workgroup_scalar_block_layout : vstate.options()->scalar_block_layout; + + // If the data type is an array that contains a Block- or + // BufferBlock-decorated struct, then use the struct for layout checks + // instead of the array. In this case, the array represents a descriptor + // array which should not have an explicit layout. + if (data_type->opcode() == spv::Op::OpTypeArray || + data_type->opcode() == spv::Op::OpTypeRuntimeArray) { + const auto ele_type = + vstate.FindDef(data_type->GetOperandAs<uint32_t>(1u)); + if (ele_type->opcode() == spv::Op::OpTypeStruct && + (vstate.HasDecoration(ele_type->id(), spv::Decoration::Block) || + vstate.HasDecoration(ele_type->id(), + spv::Decoration::BufferBlock))) { + data_type = ele_type; + data_type_id = ele_type->id(); + } + } + // Assume uniform storage class uses block rules unless we see a // BufferBlock decorated struct in the data type. bool bufferRules = sc == spv::StorageClass::Uniform ? false : true; @@ -1470,10 +1594,13 @@ bufferRules ? (sc == spv::StorageClass::Uniform ? "BufferBlock" : "Block") : "Block"; - if (auto result = - checkLayout(data_type_id, sc_str, deco_str, !bufferRules, - scalar_block_layout, 0, constraints, vstate)) { - return result; + + if (!vstate.IsDescriptorHeapBaseVariable(&inst)) { + if (auto result = + checkLayout(data_type_id, sc, deco_str, !bufferRules, + scalar_block_layout, 0, constraints, vstate)) { + return result; + } } } } @@ -1500,14 +1627,16 @@ // to the same id. static const spv::Decoration mutually_exclusive_per_id[][2] = { {spv::Decoration::Block, spv::Decoration::BufferBlock}, - {spv::Decoration::Restrict, spv::Decoration::Aliased}}; + {spv::Decoration::Restrict, spv::Decoration::Aliased}, + {spv::Decoration::RestrictPointer, spv::Decoration::AliasedPointer}}; static const auto num_mutually_exclusive_per_id_pairs = sizeof(mutually_exclusive_per_id) / (2 * sizeof(spv::Decoration)); // An Array of pairs where the decorations in the pair cannot both be applied // to the same member. static const spv::Decoration mutually_exclusive_per_member[][2] = { - {spv::Decoration::RowMajor, spv::Decoration::ColMajor}}; + {spv::Decoration::RowMajor, spv::Decoration::ColMajor}, + {spv::Decoration::Offset, spv::Decoration::OffsetIdEXT}}; static const auto num_mutually_exclusive_per_mem_pairs = sizeof(mutually_exclusive_per_member) / (2 * sizeof(spv::Decoration)); @@ -1549,7 +1678,8 @@ << " is not allowed."; } } - } else if (spv::Op::OpMemberDecorate == inst.opcode()) { + } else if (spv::Op::OpMemberDecorate == inst.opcode() || + spv::Op::OpMemberDecorateIdEXT == inst.opcode()) { const auto id = words[1]; const auto member_id = words[2]; const auto dec_type = static_cast<spv::Decoration>(words[3]); @@ -1691,14 +1821,33 @@ return SPV_SUCCESS; } -// Returns SPV_SUCCESS if validation rules are satisfied for the NonWritable +spv_result_t CheckFPRoundingModeForKernels(ValidationState_t& vstate, + const Instruction& inst) { + // Validates conversion instruction to or from a floating-point type + const auto opcode = inst.opcode(); + if (opcode != spv::Op::OpConvertFToU && opcode != spv::Op::OpConvertFToS && + opcode != spv::Op::OpConvertSToF && opcode != spv::Op::OpConvertUToF && + opcode != spv::Op::OpFConvert) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to a conversion " + "instruction to or from a floating-point type."; + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the NonReadable or +// NonWritable // decoration. Otherwise emits a diagnostic and returns something other than // SPV_SUCCESS. The |inst| parameter is the object being decorated. This must // be called after TypePass and AnnotateCheckDecorationsOfBuffers are called. -spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate, - const Instruction& inst, - const Decoration& decoration) { +spv_result_t CheckNonReadableWritableDecorations(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + const bool is_non_writable = + decoration.dec_type() == spv::Decoration::NonWritable; + assert(is_non_writable || + decoration.dec_type() == spv::Decoration::NonReadable); if (decoration.struct_member_index() == Decoration::kInvalidMember) { // The target must be a memory object declaration. @@ -1707,10 +1856,14 @@ const auto type_id = inst.type_id(); if (opcode != spv::Op::OpVariable && opcode != spv::Op::OpUntypedVariableKHR && + opcode != spv::Op::OpBufferPointerEXT && opcode != spv::Op::OpFunctionParameter && opcode != spv::Op::OpRawAccessChainNV) { return vstate.diag(SPV_ERROR_INVALID_ID, &inst) - << "Target of NonWritable decoration must be a memory object " + << "Target of " + << (is_non_writable ? "NonWritable" : "NonReadable") + << " decoration must be a " + "memory object " "declaration (a variable or a function parameter)"; } const auto var_storage_class = @@ -1719,21 +1872,42 @@ : opcode == spv::Op::OpUntypedVariableKHR ? inst.GetOperandAs<spv::StorageClass>(3) : spv::StorageClass::Max; + + if (opcode == spv::Op::OpBufferPointerEXT) { + auto result_type = vstate.FindDef(inst.type_id()); + auto sc = result_type->GetOperandAs<spv::StorageClass>(1); + if (sc == spv::StorageClass::Uniform && is_non_writable) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of NonWritable decoration is invalid: " + << "cannot be used to OpBufferPointerEXT " + << "with Uniform storage class"; + } + return SPV_SUCCESS; + } + if ((var_storage_class == spv::StorageClass::Function || var_storage_class == spv::StorageClass::Private) && - vstate.features().nonwritable_var_in_function_or_private) { + vstate.features().nonwritable_var_in_function_or_private && + is_non_writable) { // New permitted feature in SPIR-V 1.4. + } else if (var_storage_class == spv::StorageClass::TileAttachmentQCOM) { } else if ( // It may point to a UBO, SSBO, storage image, or raw access chain. vstate.IsPointerToUniformBlock(type_id) || vstate.IsPointerToStorageBuffer(type_id) || vstate.IsPointerToStorageImage(type_id) || + vstate.IsPointerToTensor(type_id) || opcode == spv::Op::OpRawAccessChainNV) { } else { return vstate.diag(SPV_ERROR_INVALID_ID, &inst) - << "Target of NonWritable decoration is invalid: must point to a " - "storage image, uniform block, " - << (vstate.features().nonwritable_var_in_function_or_private + << "Target of " + << (is_non_writable ? "NonWritable" : "NonReadable") + << " decoration is invalid: " + "must point to a " + "storage image, tensor variable in UniformConstant storage " + "class, uniform block, " + << (vstate.features().nonwritable_var_in_function_or_private && + is_non_writable ? "storage buffer, or variable in Private or Function " "storage class" : "or storage buffer"); @@ -1883,7 +2057,7 @@ if (!vstate.IsIntScalarOrVectorType(type_id) && !vstate.IsFloatScalarOrVectorType(type_id)) { return vstate.diag(SPV_ERROR_INVALID_ID, &inst) - << vstate.VkErrorID(4924) + << vstate.VkErrorID(10583) << "Component decoration specified for type " << vstate.getIdName(type_id) << " that is not a scalar or vector"; } @@ -1988,15 +2162,16 @@ { \ spv_result_t e##LINE = (X); \ if (e##LINE != SPV_SUCCESS) return e##LINE; \ - } static_assert(true, "require extra semicolon") + } \ + static_assert(true, "require extra semicolon") #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__) // Check rules for decorations where we start from the decoration rather // than the decorated object. Assumes each decoration on a group have been // propagated down to the group members. spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { - // Some rules are only checked for shaders. const bool is_shader = vstate.HasCapability(spv::Capability::Shader); + const bool is_kernel = vstate.HasCapability(spv::Capability::Kernel); for (const auto& kv : vstate.id_decorations()) { const uint32_t id = kv.first; @@ -2019,9 +2194,13 @@ if (is_shader) PASS_OR_BAIL( CheckFPRoundingModeForShaders(vstate, *inst, decoration)); + if (is_kernel) + PASS_OR_BAIL(CheckFPRoundingModeForKernels(vstate, *inst)); break; + case spv::Decoration::NonReadable: case spv::Decoration::NonWritable: - PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration)); + PASS_OR_BAIL( + CheckNonReadableWritableDecorations(vstate, *inst, decoration)); break; case spv::Decoration::Uniform: case spv::Decoration::UniformId: @@ -2050,17 +2229,273 @@ return SPV_SUCCESS; } +bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) { + switch (sc) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::Uniform: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::PushConstant: + // Always explicitly laid out. + return true; + case spv::StorageClass::UniformConstant: + return false; + case spv::StorageClass::Workgroup: + return vstate.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayoutKHR); + case spv::StorageClass::Function: + case spv::StorageClass::Private: + return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4); + case spv::StorageClass::Input: + case spv::StorageClass::Output: + // Block is used generally and mesh shaders use Offset. + return true; + default: + // TODO: Some storage classes in ray tracing use explicit layout + // decorations, but it is not well documented which. For now treat other + // storage classes as allowed to be laid out. See Vulkan internal issue + // 4192. + return true; + } +} + +// Returns a decoration used to make it explicit +spv::Decoration UsesExplicitLayout( + ValidationState_t& vstate, uint32_t type_id, + std::unordered_map<uint32_t, spv::Decoration>& cache) { + if (type_id == 0) { + return spv::Decoration::Max; + } + + if (cache.count(type_id)) { + return cache[type_id]; + } + + spv::Decoration res = spv::Decoration::Max; + const auto type_inst = vstate.FindDef(type_id); + if (type_inst->opcode() == spv::Op::OpTypeStruct || + type_inst->opcode() == spv::Op::OpTypeArray || + type_inst->opcode() == spv::Op::OpTypeRuntimeArray || + type_inst->opcode() == spv::Op::OpTypePointer || + type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + const auto& id_decs = vstate.id_decorations(); + const auto iter = id_decs.find(type_id); + if (iter != id_decs.end()) { + bool allowLayoutDecorations = false; + if (type_inst->opcode() == spv::Op::OpTypePointer || + type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1); + allowLayoutDecorations = AllowsLayout(vstate, sc); + } + if (!allowLayoutDecorations) { + for (const auto& d : iter->second) { + const spv::Decoration dec = d.dec_type(); + if (dec == spv::Decoration::Block || + dec == spv::Decoration::BufferBlock || + dec == spv::Decoration::Offset || + dec == spv::Decoration::ArrayStride || + dec == spv::Decoration::MatrixStride) { + res = dec; + break; + } + } + } + } + + if (res == spv::Decoration::Max) { + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: + for (uint32_t i = 1; + res == spv::Decoration::Max && i < type_inst->operands().size(); + i++) { + res = UsesExplicitLayout( + vstate, type_inst->GetOperandAs<uint32_t>(i), cache); + } + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + res = UsesExplicitLayout(vstate, type_inst->GetOperandAs<uint32_t>(1), + cache); + break; + case spv::Op::OpTypePointer: { + const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1); + if (!AllowsLayout(vstate, sc)) { + res = UsesExplicitLayout( + vstate, type_inst->GetOperandAs<uint32_t>(2), cache); + } + } + default: + break; + } + } + } + + cache[type_id] = res; + return res; +} + +spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) { + if (!spvIsVulkanEnv(vstate.context()->target_env)) { + return SPV_SUCCESS; + } + + std::unordered_map<uint32_t, spv::Decoration> cache; + for (const auto& inst : vstate.ordered_instructions()) { + const auto type_id = inst.type_id(); + const auto type_inst = vstate.FindDef(type_id); + + spv::StorageClass sc = spv::StorageClass::Max; + spv::Decoration layout_dec = spv::Decoration::Max; + uint32_t fail_id = 0; + uint32_t base_id = 0; + // Variables are the main place to check for improper decorations, but some + // untyped pointer instructions must also be checked since those types may + // never be instantiated by a variable. Unlike verifying a valid layout, + // physical storage buffer does not need checked here since it is always + // explicitly laid out. + switch (inst.opcode()) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: { + sc = inst.GetOperandAs<spv::StorageClass>(2); + auto check_id = type_id; + base_id = inst.id(); + if (inst.opcode() == spv::Op::OpUntypedVariableKHR) { + if (inst.operands().size() > 3) { + check_id = inst.GetOperandAs<uint32_t>(3); + } + } + if (!AllowsLayout(vstate, sc)) { + layout_dec = UsesExplicitLayout(vstate, check_id, cache); + if (layout_dec != spv::Decoration::Max) { + fail_id = check_id; + } + } + break; + } + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: { + // Check both the base type and return type. The return type may have an + // invalid array stride. + sc = type_inst->GetOperandAs<spv::StorageClass>(1); + base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id(); + if (!AllowsLayout(vstate, sc)) { + const auto base_type_id = inst.GetOperandAs<uint32_t>(2); + layout_dec = UsesExplicitLayout(vstate, base_type_id, cache); + if (layout_dec != spv::Decoration::Max) { + fail_id = base_type_id; + } else { + layout_dec = UsesExplicitLayout(vstate, type_id, cache); + if (layout_dec != spv::Decoration::Max) { + fail_id = type_id; + } + } + } + break; + } + case spv::Op::OpUntypedArrayLengthKHR: { + // Check the data type. + const auto ptr_ty_id = + vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id(); + const auto ptr_ty = vstate.FindDef(ptr_ty_id); + sc = ptr_ty->GetOperandAs<spv::StorageClass>(1); + base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id(); + if (!AllowsLayout(vstate, sc)) { + const auto base_type_id = inst.GetOperandAs<uint32_t>(2); + layout_dec = UsesExplicitLayout(vstate, base_type_id, cache); + if (layout_dec != spv::Decoration::Max) { + fail_id = base_type_id; + } + } + break; + } + case spv::Op::OpLoad: { + const auto ptr_id = inst.GetOperandAs<uint32_t>(2); + const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id()); + base_id = ptr_id; + if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + // For untyped pointers check the return type for an invalid layout. + sc = ptr_type->GetOperandAs<spv::StorageClass>(1); + if (!AllowsLayout(vstate, sc)) { + layout_dec = UsesExplicitLayout(vstate, type_id, cache); + if (layout_dec != spv::Decoration::Max) { + fail_id = type_id; + } + } + } + break; + } + case spv::Op::OpStore: { + const auto ptr_id = inst.GetOperandAs<uint32_t>(1); + const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id()); + base_id = inst.GetOperandAs<uint32_t>(0); + if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + // For untyped pointers, check the type of the data operand for an + // invalid layout. + sc = ptr_type->GetOperandAs<spv::StorageClass>(1); + if (!AllowsLayout(vstate, sc)) { + const auto data_type_id = vstate.GetOperandTypeId(&inst, 1); + layout_dec = UsesExplicitLayout(vstate, data_type_id, cache); + if (layout_dec != spv::Decoration::Max) { + fail_id = inst.GetOperandAs<uint32_t>(2); + } + } + } + break; + } + case spv::Op::OpBufferPointerEXT: { + const auto ptr_id = inst.GetOperandAs<uint32_t>(1); + const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id()); + // Check the type of the data operand for an invalid layout. + sc = ptr_type->GetOperandAs<spv::StorageClass>(1); + if (!AllowsLayout(vstate, sc) && + UsesExplicitLayout(vstate, type_id, cache) != + spv::Decoration::Max) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(11346) + << "The result type operand of OpBufferPointerEXT must have " + << "a Type operand that is explicitly laid out : " + << vstate.getIdName(type_id); + } else if (sc != spv::StorageClass::StorageBuffer && + sc != spv::StorageClass::Uniform) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "OpBufferPointerEXT's Result Type must be a pointer type " + << "with a Storage Class of Uniform or StorageBuffer."; + } + break; + } + default: + break; + } + + if (fail_id != 0 && + !vstate.IsDescriptorHeapBaseVariable(vstate.FindDef(base_id))) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(10684) + << "Invalid explicit layout decorations on type for operand " + << vstate.getIdName(fail_id) << ", the " + << spvtools::StorageClassToString(sc) + << " storage class has a explicit layout from the " + << vstate.SpvDecorationString(layout_dec) << " decoration."; + } + } + + return SPV_SUCCESS; +} + } // namespace spv_result_t ValidateDecorations(ValidationState_t& vstate) { if (auto error = CheckImportedVariableInitialization(vstate)) return error; if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error; if (auto error = CheckDecorationsOfBuffers(vstate)) return error; + if (auto error = CheckDecorationsOfVariables(vstate)) return error; if (auto error = CheckDecorationsCompatibility(vstate)) return error; if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error; if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate)) return error; if (auto error = CheckDecorationsFromDecoration(vstate)) return error; + if (auto error = CheckInvalidVulkanExplicitLayout(vstate)) return error; return SPV_SUCCESS; }
diff --git a/source/val/validate_dot_product.cpp b/source/val/validate_dot_product.cpp new file mode 100644 index 0000000..298baf4 --- /dev/null +++ b/source/val/validate_dot_product.cpp
@@ -0,0 +1,407 @@ +// Copyright (c) 2026 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstdint> + +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateSameSignedDot(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_id = inst->type_id(); + if (!_.IsIntScalarType(result_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an int scalar type."; + } + + const spv::Op opcode = inst->opcode(); + const bool has_accumulator = opcode == spv::Op::OpSDotAccSat || + opcode == spv::Op::OpUDotAccSat || + opcode == spv::Op::OpSUDotAccSat; + if (has_accumulator) { + const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4); + if (accumulator_type != result_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be the same as the Accumulator type."; + } + } + + if (opcode == spv::Op::OpUDot || opcode == spv::Op::OpUDotAccSat) { + if (!_.IsIntScalarTypeWithSignedness(result_id, 0)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an unsigned int scalar type."; + } + } + + const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2); + const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3); + + const bool is_vec_1_scalar = _.IsIntScalarType(vec_1_id, 32); + const bool is_vec_2_scalar = _.IsIntScalarType(vec_2_id, 32); + if (is_vec_1_scalar != is_vec_2_scalar) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' and 'Vector 2' must be the same type."; + } else if (is_vec_1_scalar && is_vec_2_scalar) { + if (!_.HasCapability(spv::Capability::DotProductInput4x8BitPacked)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductInput4x8BitPacked capability is required to use " + "scalar integers."; + } + + // If both are scalar, spec doesn't say Signedness needs to match + const uint32_t vec_1_width = _.GetBitWidth(vec_1_id); + const uint32_t vec_2_width = _.GetBitWidth(vec_2_id); + if (vec_1_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be 32-bit when a scalar."; + } else if (vec_2_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be 32-bit when a scalar."; + } + + // When packed, the result can be 8-bit + const uint32_t result_width = _.GetBitWidth(result_id); + if (result_width < 8) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result width (" << result_width + << ") must be greater than or equal to the packed vector width of " + "8"; + } + + // PackedVectorFormat4x8Bit is used when the "Vector" operand are really + // scalar + const uint32_t packed_operand = has_accumulator ? 6 : 5; + const bool has_packed_vec_format = + inst->operands().size() == packed_operand; + if (!has_packed_vec_format) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' and 'Vector 2' are a 32-bit int scalar, but no " + "Packed Vector " + "Format was provided."; + } + } else { + // both should be vectors + + if (!_.IsVectorType(vec_1_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be an int scalar or vector."; + } else if (!_.IsVectorType(vec_2_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be an int scalar or vector."; + } + + const uint32_t vec_1_length = _.GetDimension(vec_1_id); + const uint32_t vec_2_length = _.GetDimension(vec_2_id); + // If using OpTypeVectorIdEXT with a spec constant, this can be evaluated + // when spec constants are frozen + if (vec_1_length != 0 && vec_2_length != 0 && + vec_1_length != vec_2_length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' is " << vec_1_length + << " components but 'Vector 2' is " << vec_2_length + << " components"; + } + + const uint32_t vec_1_type = _.GetComponentType(vec_1_id); + const uint32_t vec_2_type = _.GetComponentType(vec_2_id); + if (!_.IsIntScalarType(vec_1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be a vector of integers."; + } else if (!_.IsIntScalarType(vec_2_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be a vector of integers."; + } + + const uint32_t vec_1_width = _.GetBitWidth(vec_1_type); + const uint32_t vec_2_width = _.GetBitWidth(vec_2_type); + if (vec_1_width != vec_2_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' component is " << vec_1_width + << "-bit but 'Vector 2' component is " << vec_2_width << "-bit"; + } + + const uint32_t result_width = _.GetBitWidth(result_id); + if (result_width < vec_1_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result width (" << result_width + << ") must be greater than or equal to the vectors width (" + << vec_1_width << ")."; + } + + if (!_.HasCapability(spv::Capability::DotProductInputAll)) { + // 4-wide 8-bit ints are special exception that has its own capability + if (vec_1_length == 4 && vec_1_width == 8) { + if (!_.HasCapability(spv::Capability::DotProductInput4x8Bit)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductInput4x8Bit or DotProductInputAll capability is " + "required to use 4-component vectors of 8-bit integers."; + } + } else { + // provide a more helpful message what is going on if we are here + // reporting this error + if (_.HasCapability(spv::Capability::DotProductInput4x8BitPacked)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductInputAll capability is required use vectors. " + "(DotProductInput4x8BitPacked capability declared allows " + "for only 32-bit int scalars)"; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductInputAll capability is additionally required to " + "the DotProduct capability to use vectors. (It is possible " + "to set DotProductInput4x8BitPacked to only use 32-bit " + "scalars packed as a 4-wide 8-byte vector)"; + } + } + } + + if (opcode == spv::Op::OpUDot || opcode == spv::Op::OpUDotAccSat) { + const bool vec_1_unsigned = + _.IsIntScalarTypeWithSignedness(vec_1_type, 0); + const bool vec_2_unsigned = + _.IsIntScalarTypeWithSignedness(vec_2_type, 0); + if (!vec_1_unsigned) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be an vector of unsigned integers."; + } else if (!vec_2_unsigned) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be an vector of unsigned integers."; + } + } else if (opcode == spv::Op::OpSUDot || opcode == spv::Op::OpSUDotAccSat) { + const bool vec_2_unsigned = + _.IsIntScalarTypeWithSignedness(vec_2_type, 0); + if (!vec_2_unsigned) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be an vector of unsigned integers."; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFDotMixVectors(ValidationState_t& _, + const Instruction* inst, uint32_t vec_1_id, + uint32_t vec_2_id, uint32_t length) { + if (!_.IsVectorType(vec_1_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be an vector."; + } else if (!_.IsVectorType(vec_2_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be an vector."; + } + + // If using OpTypeVectorIdEXT with a spec constant, + // this can be evaluated when spec constants are frozen + const uint32_t vec_1_length = _.GetDimension(vec_1_id); + const uint32_t vec_2_length = _.GetDimension(vec_2_id); + if (vec_1_length != 0 && vec_1_length != length && vec_2_length != 0 && + vec_2_length != length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' is " << vec_1_length + << " components and 'Vector 2' is " << vec_2_length + << " components, but both need to be " << length << "-components"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFDot2MixAcc32(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_id = inst->type_id(); + if (!_.IsFloatScalarType(result_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 32-bit IEEE 754 float scalar type."; + } + + const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2); + const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3); + + if (auto error = ValidateFDotMixVectors(_, inst, vec_1_id, vec_2_id, 2)) + return error; + + const uint32_t vec_1_type = _.GetComponentType(vec_1_id); + const uint32_t vec_2_type = _.GetComponentType(vec_2_id); + if (!_.IsFloatScalarType(vec_1_type, 16)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be a vector of 16-bit floats."; + } else if (!_.IsFloatScalarType(vec_2_type, 16)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be a vector of 16-bit floats."; + } + + // Currently 16-bit floats are only BFloat or IEEE 754 + const bool is_vec_1_bfloat = _.IsBfloat16ScalarType(vec_1_type); + const bool is_vec_2_bfloat = _.IsBfloat16ScalarType(vec_2_type); + if (is_vec_1_bfloat != is_vec_2_bfloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' and 'Vector 2' must be the same float encoding."; + } + + if (is_vec_1_bfloat) { + if (!_.HasCapability(spv::Capability::DotProductBFloat16AccVALVE)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductBFloat16AccVALVE capability is required to use " + "BFloat16 encoded floats."; + } + } else { + if (!_.HasCapability(spv::Capability::DotProductFloat16AccFloat32VALVE)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductFloat16AccFloat32VALVE capability is required to " + "use " + "IEEE 754 encoded 16-bit floats."; + } + } + + const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4); + if (accumulator_type != result_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Accumulator Type must be the same as the Result Type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFDot2MixAcc16(ValidationState_t& _, + const Instruction* inst) { + const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2); + const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3); + + if (auto error = ValidateFDotMixVectors(_, inst, vec_1_id, vec_2_id, 2)) + return error; + + const uint32_t vec_1_type = _.GetComponentType(vec_1_id); + const uint32_t vec_2_type = _.GetComponentType(vec_2_id); + if (!_.IsFloatScalarType(vec_1_type, 16)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be a vector of 16-bit floats."; + } else if (!_.IsFloatScalarType(vec_2_type, 16)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be a vector of 16-bit floats."; + } + + // Currently 16-bit floats are only BFloat or IEEE 754 + const bool is_vec_1_bfloat = _.IsBfloat16ScalarType(vec_1_type); + const bool is_vec_2_bfloat = _.IsBfloat16ScalarType(vec_2_type); + if (is_vec_1_bfloat != is_vec_2_bfloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "'Vector 1' and 'Vector 2' must be the same float encoding."; + } + + if (is_vec_1_bfloat) { + if (!_.HasCapability(spv::Capability::DotProductBFloat16AccVALVE)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductBFloat16AccVALVE capability is required to use " + "BFloat16 encoded floats."; + } + } else { + if (!_.HasCapability(spv::Capability::DotProductFloat16AccFloat16VALVE)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "DotProductFloat16AccFloat16VALVE capability is required to " + "use " + "IEEE 754 encoded 16-bit floats."; + } + } + + const uint32_t result_id = inst->type_id(); + if (!_.IsFloatScalarType(result_id, 16)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 16-bit float scalar type."; + } + + const bool is_result_bfloat = _.IsBfloat16ScalarType(result_id); + if (is_result_bfloat != is_vec_1_bfloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must have the same float encoding as 'Vector 1' and " + "'Vector 2'."; + } + + const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4); + if (accumulator_type != result_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Accumulator Type must be the same as the Result Type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFDot4MixAcc32(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_id = inst->type_id(); + if (!_.IsFloatScalarType(result_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 32-bit IEEE 754 float scalar type."; + } + + const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2); + const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3); + + if (auto error = ValidateFDotMixVectors(_, inst, vec_1_id, vec_2_id, 4)) + return error; + + // Currently 8-bit floats are only Float8E4M3/Float8E5M2 + const uint32_t vec_1_type = _.GetComponentType(vec_1_id); + const uint32_t vec_2_type = _.GetComponentType(vec_2_id); + if (!_.IsFloatScalarType(vec_1_type, 8)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 1' to be a vector of 8-bit floats."; + } else if (!_.IsFloatScalarType(vec_2_type, 8)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 'Vector 2' to be a vector of 8-bit floats."; + } + + const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4); + if (accumulator_type != result_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Accumulator Type must be the same as the Result Type."; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t DotProductPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + switch (opcode) { + case spv::Op::OpSDot: + case spv::Op::OpUDot: + case spv::Op::OpSUDot: + case spv::Op::OpSDotAccSat: + case spv::Op::OpUDotAccSat: + case spv::Op::OpSUDotAccSat: + return ValidateSameSignedDot(_, inst); + // Tried combining these to a single validate function, but they are less + // similar than appeared at first glance + case spv::Op::OpFDot2MixAcc32VALVE: + return ValidateFDot2MixAcc32(_, inst); + case spv::Op::OpFDot2MixAcc16VALVE: + return ValidateFDot2MixAcc16(_, inst); + case spv::Op::OpFDot4MixAcc32VALVE: + return ValidateFDot4MixAcc32(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index cb4768d..831823b 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp
@@ -13,38 +13,41 @@ // limitations under the License. // Validates correctness of extension SPIR-V instructions. +#include <algorithm> +#include <cstdint> #include <cstdlib> #include <sstream> #include <string> #include <vector> -#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/common_debug_info.h" -#include "source/enum_string_mapping.h" #include "source/extensions.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_opencl_std_header.h" +#include "source/opcode.h" #include "source/spirv_constant.h" +#include "source/table2.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" +#include "spirv-tools/libspirv.h" #include "spirv/unified1/NonSemanticClspvReflection.h" +#include "spirv/unified1/NonSemanticShaderDebugInfo.h" namespace spvtools { namespace val { namespace { -std::string ReflectionInstructionName(ValidationState_t& _, - const Instruction* inst) { - spv_ext_inst_desc desc = nullptr; - if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, - inst->word(4), &desc) != SPV_SUCCESS || +std::string ReflectionInstructionName(const Instruction* inst) { + const ExtInstDesc* desc = nullptr; + if (LookupExtInst(SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + inst->word(4), &desc) != SPV_SUCCESS || !desc) { return std::string("Unknown ExtInst"); } std::ostringstream ss; - ss << desc->name; + ss << desc->name().data(); return ss.str(); } @@ -85,25 +88,63 @@ return inst->word(3); } +std::string GetExtInstName(const ValidationState_t& _, + const Instruction* inst) { + const uint32_t ext_inst_set = inst->word(3); + const uint32_t ext_inst_index = inst->word(4); + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + + const ExtInstDesc* desc = nullptr; + if (LookupExtInst(ext_inst_type, ext_inst_index, &desc) != SPV_SUCCESS || + !desc) { + return std::string("Unknown ExtInst"); + } + + auto* import_inst = _.FindDef(ext_inst_set); + assert(import_inst); + + std::ostringstream ss; + ss << import_inst->GetOperandAs<std::string>(1); + ss << " "; + ss << desc->name().data(); + + return ss.str(); +} + +// Returns the declared NSDI version from the OpExtInstImport referenced by +// |inst|. Returns 0 if not a NonSemantic.Shader.DebugInfo import. +uint32_t GetNSDIVersion(const ValidationState_t& _, const Instruction* inst) { + const auto* import_inst = _.FindDef(inst->word(3)); + if (!import_inst) return 0; + const std::string name = import_inst->GetOperandAs<std::string>(1); + const char kPrefix[] = "NonSemantic.Shader.DebugInfo."; + if (name.find(kPrefix) != 0) return 0; + return static_cast<uint32_t>( + std::strtoul(name.c_str() + sizeof(kPrefix) - 1, nullptr, 10)); +} + // Check that the operand of a debug info instruction |inst| at |word_index| // is a result id of an instruction with |expected_opcode|. -spv_result_t ValidateOperandForDebugInfo( - ValidationState_t& _, const std::string& operand_name, - spv::Op expected_opcode, const Instruction* inst, uint32_t word_index, - const std::function<std::string()>& ext_inst_name) { +spv_result_t ValidateOperandForDebugInfo(ValidationState_t& _, + const std::string& operand_name, + spv::Op expected_opcode, + const Instruction* inst, + uint32_t word_index) { auto* operand = _.FindDef(inst->word(word_index)); if (operand->opcode() != expected_opcode) { - spv_opcode_desc desc = nullptr; - if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS || + const spvtools::InstructionDesc* desc = nullptr; + if (spvtools::LookupOpcodeForEnv(_.context()->target_env, expected_opcode, + &desc) != SPV_SUCCESS || !desc) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand " << operand_name << " is invalid"; + << GetExtInstName(_, inst) << ": " << "expected operand " + << operand_name << " is invalid"; } return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand " << operand_name << " must be a result id of " - << "Op" << desc->name; + << GetExtInstName(_, inst) << ": " << "expected operand " + << operand_name << " must be a result id of " << "Op" + << desc->name().data(); } return SPV_SUCCESS; } @@ -115,27 +156,57 @@ spv_result_t ValidateUint32ConstantOperandForDebugInfo( ValidationState_t& _, const std::string& operand_name, const Instruction* inst, uint32_t word_index, - const std::function<std::string()>& ext_inst_name) { - if (!IsUint32Constant(_, inst->word(word_index))) { + bool allow_spec_const = false) { + const uint32_t id = inst->word(word_index); + if (!IsUint32Constant(_, id)) { + if (allow_spec_const) { + auto* def = _.FindDef(id); + if (def && spvOpcodeIsSpecConstant(def->opcode()) && + IsIntScalar(_, def->type_id(), true, true)) + return SPV_SUCCESS; + } return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand " << operand_name - << " must be a result id of 32-bit unsigned OpConstant"; + << GetExtInstName(_, inst) << ": expected operand " << operand_name + << " must be a result id of " + << (allow_spec_const + ? "a 32-bit unsigned integer constant or specialization" + " constant" + : "32-bit unsigned OpConstant"); } return SPV_SUCCESS; } -#define CHECK_OPERAND(NAME, opcode, index) \ - do { \ - auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ - ext_inst_name); \ - if (result != SPV_SUCCESS) return result; \ +#define CHECK_OPERAND(NAME, opcode, index) \ + do { \ + auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index); \ + if (result != SPV_SUCCESS) return result; \ } while (0) -#define CHECK_CONST_UINT_OPERAND(NAME, index) \ - if (vulkanDebugInfo) { \ - auto result = ValidateUint32ConstantOperandForDebugInfo( \ - _, NAME, inst, index, ext_inst_name); \ - if (result != SPV_SUCCESS) return result; \ +#define CHECK_CONST_UINT_OPERAND(NAME, index) \ + if (vulkanDebugInfo) { \ + auto result = \ + ValidateUint32ConstantOperandForDebugInfo(_, NAME, inst, index); \ + if (result != SPV_SUCCESS) return result; \ + } + +// Like CHECK_CONST_UINT_OPERAND but also allows spec-constants. Used for +// NonSemantic.Shader.DebugInfo.101 cooperative type instructions, where +// dimension operands may be specialization constants. +#define CHECK_CONST_OR_SPEC_UINT_OPERAND(NAME, index) \ + if (vulkanDebugInfo) { \ + auto result = \ + ValidateUint32ConstantOperandForDebugInfo(_, NAME, inst, index, true); \ + if (result != SPV_SUCCESS) return result; \ + } + +// Checks that the NSDI version for the current instruction is at least |v|. +// Used to guard opcodes added after version 100. +#define CHECK_NSDI_MIN_VERSION(v) \ + if (nsdi_version < (v)) { \ + return _.diag(SPV_ERROR_INVALID_DATA, inst) \ + << GetExtInstName(_, inst) \ + << ": requires NonSemantic.Shader.DebugInfo version " << (v) \ + << " or later"; \ } // True if the operand of a debug info instruction |inst| at |word_index| @@ -157,10 +228,10 @@ return true; } -// Overload for NonSemanticShaderDebugInfo100Instructions. +// Overload for NonSemanticShaderDebugInfoInstructions. bool DoesDebugInfoOperandMatchExpectation( const ValidationState_t& _, - const std::function<bool(NonSemanticShaderDebugInfo100Instructions)>& + const std::function<bool(NonSemanticShaderDebugInfoInstructions)>& expectation, const Instruction* inst, uint32_t word_index) { if (inst->words().size() <= word_index) return false; @@ -169,7 +240,7 @@ (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || !expectation( - NonSemanticShaderDebugInfo100Instructions(debug_inst->word(4)))) { + NonSemanticShaderDebugInfoInstructions(debug_inst->word(4)))) { return false; } return true; @@ -181,7 +252,7 @@ spv_result_t ValidateDebugInfoOperand( ValidationState_t& _, const std::string& debug_inst_name, CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst, - uint32_t word_index, const std::function<std::string()>& ext_inst_name) { + uint32_t word_index) { std::function<bool(CommonDebugInfoInstructions)> expectation = [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) { return dbg_inst == expected_debug_inst; @@ -189,44 +260,44 @@ if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; - spv_ext_inst_desc desc = nullptr; - if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst, - &desc) != SPV_SUCCESS || + const ExtInstDesc* desc = nullptr; + if (LookupExtInst(inst->ext_inst_type(), expected_debug_inst, &desc) != + SPV_SUCCESS || !desc) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand " << debug_inst_name << " is invalid"; + << GetExtInstName(_, inst) << ": " << "expected operand " + << debug_inst_name << " is invalid"; } return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand " << debug_inst_name << " must be a result id of " - << desc->name; + << GetExtInstName(_, inst) << ": " << "expected operand " + << debug_inst_name << " must be a result id of " + << desc->name().data(); } -#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \ - do { \ - auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \ - ext_inst_name); \ - if (result != SPV_SUCCESS) return result; \ +#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \ + do { \ + auto result = \ + ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index); \ + if (result != SPV_SUCCESS) return result; \ } while (0) // Check that the operand of a debug info instruction |inst| at |word_index| // is a result id of an debug info instruction with DebugTypeBasic. -spv_result_t ValidateOperandBaseType( - ValidationState_t& _, const Instruction* inst, uint32_t word_index, - const std::function<std::string()>& ext_inst_name) { +spv_result_t ValidateOperandBaseType(ValidationState_t& _, + const Instruction* inst, + uint32_t word_index) { return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic, - inst, word_index, ext_inst_name); + inst, word_index); } // Check that the operand of a debug info instruction |inst| at |word_index| // is a result id of a debug lexical scope instruction which is one of // DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or // DebugTypeComposite. -spv_result_t ValidateOperandLexicalScope( - ValidationState_t& _, const std::string& debug_inst_name, - const Instruction* inst, uint32_t word_index, - const std::function<std::string()>& ext_inst_name) { +spv_result_t ValidateOperandLexicalScope(ValidationState_t& _, + const std::string& debug_inst_name, + const Instruction* inst, + uint32_t word_index) { std::function<bool(CommonDebugInfoInstructions)> expectation = [](CommonDebugInfoInstructions dbg_inst) { return dbg_inst == CommonDebugInfoDebugCompilationUnit || @@ -238,25 +309,34 @@ return SPV_SUCCESS; return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand " << debug_inst_name - << " must be a result id of a lexical scope"; + << GetExtInstName(_, inst) << ": " << "expected operand " + << debug_inst_name << " must be a result id of a lexical scope"; } // Check that the operand of a debug info instruction |inst| at |word_index| // is a result id of a debug type instruction (See DebugTypeXXX in // "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec. -spv_result_t ValidateOperandDebugType( - ValidationState_t& _, const std::string& debug_inst_name, - const Instruction* inst, uint32_t word_index, - const std::function<std::string()>& ext_inst_name, - bool allow_template_param) { - // Check for NonSemanticShaderDebugInfo100 specific types. +spv_result_t ValidateOperandDebugType(ValidationState_t& _, + const std::string& debug_inst_name, + const Instruction* inst, + uint32_t word_index, + bool allow_template_param) { + // Check for NonSemanticShaderDebugInfo specific types. if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { - std::function<bool(NonSemanticShaderDebugInfo100Instructions)> expectation = - [](NonSemanticShaderDebugInfo100Instructions dbg_inst) { - return dbg_inst == NonSemanticShaderDebugInfo100DebugTypeMatrix; + const uint32_t nsdi_version = GetNSDIVersion(_, inst); + std::function<bool(NonSemanticShaderDebugInfoInstructions)> expectation = + [nsdi_version](NonSemanticShaderDebugInfoInstructions dbg_inst) { + if (dbg_inst == NonSemanticShaderDebugInfoDebugTypeMatrix) + return true; + // DebugTypeVectorIdEXT and DebugTypeCooperativeMatrixKHR were added + // in NonSemantic.Shader.DebugInfo version 101. + if (nsdi_version >= NonSemanticShaderDebugInfoVersion && + (dbg_inst == NonSemanticShaderDebugInfoDebugTypeVectorIdEXT || + dbg_inst == + NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR)) + return true; + return false; }; if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) return SPV_SUCCESS; @@ -277,15 +357,59 @@ return SPV_SUCCESS; return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand " << debug_inst_name - << " is not a valid debug type"; + << GetExtInstName(_, inst) << ": " << "expected operand " + << debug_inst_name << " is not a valid debug type"; +} + +spv_result_t ValidateOperandDebugSource(ValidationState_t& _, + const Instruction* inst, + uint32_t source_index, + uint32_t line_index, + uint32_t column_index, + spv_ext_inst_type_t ext_inst_type) { + auto* debug_source_inst = _.FindDef(inst->word(source_index)); + const std::vector<uint32_t>& line_lengths = + _.GetDebugSourceLineLength(debug_source_inst->id()); + if (line_lengths.empty()) + return SPV_SUCCESS; // Text not provide in DebugSource + + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + uint32_t line = 0; + uint32_t column = 0; + // NonSemantic uses OpConstant for all operands + if (vulkanDebugInfo) { + bool is_int32 = false, is_const_int32 = false; + std::tie(is_int32, is_const_int32, line) = + _.EvalInt32IfConst(inst->word(line_index)); + std::tie(is_int32, is_const_int32, column) = + _.EvalInt32IfConst(inst->word(column_index)); + } else { + line = inst->word(line_index); + column = inst->word(column_index); + } + + if (line > line_lengths.size()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Line (" << line + << ") is larger then the " << line_lengths.size() + << " lines found in the DebugSource text"; + } else if (line != 0) { + const uint32_t line_length = line_lengths[line - 1]; + if (column > line_length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Column End (" << column + << ") is larger then Line " << line << " column length of " + << line_length << " found in the DebugSource text"; + } + } + return SPV_SUCCESS; } spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, const Instruction* inst, uint32_t version) { - const auto inst_name = ReflectionInstructionName(_, inst); + const auto inst_name = ReflectionInstructionName(inst); const auto kernel_id = inst->GetOperandAs<uint32_t>(4); const auto kernel = _.FindDef(kernel_id); if (kernel->opcode() != spv::Op::OpFunction) { @@ -930,7 +1054,7 @@ } if (version < required_version) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << ReflectionInstructionName(_, inst) << " requires version " + << ReflectionInstructionName(inst) << " requires version " << required_version << ", but parsed version is " << version; } @@ -1002,6 +1126,104 @@ return SPV_SUCCESS; } +std::string GetDebugSourceText(ValidationState_t& _, const Instruction* inst, + uint32_t ext_inst_opcode) { + assert(ext_inst_opcode == NonSemanticShaderDebugInfoDebugSource || + ext_inst_opcode == NonSemanticShaderDebugInfoDebugSourceContinued); + const uint32_t string_operand = + (ext_inst_opcode == NonSemanticShaderDebugInfoDebugSource) ? 6 : 5; + auto* debug_source_text_insn = _.FindDef(inst->word(string_operand)); + // Validated to be an OpString + assert(debug_source_text_insn->opcode() == spv::Op::OpString); + return debug_source_text_insn->GetOperandAs<std::string>(1); +} + +// We build up a vector that is length of the DebugSource lines and get how long +// they are to make sure anyone using a DebugSource provides valid Line/Columns +// inside +void BuildDebugSourceLineLength(ValidationState_t& _, const Instruction* inst, + uint32_t ext_inst_index) { + if (ext_inst_index == NonSemanticShaderDebugInfoDebugSource && + inst->words().size() < 7) { + return; // The optional text was not provided + } + + std::string debug_source_text = GetDebugSourceText(_, inst, ext_inst_index); + + // walk back to get DebugSource to update it's line length list + uint32_t debug_source_id = inst->id(); + uint32_t continue_count = 0; + + // There might be + // %a = OpString "line starts here" + // %b = OpString " and still the same line" + // + // %c = OpExtInst %void %1 DebugSource %_ %a + // %d = OpExtInst %void %1 DebugSourceContinued %b + // So we want to find the previous line for checking if we need to append on + // to the length of the previous line + bool start_new_line = true; + if (ext_inst_index == NonSemanticShaderDebugInfoDebugSourceContinued) { + auto prev_index = inst - &_.ordered_instructions()[0] - 1; + auto prev_inst = &_.ordered_instructions()[prev_index]; + + std::string previous_line_text = + GetDebugSourceText(_, prev_inst, prev_inst->GetOperandAs<uint32_t>(3)); + if (!previous_line_text.empty() && previous_line_text.back() != '\n') { + start_new_line = false; + } + } + + while (ext_inst_index == NonSemanticShaderDebugInfoDebugSourceContinued) { + continue_count++; // might have multiple Continues in a row + auto prev_index = inst - &_.ordered_instructions()[0] - continue_count; + auto prev_inst = &_.ordered_instructions()[prev_index]; + debug_source_id = prev_inst->id(); + ext_inst_index = prev_inst->GetOperandAs<uint32_t>(3); + } + + std::vector<uint32_t>& line_lengths = + _.GetDebugSourceLineLength(debug_source_id); + uint32_t line_start = 0; + // If we have a line like "abc", it really column 1-to-4. + // Even an empty line should have a column of 1 + // Add 1 to length to emulate this later + uint32_t length = 1; + + // Continue from the previous line length + if (!start_new_line) { + length = line_lengths.back(); + line_lengths.pop_back(); + } + + for (uint32_t i = 0; i < debug_source_text.size(); ++i) { + if (debug_source_text[i] == '\n') { + // Unix-style new line + line_lengths.push_back(length); + length = 1; + line_start = i + 1; + } else if (debug_source_text[i] == '\r') { + // Handle Windows-style \r\n + if (i + 1 < debug_source_text.size() && + debug_source_text[i + 1] == '\n') { + line_lengths.push_back(length); + ++i; // Skip '\n' + } else { + line_lengths.push_back(length); + } + length = 1; + line_start = i + 1; + } else { + ++length; + } + } + + // Capture last line if the string does not end in a newline + if (line_start < debug_source_text.size()) { + line_lengths.push_back(length); + } +} + bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _, Instruction* instr) { if (instr->opcode() != spv::Op::OpConstant) return false; @@ -1010,6 +1232,14 @@ return size_in_bits == 32 || size_in_bits == 64; } +bool IsSpecConstIntScalarTypeWith32Or64Bits(ValidationState_t& _, + Instruction* instr) { + if (!spvOpcodeIsSpecConstant(instr->opcode())) return false; + if (!_.IsIntScalarType(instr->type_id())) return false; + uint32_t size_in_bits = _.GetBitWidth(instr->type_id()); + return size_in_bits == 32 || size_in_bits == 64; +} + bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst, uint32_t word_index) { auto* int_scalar_const = _.FindDef(inst->word(word_index)); @@ -1052,12 +1282,26 @@ } // anonymous namespace spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { + std::string extension = GetExtensionString(&(inst->c_inst())); + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) { + if (extension == ExtensionToString(kSPV_KHR_vulkan_memory_model) || + extension == + ExtensionToString(kSPV_QCOM_cooperative_matrix_conversion)) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << extension << " extension requires SPIR-V version 1.3 or later."; + } + } if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) { - std::string extension = GetExtensionString(&(inst->c_inst())); if (extension == ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout) || extension == ExtensionToString(kSPV_EXT_mesh_shader) || - extension == ExtensionToString(kSPV_NV_shader_invocation_reorder)) { + extension == ExtensionToString(kSPV_NV_shader_invocation_reorder) || + extension == ExtensionToString(kSPV_EXT_shader_invocation_reorder) || + extension == + ExtensionToString(kSPV_NV_cluster_acceleration_structure) || + extension == ExtensionToString(kSPV_NV_linear_swept_spheres) || + extension == ExtensionToString(kSPV_QCOM_image_processing) || + extension == ExtensionToString(kSPV_QCOM_image_processing2)) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) << extension << " extension requires SPIR-V version 1.4 or later."; } @@ -1069,2735 +1313,2916 @@ spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; + const std::string name = inst->GetOperandAs<std::string>(name_id); if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) && !_.HasExtension(kSPV_KHR_non_semantic_info)) { - const std::string name = inst->GetOperandAs<std::string>(name_id); if (name.find("NonSemantic.") == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "NonSemantic extended instruction sets cannot be declared " - "without SPV_KHR_non_semantic_info."; + << "NonSemantic extended instruction " + "sets cannot be declared " + "without SPV_KHR_non_semantic_info. (This can also be fixed " + "having SPIR-V 1.6 or later)"; + } + } + + // Validate the version suffix of a NonSemantic.Shader.DebugInfo import. + // Accept any version >= kNSDIMinVersion; no upper bound is imposed because + // later versions are backward-compatible supersets of earlier ones. + const std::string nsdi_prefix = "NonSemantic.Shader.DebugInfo."; + if (name.find(nsdi_prefix) == 0) { + static const uint32_t kNSDIMinVersion = 100; + auto version_string = name.substr(nsdi_prefix.size()); + if (version_string.empty()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NonSemantic.Shader.DebugInfo import does not encode the " + "version correctly"; + } + char* end_ptr; + uint32_t ver = static_cast<uint32_t>( + std::strtoul(version_string.c_str(), &end_ptr, 10)); + if (end_ptr && *end_ptr != '\0') { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NonSemantic.Shader.DebugInfo import does not encode the " + "version correctly"; + } + if (ver < kNSDIMinVersion) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NonSemantic.Shader.DebugInfo import version " << ver + << " is below the minimum supported version " << kNSDIMinVersion; } } return SPV_SUCCESS; } -spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { +spv_result_t ValidateExtInstGlslStd450(ValidationState_t& _, + const Instruction* inst) { const uint32_t result_type = inst->type_id(); const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size()); - const uint32_t ext_inst_set = inst->word(3); const uint32_t ext_inst_index = inst->word(4); - const spv_ext_inst_type_t ext_inst_type = - spv_ext_inst_type_t(inst->ext_inst_type()); + const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index); + switch (ext_inst_key) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450FAbs: + case GLSLstd450Trunc: + case GLSLstd450FSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450FMin: + case GLSLstd450FMax: + case GLSLstd450FClamp: + case GLSLstd450FMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450Normalize: + case GLSLstd450FaceForward: + case GLSLstd450Reflect: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: { + bool supportsCoopVec = + (ext_inst_key == GLSLstd450FMin || ext_inst_key == GLSLstd450FMax || + ext_inst_key == GLSLstd450FClamp || ext_inst_key == GLSLstd450NMin || + ext_inst_key == GLSLstd450NMax || ext_inst_key == GLSLstd450NClamp || + ext_inst_key == GLSLstd450Step || ext_inst_key == GLSLstd450Fma); - auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() { - spv_ext_inst_desc desc = nullptr; - if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) != - SPV_SUCCESS || - !desc) { - return std::string("Unknown ExtInst"); + if (!_.IsFloatScalarOrVectorType(result_type) && + !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; } - auto* import_inst = _.FindDef(ext_inst_set); - assert(import_inst); + case GLSLstd450SAbs: + case GLSLstd450SSign: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FindILsb: + case GLSLstd450FindUMsb: + case GLSLstd450FindSMsb: { + bool supportsCoopVec = + (ext_inst_key == GLSLstd450UMin || ext_inst_key == GLSLstd450UMax || + ext_inst_key == GLSLstd450UClamp || ext_inst_key == GLSLstd450SMin || + ext_inst_key == GLSLstd450SMax || ext_inst_key == GLSLstd450SClamp); - std::ostringstream ss; - ss << import_inst->GetOperandAs<std::string>(1); - ss << " "; - ss << desc->name; - - return ss.str(); - }; - - if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) { - const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index); - switch (ext_inst_key) { - case GLSLstd450Round: - case GLSLstd450RoundEven: - case GLSLstd450FAbs: - case GLSLstd450Trunc: - case GLSLstd450FSign: - case GLSLstd450Floor: - case GLSLstd450Ceil: - case GLSLstd450Fract: - case GLSLstd450Sqrt: - case GLSLstd450InverseSqrt: - case GLSLstd450FMin: - case GLSLstd450FMax: - case GLSLstd450FClamp: - case GLSLstd450FMix: - case GLSLstd450Step: - case GLSLstd450SmoothStep: - case GLSLstd450Fma: - case GLSLstd450Normalize: - case GLSLstd450FaceForward: - case GLSLstd450Reflect: - case GLSLstd450NMin: - case GLSLstd450NMax: - case GLSLstd450NClamp: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } - - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (result_type != operand_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected types of all operands to be equal to Result " - "Type"; - } - } - break; - } - - case GLSLstd450SAbs: - case GLSLstd450SSign: - case GLSLstd450UMin: - case GLSLstd450SMin: - case GLSLstd450UMax: - case GLSLstd450SMax: - case GLSLstd450UClamp: - case GLSLstd450SClamp: - case GLSLstd450FindILsb: - case GLSLstd450FindUMsb: - case GLSLstd450FindSMsb: { - if (!_.IsIntScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int scalar or vector type"; - } - - const uint32_t result_type_bit_width = _.GetBitWidth(result_type); - const uint32_t result_type_dimension = _.GetDimension(result_type); - - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (!operand_type || !_.IsIntScalarOrVectorType(operand_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected all operands to be int scalars or vectors"; - } - - if (result_type_dimension != _.GetDimension(operand_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected all operands to have the same dimension as " - << "Result Type"; - } - - if (result_type_bit_width != _.GetBitWidth(operand_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected all operands to have the same bit width as " - << "Result Type"; - } - - if (ext_inst_key == GLSLstd450FindUMsb || - ext_inst_key == GLSLstd450FindSMsb) { - if (result_type_bit_width != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "this instruction is currently limited to 32-bit width " - << "components"; - } - } - } - break; - } - - case GLSLstd450Radians: - case GLSLstd450Degrees: - case GLSLstd450Sin: - case GLSLstd450Cos: - case GLSLstd450Tan: - case GLSLstd450Asin: - case GLSLstd450Acos: - case GLSLstd450Atan: - case GLSLstd450Sinh: - case GLSLstd450Cosh: - case GLSLstd450Tanh: - case GLSLstd450Asinh: - case GLSLstd450Acosh: - case GLSLstd450Atanh: - case GLSLstd450Exp: - case GLSLstd450Exp2: - case GLSLstd450Log: - case GLSLstd450Log2: - case GLSLstd450Atan2: - case GLSLstd450Pow: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 16 or 32-bit scalar or " - "vector float type"; - } - - const uint32_t result_type_bit_width = _.GetBitWidth(result_type); - if (result_type_bit_width != 16 && result_type_bit_width != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 16 or 32-bit scalar or " - "vector float type"; - } - - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (result_type != operand_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected types of all operands to be equal to Result " - "Type"; - } - } - break; - } - - case GLSLstd450Determinant: { - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - uint32_t num_rows = 0; - uint32_t num_cols = 0; - uint32_t col_type = 0; - uint32_t component_type = 0; - if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type, - &component_type) || - num_rows != num_cols) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X to be a square matrix"; - } - - if (result_type != component_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X component type to be equal to " - << "Result Type"; - } - break; - } - - case GLSLstd450MatrixInverse: { - uint32_t num_rows = 0; - uint32_t num_cols = 0; - uint32_t col_type = 0; - uint32_t component_type = 0; - if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, - &component_type) || - num_rows != num_cols) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a square matrix"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (result_type != x_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to Result Type"; - } - break; - } - - case GLSLstd450Modf: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or vector float type"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - const uint32_t i_type = _.GetOperandTypeId(inst, 5); - - if (x_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to Result Type"; - } - - spv::StorageClass i_storage_class; - uint32_t i_data_type = 0; - if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand I to be a pointer"; - } - - if (i_data_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand I data type to be equal to Result Type"; - } - - break; - } - - case GLSLstd450ModfStruct: { - std::vector<uint32_t> result_types; - if (!_.GetStructMemberTypes(result_type, &result_types) || - result_types.size() != 2 || - !_.IsFloatScalarOrVectorType(result_types[0]) || - result_types[1] != result_types[0]) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a struct with two identical " - << "scalar or vector float type members"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (x_type != result_types[0]) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to members of " - << "Result Type struct"; - } - break; - } - - case GLSLstd450Frexp: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or vector float type"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - const uint32_t exp_type = _.GetOperandTypeId(inst, 5); - - if (x_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to Result Type"; - } - - spv::StorageClass exp_storage_class; - uint32_t exp_data_type = 0; - if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, - &exp_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Exp to be a pointer"; - } - - if (!_.IsIntScalarOrVectorType(exp_data_type) || - (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && - _.GetBitWidth(exp_data_type) != 32) || - (_.HasExtension(kSPV_AMD_gpu_shader_int16) && - _.GetBitWidth(exp_data_type) != 16 && - _.GetBitWidth(exp_data_type) != 32)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Exp data type to be a " - << (_.HasExtension(kSPV_AMD_gpu_shader_int16) - ? "16-bit or 32-bit " - : "32-bit ") - << "int scalar or vector type"; - } - - if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Exp data type to have the same component " - << "number as Result Type"; - } - - break; - } - - case GLSLstd450Ldexp: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or vector float type"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - const uint32_t exp_type = _.GetOperandTypeId(inst, 5); - - if (x_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to Result Type"; - } - - if (!_.IsIntScalarOrVectorType(exp_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Exp to be a 32-bit int scalar " - << "or vector type"; - } - - if (_.GetDimension(result_type) != _.GetDimension(exp_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Exp to have the same component " - << "number as Result Type"; - } - - break; - } - - case GLSLstd450FrexpStruct: { - std::vector<uint32_t> result_types; - if (!_.GetStructMemberTypes(result_type, &result_types) || - result_types.size() != 2 || - !_.IsFloatScalarOrVectorType(result_types[0]) || - !_.IsIntScalarOrVectorType(result_types[1]) || - (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && - _.GetBitWidth(result_types[1]) != 32) || - (_.HasExtension(kSPV_AMD_gpu_shader_int16) && - _.GetBitWidth(result_types[1]) != 16 && - _.GetBitWidth(result_types[1]) != 32) || - _.GetDimension(result_types[0]) != - _.GetDimension(result_types[1])) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a struct with two members, " - << "first member a float scalar or vector, second member a " - << (_.HasExtension(kSPV_AMD_gpu_shader_int16) - ? "16-bit or 32-bit " - : "32-bit ") - << "int scalar or vector with the same number of " - << "components as the first member"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (x_type != result_types[0]) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to the first member " - << "of Result Type struct"; - } - break; - } - - case GLSLstd450PackSnorm4x8: - case GLSLstd450PackUnorm4x8: { - if (!_.IsIntScalarType(result_type) || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be 32-bit int scalar type"; - } - - const uint32_t v_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 || - _.GetBitWidth(v_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand V to be a 32-bit float vector of size 4"; - } - break; - } - - case GLSLstd450PackSnorm2x16: - case GLSLstd450PackUnorm2x16: - case GLSLstd450PackHalf2x16: { - if (!_.IsIntScalarType(result_type) || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be 32-bit int scalar type"; - } - - const uint32_t v_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 || - _.GetBitWidth(v_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand V to be a 32-bit float vector of size 2"; - } - break; - } - - case GLSLstd450PackDouble2x32: { - if (!_.IsFloatScalarType(result_type) || - _.GetBitWidth(result_type) != 64) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be 64-bit float scalar type"; - } - - const uint32_t v_type = _.GetOperandTypeId(inst, 4); - if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 || - _.GetBitWidth(v_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand V to be a 32-bit int vector of size 2"; - } - break; - } - - case GLSLstd450UnpackSnorm4x8: - case GLSLstd450UnpackUnorm4x8: { - if (!_.IsFloatVectorType(result_type) || - _.GetDimension(result_type) != 4 || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit float vector of size " - "4"; - } - - const uint32_t v_type = _.GetOperandTypeId(inst, 4); - if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a 32-bit int scalar"; - } - break; - } - - case GLSLstd450UnpackSnorm2x16: - case GLSLstd450UnpackUnorm2x16: - case GLSLstd450UnpackHalf2x16: { - if (!_.IsFloatVectorType(result_type) || - _.GetDimension(result_type) != 2 || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit float vector of size " - "2"; - } - - const uint32_t v_type = _.GetOperandTypeId(inst, 4); - if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a 32-bit int scalar"; - } - break; - } - - case GLSLstd450UnpackDouble2x32: { - if (!_.IsIntVectorType(result_type) || - _.GetDimension(result_type) != 2 || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit int vector of size " - "2"; - } - - const uint32_t v_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand V to be a 64-bit float scalar"; - } - break; - } - - case GLSLstd450Length: { - if (!_.IsFloatScalarType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar type"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarOrVectorType(x_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X to be of float scalar or vector type"; - } - - if (result_type != _.GetComponentType(x_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X component type to be equal to Result " - "Type"; - } - break; - } - - case GLSLstd450Distance: { - if (!_.IsFloatScalarType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar type"; - } - - const uint32_t p0_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarOrVectorType(p0_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P0 to be of float scalar or vector type"; - } - - if (result_type != _.GetComponentType(p0_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P0 component type to be equal to " - << "Result Type"; - } - - const uint32_t p1_type = _.GetOperandTypeId(inst, 5); - if (!_.IsFloatScalarOrVectorType(p1_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P1 to be of float scalar or vector type"; - } - - if (result_type != _.GetComponentType(p1_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P1 component type to be equal to " - << "Result Type"; - } - - if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operands P0 and P1 to have the same number of " - << "components"; - } - break; - } - - case GLSLstd450Cross: { - if (!_.IsFloatVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float vector type"; - } - - if (_.GetDimension(result_type) != 3) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to have 3 components"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - const uint32_t y_type = _.GetOperandTypeId(inst, 5); - - if (x_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to Result Type"; - } - - if (y_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Y type to be equal to Result Type"; - } - break; - } - - case GLSLstd450Refract: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } - - const uint32_t i_type = _.GetOperandTypeId(inst, 4); - const uint32_t n_type = _.GetOperandTypeId(inst, 5); - const uint32_t eta_type = _.GetOperandTypeId(inst, 6); - - if (result_type != i_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand I to be of type equal to Result Type"; - } - - if (result_type != n_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand N to be of type equal to Result Type"; - } - - if (!_.IsFloatScalarType(eta_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Eta to be a float scalar"; - } - break; - } - - case GLSLstd450InterpolateAtCentroid: - case GLSLstd450InterpolateAtSample: - case GLSLstd450InterpolateAtOffset: { - if (!_.HasCapability(spv::Capability::InterpolationFunction)) { - return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) - << ext_inst_name() - << " requires capability InterpolationFunction"; - } - - if (!_.IsFloatScalarOrVectorType(result_type) || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit float scalar " - << "or vector type"; - } - - // If HLSL legalization and first operand is an OpLoad, use load - // pointer as the interpolant lvalue. Else use interpolate first - // operand. - uint32_t interp_id = inst->GetOperandAs<uint32_t>(4); - auto* interp_inst = _.FindDef(interp_id); - uint32_t interpolant_type = (_.options()->before_hlsl_legalization && - interp_inst->opcode() == spv::Op::OpLoad) - ? _.GetOperandTypeId(interp_inst, 2) - : _.GetOperandTypeId(inst, 4); - - spv::StorageClass interpolant_storage_class; - uint32_t interpolant_data_type = 0; - if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type, - &interpolant_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Interpolant to be a pointer"; - } - - if (result_type != interpolant_data_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Interpolant data type to be equal to Result Type"; - } - - if (interpolant_storage_class != spv::StorageClass::Input) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Interpolant storage class to be Input"; - } - - if (ext_inst_key == GLSLstd450InterpolateAtSample) { - const uint32_t sample_type = _.GetOperandTypeId(inst, 5); - if (!_.IsIntScalarType(sample_type) || - _.GetBitWidth(sample_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Sample to be 32-bit integer"; - } - } - - if (ext_inst_key == GLSLstd450InterpolateAtOffset) { - const uint32_t offset_type = _.GetOperandTypeId(inst, 5); - if (!_.IsFloatVectorType(offset_type) || - _.GetDimension(offset_type) != 2 || - _.GetBitWidth(offset_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Offset to be a vector of 2 32-bit floats"; - } - } - - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation( - spv::ExecutionModel::Fragment, - ext_inst_name() + - std::string(" requires Fragment execution model")); - break; - } - - case GLSLstd450IMix: { + if (!_.IsIntScalarOrVectorType(result_type) && + !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Extended instruction GLSLstd450IMix is not supported"; + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int scalar or vector type"; } - case GLSLstd450Bad: { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Encountered extended instruction GLSLstd450Bad"; - } + const uint32_t result_type_bit_width = _.GetBitWidth(result_type); + const uint32_t result_type_dimension = _.GetDimension(result_type); - case GLSLstd450Count: { - assert(0); - break; + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (!operand_type || + (!_.IsIntScalarOrVectorType(operand_type) && + !(supportsCoopVec && + _.IsIntCooperativeVectorNVType(operand_type)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected all operands to be int scalars or vectors"; + } + + if (result_type_dimension != _.GetDimension(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected all operands to have the same dimension as " + << "Result Type"; + } + + if (result_type_bit_width != _.GetBitWidth(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected all operands to have the same bit width as " + << "Result Type"; + } + + if (ext_inst_key == GLSLstd450FindUMsb || + ext_inst_key == GLSLstd450FindSMsb) { + if (result_type_bit_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "this instruction is currently limited to 32-bit width " + << "components"; + } + } } + break; } - } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) { - const OpenCLLIB::Entrypoints ext_inst_key = - OpenCLLIB::Entrypoints(ext_inst_index); - switch (ext_inst_key) { - case OpenCLLIB::Acos: - case OpenCLLIB::Acosh: - case OpenCLLIB::Acospi: - case OpenCLLIB::Asin: - case OpenCLLIB::Asinh: - case OpenCLLIB::Asinpi: - case OpenCLLIB::Atan: - case OpenCLLIB::Atan2: - case OpenCLLIB::Atanh: - case OpenCLLIB::Atanpi: - case OpenCLLIB::Atan2pi: - case OpenCLLIB::Cbrt: - case OpenCLLIB::Ceil: - case OpenCLLIB::Copysign: - case OpenCLLIB::Cos: - case OpenCLLIB::Cosh: - case OpenCLLIB::Cospi: - case OpenCLLIB::Erfc: - case OpenCLLIB::Erf: - case OpenCLLIB::Exp: - case OpenCLLIB::Exp2: - case OpenCLLIB::Exp10: - case OpenCLLIB::Expm1: - case OpenCLLIB::Fabs: - case OpenCLLIB::Fdim: - case OpenCLLIB::Floor: - case OpenCLLIB::Fma: - case OpenCLLIB::Fmax: - case OpenCLLIB::Fmin: - case OpenCLLIB::Fmod: - case OpenCLLIB::Hypot: - case OpenCLLIB::Lgamma: - case OpenCLLIB::Log: - case OpenCLLIB::Log2: - case OpenCLLIB::Log10: - case OpenCLLIB::Log1p: - case OpenCLLIB::Logb: - case OpenCLLIB::Mad: - case OpenCLLIB::Maxmag: - case OpenCLLIB::Minmag: - case OpenCLLIB::Nextafter: - case OpenCLLIB::Pow: - case OpenCLLIB::Powr: - case OpenCLLIB::Remainder: - case OpenCLLIB::Rint: - case OpenCLLIB::Round: - case OpenCLLIB::Rsqrt: - case OpenCLLIB::Sin: - case OpenCLLIB::Sinh: - case OpenCLLIB::Sinpi: - case OpenCLLIB::Sqrt: - case OpenCLLIB::Tan: - case OpenCLLIB::Tanh: - case OpenCLLIB::Tanpi: - case OpenCLLIB::Tgamma: - case OpenCLLIB::Trunc: - case OpenCLLIB::Half_cos: - case OpenCLLIB::Half_divide: - case OpenCLLIB::Half_exp: - case OpenCLLIB::Half_exp2: - case OpenCLLIB::Half_exp10: - case OpenCLLIB::Half_log: - case OpenCLLIB::Half_log2: - case OpenCLLIB::Half_log10: - case OpenCLLIB::Half_powr: - case OpenCLLIB::Half_recip: - case OpenCLLIB::Half_rsqrt: - case OpenCLLIB::Half_sin: - case OpenCLLIB::Half_sqrt: - case OpenCLLIB::Half_tan: - case OpenCLLIB::Native_cos: - case OpenCLLIB::Native_divide: - case OpenCLLIB::Native_exp: - case OpenCLLIB::Native_exp2: - case OpenCLLIB::Native_exp10: - case OpenCLLIB::Native_log: - case OpenCLLIB::Native_log2: - case OpenCLLIB::Native_log10: - case OpenCLLIB::Native_powr: - case OpenCLLIB::Native_recip: - case OpenCLLIB::Native_rsqrt: - case OpenCLLIB::Native_sin: - case OpenCLLIB::Native_sqrt: - case OpenCLLIB::Native_tan: - case OpenCLLIB::FClamp: - case OpenCLLIB::Degrees: - case OpenCLLIB::FMax_common: - case OpenCLLIB::FMin_common: - case OpenCLLIB::Mix: - case OpenCLLIB::Radians: - case OpenCLLIB::Step: - case OpenCLLIB::Smoothstep: - case OpenCLLIB::Sign: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Exp: + case GLSLstd450Exp2: + case GLSLstd450Log: + case GLSLstd450Log2: + case GLSLstd450Atan2: + case GLSLstd450Pow: { + bool supportsCoopVec = + (ext_inst_key == GLSLstd450Atan || ext_inst_key == GLSLstd450Tanh || + ext_inst_key == GLSLstd450Exp || ext_inst_key == GLSLstd450Log); - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (result_type != operand_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected types of all operands to be equal to Result " - "Type"; - } - } - break; + if (!_.IsFloatScalarOrVectorType(result_type) && + !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 16 or 32-bit scalar or " + "vector float type"; } - case OpenCLLIB::Fract: - case OpenCLLIB::Modf: - case OpenCLLIB::Sincos: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } + const uint32_t result_type_bit_width = _.GetBitWidth(result_type); + if (result_type_bit_width != 16 && result_type_bit_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 16 or 32-bit scalar or " + "vector float type"; + } - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (result_type != x_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected type of operand X to be equal to Result Type"; - } - - const uint32_t p_type = _.GetOperandTypeId(inst, 5); - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected the last operand to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected storage class of the pointer to be Generic, " - "CrossWorkgroup, Workgroup or Function"; - } - - if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected data type of the pointer to be equal to Result " + << GetExtInstName(_, inst) << ": " + << "expected types of all operands to be equal to Result " "Type"; } - break; + } + break; + } + + case GLSLstd450Determinant: { + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X to be a square matrix"; } - case OpenCLLIB::Frexp: - case OpenCLLIB::Lgamma_r: - case OpenCLLIB::Remquo: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } + if (result_type != component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X component type to be equal to " + << "Result Type"; + } + break; + } - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - uint32_t operand_index = 4; - const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); - if (result_type != x_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected type of operand X to be equal to Result Type"; - } - - if (ext_inst_key == OpenCLLIB::Remquo) { - const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); - if (result_type != y_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected type of operand Y to be equal to Result Type"; - } - } - - const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++); - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected the last operand to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected storage class of the pointer to be Generic, " - "CrossWorkgroup, Workgroup or Function"; - } - - if ((!_.IsIntScalarOrVectorType(p_data_type) || - _.GetBitWidth(p_data_type) != 32) && - !_.ContainsUntypedPointer(p_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected data type of the pointer to be a 32-bit int " - "scalar or vector type"; - } - - if (!_.ContainsUntypedPointer(p_type) && - _.GetDimension(p_data_type) != num_components) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected data type of the pointer to have the same number " - "of components as Result Type"; - } - break; + case GLSLstd450MatrixInverse: { + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a square matrix"; } - case OpenCLLIB::Ilogb: { - if (!_.IsIntScalarOrVectorType(result_type) || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit int scalar or vector " - "type"; - } + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to Result Type"; + } + break; + } - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarOrVectorType(x_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X to be a float scalar or vector"; - } - - if (_.GetDimension(x_type) != num_components) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X to have the same number of components " - "as Result Type"; - } - break; + case GLSLstd450Modf: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or vector float type"; } - case OpenCLLIB::Ldexp: - case OpenCLLIB::Pown: - case OpenCLLIB::Rootn: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t i_type = _.GetOperandTypeId(inst, 5); - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - if (result_type != x_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected type of operand X to be equal to Result Type"; - } - - const uint32_t exp_type = _.GetOperandTypeId(inst, 5); - if (!_.IsIntScalarOrVectorType(exp_type) || - _.GetBitWidth(exp_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected the exponent to be a 32-bit int scalar or vector"; - } - - if (_.GetDimension(exp_type) != num_components) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected the exponent to have the same number of " - "components as Result Type"; - } - break; + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to Result Type"; } - case OpenCLLIB::Nan: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } - - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t nancode_type = _.GetOperandTypeId(inst, 4); - if (!_.IsIntScalarOrVectorType(nancode_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Nancode to be an int scalar or vector type"; - } - - if (_.GetDimension(nancode_type) != num_components) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Nancode to have the same number of components as " - "Result Type"; - } - - if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Nancode to have the same bit width as Result " - "Type"; - } - break; + spv::StorageClass i_storage_class; + uint32_t i_data_type = 0; + if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand I to be a pointer"; } - case OpenCLLIB::SAbs: - case OpenCLLIB::SAbs_diff: - case OpenCLLIB::SAdd_sat: - case OpenCLLIB::UAdd_sat: - case OpenCLLIB::SHadd: - case OpenCLLIB::UHadd: - case OpenCLLIB::SRhadd: - case OpenCLLIB::URhadd: - case OpenCLLIB::SClamp: - case OpenCLLIB::UClamp: - case OpenCLLIB::Clz: - case OpenCLLIB::Ctz: - case OpenCLLIB::SMad_hi: - case OpenCLLIB::UMad_sat: - case OpenCLLIB::SMad_sat: - case OpenCLLIB::SMax: - case OpenCLLIB::UMax: - case OpenCLLIB::SMin: - case OpenCLLIB::UMin: - case OpenCLLIB::SMul_hi: - case OpenCLLIB::Rotate: - case OpenCLLIB::SSub_sat: - case OpenCLLIB::USub_sat: - case OpenCLLIB::Popcount: - case OpenCLLIB::UAbs: - case OpenCLLIB::UAbs_diff: - case OpenCLLIB::UMul_hi: - case OpenCLLIB::UMad_hi: { - if (!_.IsIntScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int scalar or vector type"; - } - - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (result_type != operand_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected types of all operands to be equal to Result " - "Type"; - } - } - break; + if (i_data_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand I data type to be equal to Result Type"; } - case OpenCLLIB::U_Upsample: - case OpenCLLIB::S_Upsample: { - if (!_.IsIntScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int scalar or vector " - "type"; - } + break; + } - const uint32_t result_num_components = _.GetDimension(result_type); - if (result_num_components > 4 && result_num_components != 8 && - result_num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t result_bit_width = _.GetBitWidth(result_type); - if (result_bit_width != 16 && result_bit_width != 32 && - result_bit_width != 64) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected bit width of Result Type components to be 16, 32 " - "or 64"; - } - - const uint32_t hi_type = _.GetOperandTypeId(inst, 4); - const uint32_t lo_type = _.GetOperandTypeId(inst, 5); - - if (hi_type != lo_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Hi and Lo operands to have the same type"; - } - - if (result_num_components != _.GetDimension(hi_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Hi and Lo operands to have the same number of " - "components as Result Type"; - } - - if (result_bit_width != 2 * _.GetBitWidth(hi_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected bit width of components of Hi and Lo operands to " - "be half of the bit width of components of Result Type"; - } - break; + case GLSLstd450ModfStruct: { + std::vector<uint32_t> result_types; + if (!_.GetStructMemberTypes(result_type, &result_types) || + result_types.size() != 2 || + !_.IsFloatScalarOrVectorType(result_types[0]) || + result_types[1] != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a struct with two identical " + << "scalar or vector float type members"; } - case OpenCLLIB::SMad24: - case OpenCLLIB::UMad24: - case OpenCLLIB::SMul24: - case OpenCLLIB::UMul24: { - if (!_.IsIntScalarOrVectorType(result_type) || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit int scalar or vector " - "type"; - } + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (x_type != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to members of " + << "Result Type struct"; + } + break; + } - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (result_type != operand_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected types of all operands to be equal to Result " - "Type"; - } - } - break; + case GLSLstd450Frexp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or vector float type"; } - case OpenCLLIB::Cross: { - if (!_.IsFloatVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float vector type"; - } + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); - const uint32_t num_components = _.GetDimension(result_type); - if (num_components != 3 && num_components != 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to have 3 or 4 components"; - } - - const uint32_t x_type = _.GetOperandTypeId(inst, 4); - const uint32_t y_type = _.GetOperandTypeId(inst, 5); - - if (x_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X type to be equal to Result Type"; - } - - if (y_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Y type to be equal to Result Type"; - } - break; + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to Result Type"; } - case OpenCLLIB::Distance: - case OpenCLLIB::Fast_distance: { - if (!_.IsFloatScalarType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar type"; - } - - const uint32_t p0_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarOrVectorType(p0_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P0 to be of float scalar or vector type"; - } - - const uint32_t num_components = _.GetDimension(p0_type); - if (num_components > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P0 to have no more than 4 components"; - } - - if (result_type != _.GetComponentType(p0_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P0 component type to be equal to " - << "Result Type"; - } - - const uint32_t p1_type = _.GetOperandTypeId(inst, 5); - if (p0_type != p1_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operands P0 and P1 to be of the same type"; - } - break; + spv::StorageClass exp_storage_class; + uint32_t exp_data_type = 0; + if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, &exp_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Exp to be a pointer"; } - case OpenCLLIB::Length: - case OpenCLLIB::Fast_length: { - if (!_.IsFloatScalarType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar type"; - } - - const uint32_t p_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarOrVectorType(p_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a float scalar or vector"; - } - - const uint32_t num_components = _.GetDimension(p_type); - if (num_components > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to have no more than 4 components"; - } - - if (result_type != _.GetComponentType(p_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P component type to be equal to Result " - "Type"; - } - break; + if (!_.IsIntScalarOrVectorType(exp_data_type) || + (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(exp_data_type) != 32) || + (_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(exp_data_type) != 16 && + _.GetBitWidth(exp_data_type) != 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Exp data type to be a " + << (_.HasExtension(kSPV_AMD_gpu_shader_int16) + ? "16-bit or 32-bit " + : "32-bit ") + << "int scalar or vector type"; } - case OpenCLLIB::Normalize: - case OpenCLLIB::Fast_normalize: { - if (!_.IsFloatScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar or vector type"; - } - - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to have no more than 4 components"; - } - - const uint32_t p_type = _.GetOperandTypeId(inst, 4); - if (p_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P type to be equal to Result Type"; - } - break; + if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Exp data type to have the same component " + << "number as Result Type"; } - case OpenCLLIB::Bitselect: { - if (!_.IsFloatScalarOrVectorType(result_type) && - !_.IsIntScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int or float scalar or " - "vector type"; - } + break; + } - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - for (uint32_t operand_index = 4; operand_index < num_operands; - ++operand_index) { - const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); - if (result_type != operand_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected types of all operands to be equal to Result " - "Type"; - } - } - break; + case GLSLstd450Ldexp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or vector float type"; } - case OpenCLLIB::Select: { - if (!_.IsFloatScalarOrVectorType(result_type) && - !_.IsIntScalarOrVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int or float scalar or " - "vector type"; - } + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t a_type = _.GetOperandTypeId(inst, 4); - const uint32_t b_type = _.GetOperandTypeId(inst, 5); - const uint32_t c_type = _.GetOperandTypeId(inst, 6); - - if (result_type != a_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand A type to be equal to Result Type"; - } - - if (result_type != b_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand B type to be equal to Result Type"; - } - - if (!_.IsIntScalarOrVectorType(c_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand C to be an int scalar or vector"; - } - - if (num_components != _.GetDimension(c_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand C to have the same number of components " - "as Result Type"; - } - - if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand C to have the same bit width as Result " - "Type"; - } - break; + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to Result Type"; } - case OpenCLLIB::Vloadn: { - if (!_.IsFloatVectorType(result_type) && - !_.IsIntVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int or float vector type"; - } - - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to have 2, 3, 4, 8 or 16 components"; - } - - const uint32_t offset_type = _.GetOperandTypeId(inst, 4); - const uint32_t p_type = _.GetOperandTypeId(inst, 5); - - const uint32_t size_t_bit_width = GetSizeTBitWidth(_); - if (!size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << " can only be used with physical addressing models"; - } - - if (!_.IsIntScalarType(offset_type) || - _.GetBitWidth(offset_type) != size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Offset to be of type size_t (" - << size_t_bit_width - << "-bit integer for the addressing model used in the module)"; - } - - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::UniformConstant && - p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P storage class to be UniformConstant, " - "Generic, CrossWorkgroup, Workgroup or Function"; - } - - if (_.GetComponentType(result_type) != p_data_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P data type to be equal to component " - "type of Result Type"; - } - - const uint32_t n_value = inst->word(7); - if (num_components != n_value) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected literal N to be equal to the number of " - "components of Result Type"; - } - break; + if (!_.IsIntScalarOrVectorType(exp_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Exp to be a 32-bit int scalar " + << "or vector type"; } - case OpenCLLIB::Vstoren: { - if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected Result Type to be void"; - } + if (_.GetDimension(result_type) != _.GetDimension(exp_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Exp to have the same component " + << "number as Result Type"; + } - const uint32_t data_type = _.GetOperandTypeId(inst, 4); + break; + } + + case GLSLstd450FrexpStruct: { + std::vector<uint32_t> result_types; + if (!_.GetStructMemberTypes(result_type, &result_types) || + result_types.size() != 2 || + !_.IsFloatScalarOrVectorType(result_types[0]) || + !_.IsIntScalarOrVectorType(result_types[1]) || + (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(result_types[1]) != 32) || + (_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(result_types[1]) != 16 && + _.GetBitWidth(result_types[1]) != 32) || + _.GetDimension(result_types[0]) != _.GetDimension(result_types[1])) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a struct with two members, " + << "first member a float scalar or vector, second member a " + << (_.HasExtension(kSPV_AMD_gpu_shader_int16) + ? "16-bit or 32-bit " + : "32-bit ") + << "int scalar or vector with the same number of " + << "components as the first member"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (x_type != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to the first member " + << "of Result Type struct"; + } + break; + } + + case GLSLstd450PackSnorm4x8: + case GLSLstd450PackUnorm4x8: { + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be 32-bit int scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand V to be a 32-bit float vector of size 4"; + } + break; + } + + case GLSLstd450PackSnorm2x16: + case GLSLstd450PackUnorm2x16: + case GLSLstd450PackHalf2x16: { + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be 32-bit int scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand V to be a 32-bit float vector of size 2"; + } + break; + } + + case GLSLstd450PackDouble2x32: { + if (!_.IsFloatScalarType(result_type, 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be 64-bit float scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand V to be a 32-bit int vector of size 2"; + } + break; + } + + case GLSLstd450UnpackSnorm4x8: + case GLSLstd450UnpackUnorm4x8: { + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 4 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit float vector of size " + "4"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(v_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a 32-bit int scalar"; + } + break; + } + + case GLSLstd450UnpackSnorm2x16: + case GLSLstd450UnpackUnorm2x16: + case GLSLstd450UnpackHalf2x16: { + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit float vector of size " + "2"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(v_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a 32-bit int scalar"; + } + break; + } + + case GLSLstd450UnpackDouble2x32: { + if (!_.IsIntVectorType(result_type) || _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit int vector of size " + "2"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(v_type, 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand V to be a 64-bit float scalar"; + } + break; + } + + case GLSLstd450Length: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X component type to be equal to Result " + "Type"; + } + break; + } + + case GLSLstd450Distance: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p0_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P0 to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P0 component type to be equal to " + << "Result Type"; + } + + const uint32_t p1_type = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatScalarOrVectorType(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P1 to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P1 component type to be equal to " + << "Result Type"; + } + + if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operands P0 and P1 to have the same number of " + << "components"; + } + break; + } + + case GLSLstd450Cross: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float vector type"; + } + + if (_.GetDimension(result_type) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to have 3 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t y_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (y_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Y type to be equal to Result Type"; + } + break; + } + + case GLSLstd450Refract: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t i_type = _.GetOperandTypeId(inst, 4); + const uint32_t n_type = _.GetOperandTypeId(inst, 5); + const uint32_t eta_type = _.GetOperandTypeId(inst, 6); + + if (result_type != i_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand I to be of type equal to Result Type"; + } + + if (result_type != n_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand N to be of type equal to Result Type"; + } + + if (!_.IsFloatScalarType(eta_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Eta to be a float scalar"; + } + break; + } + + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: { + if (!_.HasCapability(spv::Capability::InterpolationFunction)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << GetExtInstName(_, inst) + << " requires capability InterpolationFunction"; + } + + if (!_.IsFloatScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit float scalar " + << "or vector type"; + } + + // If HLSL legalization and first operand is an OpLoad, use load + // pointer as the interpolant lvalue. Else use interpolate first + // operand. + uint32_t interp_id = inst->GetOperandAs<uint32_t>(4); + auto* interp_inst = _.FindDef(interp_id); + uint32_t interpolant_type = (_.options()->before_hlsl_legalization && + interp_inst->opcode() == spv::Op::OpLoad) + ? _.GetOperandTypeId(interp_inst, 2) + : _.GetOperandTypeId(inst, 4); + + spv::StorageClass interpolant_storage_class; + uint32_t interpolant_data_type = 0; + if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type, + &interpolant_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Interpolant to be a pointer"; + } + + if (result_type != interpolant_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Interpolant data type to be equal to Result Type"; + } + + if (interpolant_storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Interpolant storage class to be Input"; + } + + if (ext_inst_key == GLSLstd450InterpolateAtSample) { + const uint32_t sample_type = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarType(sample_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Sample to be 32-bit integer"; + } + } + + if (ext_inst_key == GLSLstd450InterpolateAtOffset) { const uint32_t offset_type = _.GetOperandTypeId(inst, 5); - const uint32_t p_type = _.GetOperandTypeId(inst, 6); - - if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) { + if (!_.IsFloatVectorType(offset_type) || + _.GetDimension(offset_type) != 2 || + _.GetBitWidth(offset_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Data to be an int or float vector"; + << GetExtInstName(_, inst) << ": " + << "expected Offset to be a vector of 2 32-bit floats"; + } + } + + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + GetExtInstName(_, inst) + + std::string(" requires Fragment execution model")); + break; + } + + case GLSLstd450IMix: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Extended instruction GLSLstd450IMix is not supported"; + } + + case GLSLstd450Bad: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Encountered extended instruction GLSLstd450Bad"; + } + + case GLSLstd450Count: { + assert(0); + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInstOpenClStd(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size()); + const uint32_t ext_inst_index = inst->word(4); + const OpenCLLIB::Entrypoints ext_inst_key = + OpenCLLIB::Entrypoints(ext_inst_index); + switch (ext_inst_key) { + case OpenCLLIB::Acos: + case OpenCLLIB::Acosh: + case OpenCLLIB::Acospi: + case OpenCLLIB::Asin: + case OpenCLLIB::Asinh: + case OpenCLLIB::Asinpi: + case OpenCLLIB::Atan: + case OpenCLLIB::Atan2: + case OpenCLLIB::Atanh: + case OpenCLLIB::Atanpi: + case OpenCLLIB::Atan2pi: + case OpenCLLIB::Cbrt: + case OpenCLLIB::Ceil: + case OpenCLLIB::Copysign: + case OpenCLLIB::Cos: + case OpenCLLIB::Cosh: + case OpenCLLIB::Cospi: + case OpenCLLIB::Erfc: + case OpenCLLIB::Erf: + case OpenCLLIB::Exp: + case OpenCLLIB::Exp2: + case OpenCLLIB::Exp10: + case OpenCLLIB::Expm1: + case OpenCLLIB::Fabs: + case OpenCLLIB::Fdim: + case OpenCLLIB::Floor: + case OpenCLLIB::Fma: + case OpenCLLIB::Fmax: + case OpenCLLIB::Fmin: + case OpenCLLIB::Fmod: + case OpenCLLIB::Hypot: + case OpenCLLIB::Lgamma: + case OpenCLLIB::Log: + case OpenCLLIB::Log2: + case OpenCLLIB::Log10: + case OpenCLLIB::Log1p: + case OpenCLLIB::Logb: + case OpenCLLIB::Mad: + case OpenCLLIB::Maxmag: + case OpenCLLIB::Minmag: + case OpenCLLIB::Nextafter: + case OpenCLLIB::Pow: + case OpenCLLIB::Powr: + case OpenCLLIB::Remainder: + case OpenCLLIB::Rint: + case OpenCLLIB::Round: + case OpenCLLIB::Rsqrt: + case OpenCLLIB::Sin: + case OpenCLLIB::Sinh: + case OpenCLLIB::Sinpi: + case OpenCLLIB::Sqrt: + case OpenCLLIB::Tan: + case OpenCLLIB::Tanh: + case OpenCLLIB::Tanpi: + case OpenCLLIB::Tgamma: + case OpenCLLIB::Trunc: + case OpenCLLIB::Half_cos: + case OpenCLLIB::Half_divide: + case OpenCLLIB::Half_exp: + case OpenCLLIB::Half_exp2: + case OpenCLLIB::Half_exp10: + case OpenCLLIB::Half_log: + case OpenCLLIB::Half_log2: + case OpenCLLIB::Half_log10: + case OpenCLLIB::Half_powr: + case OpenCLLIB::Half_recip: + case OpenCLLIB::Half_rsqrt: + case OpenCLLIB::Half_sin: + case OpenCLLIB::Half_sqrt: + case OpenCLLIB::Half_tan: + case OpenCLLIB::Native_cos: + case OpenCLLIB::Native_divide: + case OpenCLLIB::Native_exp: + case OpenCLLIB::Native_exp2: + case OpenCLLIB::Native_exp10: + case OpenCLLIB::Native_log: + case OpenCLLIB::Native_log2: + case OpenCLLIB::Native_log10: + case OpenCLLIB::Native_powr: + case OpenCLLIB::Native_recip: + case OpenCLLIB::Native_rsqrt: + case OpenCLLIB::Native_sin: + case OpenCLLIB::Native_sqrt: + case OpenCLLIB::Native_tan: + case OpenCLLIB::FClamp: + case OpenCLLIB::Degrees: + case OpenCLLIB::FMax_common: + case OpenCLLIB::FMin_common: + case OpenCLLIB::Mix: + case OpenCLLIB::Radians: + case OpenCLLIB::Step: + case OpenCLLIB::Smoothstep: + case OpenCLLIB::Sign: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Fract: + case OpenCLLIB::Modf: + case OpenCLLIB::Sincos: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected data type of the pointer to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Frexp: + case OpenCLLIB::Lgamma_r: + case OpenCLLIB::Remquo: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + uint32_t operand_index = 4; + const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected type of operand X to be equal to Result Type"; + } + + if (ext_inst_key == OpenCLLIB::Remquo) { + const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); + if (result_type != y_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected type of operand Y to be equal to Result Type"; + } + } + + const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++); + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsIntScalarOrVectorType(p_data_type) || + _.GetBitWidth(p_data_type) != 32) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected data type of the pointer to be a 32-bit int " + "scalar or vector type"; + } + + if (!_.ContainsUntypedPointer(p_type) && + _.GetDimension(p_data_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected data type of the pointer to have the same number " + "of components as Result Type"; + } + break; + } + + case OpenCLLIB::Ilogb: { + if (!_.IsIntScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit int scalar or vector " + "type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X to be a float scalar or vector"; + } + + if (_.GetDimension(x_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X to have the same number of components " + "as Result Type"; + } + break; + } + + case OpenCLLIB::Ldexp: + case OpenCLLIB::Pown: + case OpenCLLIB::Rootn: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarOrVectorType(exp_type) || + _.GetBitWidth(exp_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected the exponent to be a 32-bit int scalar or vector"; + } + + if (_.GetDimension(exp_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected the exponent to have the same number of " + "components as Result Type"; + } + break; + } + + case OpenCLLIB::Nan: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t nancode_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarOrVectorType(nancode_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Nancode to be an int scalar or vector type"; + } + + if (_.GetDimension(nancode_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Nancode to have the same number of components as " + "Result Type"; + } + + if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Nancode to have the same bit width as Result " + "Type"; + } + break; + } + + case OpenCLLIB::SAbs: + case OpenCLLIB::SAbs_diff: + case OpenCLLIB::SAdd_sat: + case OpenCLLIB::UAdd_sat: + case OpenCLLIB::SHadd: + case OpenCLLIB::UHadd: + case OpenCLLIB::SRhadd: + case OpenCLLIB::URhadd: + case OpenCLLIB::SClamp: + case OpenCLLIB::UClamp: + case OpenCLLIB::Clz: + case OpenCLLIB::Ctz: + case OpenCLLIB::SMad_hi: + case OpenCLLIB::UMad_sat: + case OpenCLLIB::SMad_sat: + case OpenCLLIB::SMax: + case OpenCLLIB::UMax: + case OpenCLLIB::SMin: + case OpenCLLIB::UMin: + case OpenCLLIB::SMul_hi: + case OpenCLLIB::Rotate: + case OpenCLLIB::SSub_sat: + case OpenCLLIB::USub_sat: + case OpenCLLIB::Popcount: + case OpenCLLIB::UAbs: + case OpenCLLIB::UAbs_diff: + case OpenCLLIB::UMul_hi: + case OpenCLLIB::UMad_hi: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::U_Upsample: + case OpenCLLIB::S_Upsample: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int scalar or vector " + "type"; + } + + const uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components > 4 && result_num_components != 8 && + result_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t result_bit_width = _.GetBitWidth(result_type); + if (result_bit_width != 16 && result_bit_width != 32 && + result_bit_width != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected bit width of Result Type components to be 16, 32 " + "or 64"; + } + + const uint32_t hi_type = _.GetOperandTypeId(inst, 4); + const uint32_t lo_type = _.GetOperandTypeId(inst, 5); + + if (hi_type != lo_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Hi and Lo operands to have the same type"; + } + + if (result_num_components != _.GetDimension(hi_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Hi and Lo operands to have the same number of " + "components as Result Type"; + } + + if (result_bit_width != 2 * _.GetBitWidth(hi_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected bit width of components of Hi and Lo operands to " + "be half of the bit width of components of Result Type"; + } + break; + } + + case OpenCLLIB::SMad24: + case OpenCLLIB::UMad24: + case OpenCLLIB::SMul24: + case OpenCLLIB::UMul24: { + if (!_.IsIntScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit int scalar or vector " + "type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Cross: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components != 3 && num_components != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to have 3 or 4 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t y_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (y_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Y type to be equal to Result Type"; + } + break; + } + + case OpenCLLIB::Distance: + case OpenCLLIB::Fast_distance: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p0_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P0 to be of float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(p0_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P0 to have no more than 4 components"; + } + + if (result_type != _.GetComponentType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P0 component type to be equal to " + << "Result Type"; + } + + const uint32_t p1_type = _.GetOperandTypeId(inst, 5); + if (p0_type != p1_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operands P0 and P1 to be of the same type"; + } + break; + } + + case OpenCLLIB::Length: + case OpenCLLIB::Fast_length: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a float scalar or vector"; + } + + const uint32_t num_components = _.GetDimension(p_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to have no more than 4 components"; + } + + if (result_type != _.GetComponentType(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P component type to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Normalize: + case OpenCLLIB::Fast_normalize: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to have no more than 4 components"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + if (p_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P type to be equal to Result Type"; + } + break; + } + + case OpenCLLIB::Bitselect: { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int or float scalar or " + "vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Select: { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int or float scalar or " + "vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t a_type = _.GetOperandTypeId(inst, 4); + const uint32_t b_type = _.GetOperandTypeId(inst, 5); + const uint32_t c_type = _.GetOperandTypeId(inst, 6); + + if (result_type != a_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand A type to be equal to Result Type"; + } + + if (result_type != b_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand B type to be equal to Result Type"; + } + + if (!_.IsIntScalarOrVectorType(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand C to be an int scalar or vector"; + } + + if (num_components != _.GetDimension(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand C to have the same number of components " + "as Result Type"; + } + + if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand C to have the same bit width as Result " + "Type"; + } + break; + } + + case OpenCLLIB::Vloadn: { + if (!_.IsFloatVectorType(result_type) && + !_.IsIntVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int or float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type, size_t_bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::UniformConstant && + p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if (_.GetComponentType(result_type) != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P data type to be equal to component " + "type of Result Type"; + } + + const uint32_t n_value = inst->word(7); + if (num_components != n_value) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected literal N to be equal to the number of " + "components of Result Type"; + } + break; + } + + case OpenCLLIB::Vstoren: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": expected Result Type to be void"; + } + + const uint32_t data_type = _.GetOperandTypeId(inst, 4); + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + const uint32_t p_type = _.GetOperandTypeId(inst, 6); + + if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Data to be an int or float vector"; + } + + const uint32_t num_components = _.GetDimension(data_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Data to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type, size_t_bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (_.GetComponentType(data_type) != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P data type to be equal to the type of " + "operand Data components"; + } + break; + } + + case OpenCLLIB::Vload_half: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type, size_t_bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::UniformConstant && + p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsFloatScalarType(p_data_type, 16)) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + break; + } + + case OpenCLLIB::Vload_halfn: + case OpenCLLIB::Vloada_halfn: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type, size_t_bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::UniformConstant && + p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsFloatScalarType(p_data_type, 16)) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + + const uint32_t n_value = inst->word(7); + if (num_components != n_value) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected literal N to be equal to the number of " + "components of Result Type"; + } + break; + } + + case OpenCLLIB::Vstore_half: + case OpenCLLIB::Vstore_half_r: + case OpenCLLIB::Vstore_halfn: + case OpenCLLIB::Vstore_halfn_r: + case OpenCLLIB::Vstorea_halfn: + case OpenCLLIB::Vstorea_halfn_r: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": expected Result Type to be void"; + } + + const uint32_t data_type = _.GetOperandTypeId(inst, 4); + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + const uint32_t p_type = _.GetOperandTypeId(inst, 6); + const uint32_t data_type_bit_width = _.GetBitWidth(data_type); + + if (ext_inst_key == OpenCLLIB::Vstore_half || + ext_inst_key == OpenCLLIB::Vstore_half_r) { + if (!_.IsFloatScalarType(data_type) || + (data_type_bit_width != 32 && data_type_bit_width != 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Data to be a 32 or 64-bit float scalar"; + } + } else { + if (!_.IsFloatVectorType(data_type) || + (data_type_bit_width != 32 && data_type_bit_width != 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Data to be a 32 or 64-bit float vector"; } const uint32_t num_components = _.GetDimension(data_type); if (num_components > 4 && num_components != 8 && num_components != 16) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " + << GetExtInstName(_, inst) << ": " << "expected Data to have 2, 3, 4, 8 or 16 components"; } - - const uint32_t size_t_bit_width = GetSizeTBitWidth(_); - if (!size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << " can only be used with physical addressing models"; - } - - if (!_.IsIntScalarType(offset_type) || - _.GetBitWidth(offset_type) != size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Offset to be of type size_t (" - << size_t_bit_width - << "-bit integer for the addressing model used in the module)"; - } - - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P storage class to be Generic, " - "CrossWorkgroup, Workgroup or Function"; - } - - if (_.GetComponentType(data_type) != p_data_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P data type to be equal to the type of " - "operand Data components"; - } - break; } - case OpenCLLIB::Vload_half: { - if (!_.IsFloatScalarType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float scalar type"; - } - - const uint32_t offset_type = _.GetOperandTypeId(inst, 4); - const uint32_t p_type = _.GetOperandTypeId(inst, 5); - - const uint32_t size_t_bit_width = GetSizeTBitWidth(_); - if (!size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << " can only be used with physical addressing models"; - } - - if (!_.IsIntScalarType(offset_type) || - _.GetBitWidth(offset_type) != size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Offset to be of type size_t (" - << size_t_bit_width - << "-bit integer for the addressing model used in the module)"; - } - - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::UniformConstant && - p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P storage class to be UniformConstant, " - "Generic, CrossWorkgroup, Workgroup or Function"; - } - - if ((!_.IsFloatScalarType(p_data_type) || - _.GetBitWidth(p_data_type) != 16) && - !_.ContainsUntypedPointer(p_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P data type to be 16-bit float scalar"; - } - break; + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << " can only be used with physical addressing models"; } - case OpenCLLIB::Vload_halfn: - case OpenCLLIB::Vloada_halfn: { - if (!_.IsFloatVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a float vector type"; - } - - const uint32_t num_components = _.GetDimension(result_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to have 2, 3, 4, 8 or 16 components"; - } - - const uint32_t offset_type = _.GetOperandTypeId(inst, 4); - const uint32_t p_type = _.GetOperandTypeId(inst, 5); - - const uint32_t size_t_bit_width = GetSizeTBitWidth(_); - if (!size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << " can only be used with physical addressing models"; - } - - if (!_.IsIntScalarType(offset_type) || - _.GetBitWidth(offset_type) != size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Offset to be of type size_t (" - << size_t_bit_width - << "-bit integer for the addressing model used in the module)"; - } - - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::UniformConstant && - p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P storage class to be UniformConstant, " - "Generic, CrossWorkgroup, Workgroup or Function"; - } - - if ((!_.IsFloatScalarType(p_data_type) || - _.GetBitWidth(p_data_type) != 16) && - !_.ContainsUntypedPointer(p_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P data type to be 16-bit float scalar"; - } - - const uint32_t n_value = inst->word(7); - if (num_components != n_value) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected literal N to be equal to the number of " - "components of Result Type"; - } - break; + if (!_.IsIntScalarType(offset_type, size_t_bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; } - case OpenCLLIB::Vstore_half: - case OpenCLLIB::Vstore_half_r: - case OpenCLLIB::Vstore_halfn: - case OpenCLLIB::Vstore_halfn_r: - case OpenCLLIB::Vstorea_halfn: - case OpenCLLIB::Vstorea_halfn_r: { - if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected Result Type to be void"; - } - - const uint32_t data_type = _.GetOperandTypeId(inst, 4); - const uint32_t offset_type = _.GetOperandTypeId(inst, 5); - const uint32_t p_type = _.GetOperandTypeId(inst, 6); - const uint32_t data_type_bit_width = _.GetBitWidth(data_type); - - if (ext_inst_key == OpenCLLIB::Vstore_half || - ext_inst_key == OpenCLLIB::Vstore_half_r) { - if (!_.IsFloatScalarType(data_type) || - (data_type_bit_width != 32 && data_type_bit_width != 64)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Data to be a 32 or 64-bit float scalar"; - } - } else { - if (!_.IsFloatVectorType(data_type) || - (data_type_bit_width != 32 && data_type_bit_width != 64)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Data to be a 32 or 64-bit float vector"; - } - - const uint32_t num_components = _.GetDimension(data_type); - if (num_components > 4 && num_components != 8 && - num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Data to have 2, 3, 4, 8 or 16 components"; - } - } - - const uint32_t size_t_bit_width = GetSizeTBitWidth(_); - if (!size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << " can only be used with physical addressing models"; - } - - if (!_.IsIntScalarType(offset_type) || - _.GetBitWidth(offset_type) != size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Offset to be of type size_t (" - << size_t_bit_width - << "-bit integer for the addressing model used in the module)"; - } - - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::Generic && - p_storage_class != spv::StorageClass::CrossWorkgroup && - p_storage_class != spv::StorageClass::Workgroup && - p_storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P storage class to be Generic, " - "CrossWorkgroup, Workgroup or Function"; - } - - if ((!_.IsFloatScalarType(p_data_type) || - _.GetBitWidth(p_data_type) != 16) && - !_.ContainsUntypedPointer(p_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand P data type to be 16-bit float scalar"; - } - - // Rounding mode enum is checked by assembler. - break; + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P to be a pointer"; } - case OpenCLLIB::Shuffle: - case OpenCLLIB::Shuffle2: { - if (!_.IsFloatVectorType(result_type) && - !_.IsIntVectorType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be an int or float vector type"; - } - - const uint32_t result_num_components = _.GetDimension(result_type); - if (result_num_components != 2 && result_num_components != 4 && - result_num_components != 8 && result_num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to have 2, 4, 8 or 16 components"; - } - - uint32_t operand_index = 4; - const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); - - if (ext_inst_key == OpenCLLIB::Shuffle2) { - const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); - if (x_type != y_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operands X and Y to be of the same type"; - } - } - - const uint32_t shuffle_mask_type = - _.GetOperandTypeId(inst, operand_index++); - - if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X to be an int or float vector"; - } - - const uint32_t x_num_components = _.GetDimension(x_type); - if (x_num_components != 2 && x_num_components != 4 && - x_num_components != 8 && x_num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X to have 2, 4, 8 or 16 components"; - } - - const uint32_t result_component_type = _.GetComponentType(result_type); - - if (result_component_type != _.GetComponentType(x_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand X and Result Type to have equal " - "component types"; - } - - if (!_.IsIntVectorType(shuffle_mask_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Shuffle Mask to be an int vector"; - } - - if (result_num_components != _.GetDimension(shuffle_mask_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Shuffle Mask to have the same number of " - "components as Result Type"; - } - - if (_.GetBitWidth(result_component_type) != - _.GetBitWidth(shuffle_mask_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Shuffle Mask components to have the same " - "bit width as Result Type components"; - } - break; + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function"; } - case OpenCLLIB::Printf: { - if (!_.IsIntScalarType(result_type) || - _.GetBitWidth(result_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a 32-bit int type"; - } - - const uint32_t format_type = _.GetOperandTypeId(inst, 4); - spv::StorageClass format_storage_class; - uint32_t format_data_type = 0; - if (!_.GetPointerTypeInfo(format_type, &format_data_type, - &format_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Format to be a pointer"; - } - - if (_.HasExtension( - Extension::kSPV_EXT_relaxed_printf_string_address_space)) { - if (format_storage_class != spv::StorageClass::UniformConstant && - // Extension SPV_EXT_relaxed_printf_string_address_space allows - // format strings in Global, Local, Private and Generic address - // spaces - - // Global - format_storage_class != spv::StorageClass::CrossWorkgroup && - // Local - format_storage_class != spv::StorageClass::Workgroup && - // Private - format_storage_class != spv::StorageClass::Function && - // Generic - format_storage_class != spv::StorageClass::Generic) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Format storage class to be UniformConstant, " - "Crossworkgroup, Workgroup, Function, or Generic"; - } - } else { - if (format_storage_class != spv::StorageClass::UniformConstant) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Format storage class to be UniformConstant"; - } - } - - // If pointer points to an array, get the type of an element - if (_.IsIntArrayType(format_data_type)) - format_data_type = _.GetComponentType(format_data_type); - - if ((!_.IsIntScalarType(format_data_type) || - _.GetBitWidth(format_data_type) != 8) && - !_.ContainsUntypedPointer(format_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Format data type to be 8-bit int"; - } - break; + if ((!_.IsFloatScalarType(p_data_type, 16)) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand P data type to be 16-bit float scalar"; } - case OpenCLLIB::Prefetch: { - if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected Result Type to be void"; - } - - const uint32_t p_type = _.GetOperandTypeId(inst, 4); - const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5); - - spv::StorageClass p_storage_class; - uint32_t p_data_type = 0; - if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Ptr to be a pointer"; - } - - if (p_storage_class != spv::StorageClass::CrossWorkgroup) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Ptr storage class to be CrossWorkgroup"; - } - - if (!_.IsFloatScalarOrVectorType(p_data_type) && - !_.IsIntScalarOrVectorType(p_data_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Ptr data type to be int or float scalar or " - "vector"; - } - - const uint32_t num_components = _.GetDimension(p_data_type); - if (num_components > 4 && num_components != 8 && num_components != 16) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected Result Type to be a scalar or a vector with 2, " - "3, 4, 8 or 16 components"; - } - - const uint32_t size_t_bit_width = GetSizeTBitWidth(_); - if (!size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << " can only be used with physical addressing models"; - } - - if (!_.IsIntScalarType(num_elements_type) || - _.GetBitWidth(num_elements_type) != size_t_bit_width) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Num Elements to be of type size_t (" - << size_t_bit_width - << "-bit integer for the addressing model used in the module)"; - } - break; - } - } - } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || - ext_inst_type == - SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { - if (!_.IsVoidType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected result type must be a result id of " - << "OpTypeVoid"; + // Rounding mode enum is checked by assembler. + break; } - const bool vulkanDebugInfo = - ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + case OpenCLLIB::Shuffle: + case OpenCLLIB::Shuffle2: { + if (!_.IsFloatVectorType(result_type) && + !_.IsIntVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be an int or float vector type"; + } - auto num_words = inst->words().size(); + const uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != 2 && result_num_components != 4 && + result_num_components != 8 && result_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to have 2, 4, 8 or 16 components"; + } - // Handle any non-common NonSemanticShaderDebugInfo instructions. - if (vulkanDebugInfo) { - const NonSemanticShaderDebugInfo100Instructions ext_inst_key = - NonSemanticShaderDebugInfo100Instructions(ext_inst_index); - switch (ext_inst_key) { - // The following block of instructions will be handled by the common - // validation. - case NonSemanticShaderDebugInfo100DebugInfoNone: - case NonSemanticShaderDebugInfo100DebugCompilationUnit: - case NonSemanticShaderDebugInfo100DebugTypePointer: - case NonSemanticShaderDebugInfo100DebugTypeQualifier: - case NonSemanticShaderDebugInfo100DebugTypeArray: - case NonSemanticShaderDebugInfo100DebugTypeVector: - case NonSemanticShaderDebugInfo100DebugTypedef: - case NonSemanticShaderDebugInfo100DebugTypeFunction: - case NonSemanticShaderDebugInfo100DebugTypeEnum: - case NonSemanticShaderDebugInfo100DebugTypeComposite: - case NonSemanticShaderDebugInfo100DebugTypeMember: - case NonSemanticShaderDebugInfo100DebugTypeInheritance: - case NonSemanticShaderDebugInfo100DebugTypePtrToMember: - case NonSemanticShaderDebugInfo100DebugTypeTemplate: - case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter: - case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter: - case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack: - case NonSemanticShaderDebugInfo100DebugGlobalVariable: - case NonSemanticShaderDebugInfo100DebugFunctionDeclaration: - case NonSemanticShaderDebugInfo100DebugFunction: - case NonSemanticShaderDebugInfo100DebugLexicalBlock: - case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator: - case NonSemanticShaderDebugInfo100DebugScope: - case NonSemanticShaderDebugInfo100DebugNoScope: - case NonSemanticShaderDebugInfo100DebugInlinedAt: - case NonSemanticShaderDebugInfo100DebugLocalVariable: - case NonSemanticShaderDebugInfo100DebugInlinedVariable: - case NonSemanticShaderDebugInfo100DebugValue: - case NonSemanticShaderDebugInfo100DebugOperation: - case NonSemanticShaderDebugInfo100DebugExpression: - case NonSemanticShaderDebugInfo100DebugMacroDef: - case NonSemanticShaderDebugInfo100DebugMacroUndef: - case NonSemanticShaderDebugInfo100DebugImportedEntity: - case NonSemanticShaderDebugInfo100DebugSource: - break; + uint32_t operand_index = 4; + const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); - // These checks are for operands that are differnet in - // ShaderDebugInfo100 - case NonSemanticShaderDebugInfo100DebugTypeBasic: { - CHECK_CONST_UINT_OPERAND("Flags", 8); - break; + if (ext_inst_key == OpenCLLIB::Shuffle2) { + const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); + if (x_type != y_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operands X and Y to be of the same type"; } - case NonSemanticShaderDebugInfo100DebugDeclare: { - for (uint32_t word_index = 8; word_index < num_words; ++word_index) { - auto index_inst = _.FindDef(inst->word(word_index)); - auto type_id = index_inst != nullptr ? index_inst->type_id() : 0; - if (type_id == 0 || !IsIntScalar(_, type_id, false, false)) + } + + const uint32_t shuffle_mask_type = + _.GetOperandTypeId(inst, operand_index++); + + if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X to be an int or float vector"; + } + + const uint32_t x_num_components = _.GetDimension(x_type); + if (x_num_components != 2 && x_num_components != 4 && + x_num_components != 8 && x_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X to have 2, 4, 8 or 16 components"; + } + + const uint32_t result_component_type = _.GetComponentType(result_type); + + if (result_component_type != _.GetComponentType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand X and Result Type to have equal " + "component types"; + } + + if (!_.IsIntVectorType(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Shuffle Mask to be an int vector"; + } + + if (result_num_components != _.GetDimension(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Shuffle Mask to have the same number of " + "components as Result Type"; + } + + if (_.GetBitWidth(result_component_type) != + _.GetBitWidth(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Shuffle Mask components to have the same " + "bit width as Result Type components"; + } + break; + } + + case OpenCLLIB::Printf: { + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a 32-bit int type"; + } + + const uint32_t format_type = _.GetOperandTypeId(inst, 4); + spv::StorageClass format_storage_class; + uint32_t format_data_type = 0; + if (!_.GetPointerTypeInfo(format_type, &format_data_type, + &format_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Format to be a pointer"; + } + + if (_.HasExtension( + Extension::kSPV_EXT_relaxed_printf_string_address_space)) { + if (format_storage_class != spv::StorageClass::UniformConstant && + // Extension SPV_EXT_relaxed_printf_string_address_space allows + // format strings in Global, Local, Private and Generic address + // spaces + + // Global + format_storage_class != spv::StorageClass::CrossWorkgroup && + // Local + format_storage_class != spv::StorageClass::Workgroup && + // Private + format_storage_class != spv::StorageClass::Function && + // Generic + format_storage_class != spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Format storage class to be UniformConstant, " + "Crossworkgroup, Workgroup, Function, or Generic"; + } + } else { + if (format_storage_class != spv::StorageClass::UniformConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Format storage class to be UniformConstant"; + } + } + + // If pointer points to an array, get the type of an element + if (_.IsIntArrayType(format_data_type)) + format_data_type = _.GetComponentType(format_data_type); + + if (!_.IsIntScalarType(format_data_type, 8) && + !_.ContainsUntypedPointer(format_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Format data type to be 8-bit int"; + } + break; + } + + case OpenCLLIB::Prefetch: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": expected Result Type to be void"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5); + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Ptr to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::CrossWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Ptr storage class to be CrossWorkgroup"; + } + + if (!_.IsFloatScalarOrVectorType(p_data_type) && + !_.IsIntScalarOrVectorType(p_data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Ptr data type to be int or float scalar or " + "vector"; + } + + const uint32_t num_components = _.GetDimension(p_data_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(num_elements_type) || + _.GetBitWidth(num_elements_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Num Elements to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + break; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInstDebugInfo(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const uint32_t ext_inst_index = inst->word(4); + if (!_.IsVoidType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected result type must be a result id of " << "OpTypeVoid"; + } + + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + + auto num_words = inst->words().size(); + + // Parse the declared NSDI version so optional-operand checks are strict + // (num_words == n) for version kNSDIKnownVersion and lenient (num_words >= n) + // for future versions that may add trailing operands. + static const uint32_t kNSDIKnownVersion = NonSemanticShaderDebugInfoVersion; + const uint32_t nsdi_version = vulkanDebugInfo ? GetNSDIVersion(_, inst) : 0; + // True if the optional operand at word |n| is present and should be checked. + auto has_optional_at = [&](uint32_t n) -> bool { + return num_words >= n && + (nsdi_version > kNSDIKnownVersion || num_words == n); + }; + + // Handle any non-common NonSemanticShaderDebugInfo instructions. + if (vulkanDebugInfo) { + const NonSemanticShaderDebugInfoInstructions ext_inst_key = + NonSemanticShaderDebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + // The following block of instructions will be handled by the common + // validation. + case NonSemanticShaderDebugInfoDebugInfoNone: + case NonSemanticShaderDebugInfoDebugCompilationUnit: + case NonSemanticShaderDebugInfoDebugTypePointer: + case NonSemanticShaderDebugInfoDebugTypeQualifier: + case NonSemanticShaderDebugInfoDebugTypeArray: + case NonSemanticShaderDebugInfoDebugTypeVector: + case NonSemanticShaderDebugInfoDebugTypedef: + case NonSemanticShaderDebugInfoDebugTypeFunction: + case NonSemanticShaderDebugInfoDebugTypeEnum: + case NonSemanticShaderDebugInfoDebugTypeComposite: + case NonSemanticShaderDebugInfoDebugTypeMember: + case NonSemanticShaderDebugInfoDebugTypeInheritance: + case NonSemanticShaderDebugInfoDebugTypePtrToMember: + case NonSemanticShaderDebugInfoDebugTypeTemplate: + case NonSemanticShaderDebugInfoDebugTypeTemplateParameter: + case NonSemanticShaderDebugInfoDebugTypeTemplateTemplateParameter: + case NonSemanticShaderDebugInfoDebugTypeTemplateParameterPack: + case NonSemanticShaderDebugInfoDebugGlobalVariable: + case NonSemanticShaderDebugInfoDebugFunctionDeclaration: + case NonSemanticShaderDebugInfoDebugFunction: + case NonSemanticShaderDebugInfoDebugLexicalBlock: + case NonSemanticShaderDebugInfoDebugLexicalBlockDiscriminator: + case NonSemanticShaderDebugInfoDebugScope: + case NonSemanticShaderDebugInfoDebugNoScope: + case NonSemanticShaderDebugInfoDebugInlinedAt: + case NonSemanticShaderDebugInfoDebugLocalVariable: + case NonSemanticShaderDebugInfoDebugInlinedVariable: + case NonSemanticShaderDebugInfoDebugValue: + case NonSemanticShaderDebugInfoDebugOperation: + case NonSemanticShaderDebugInfoDebugExpression: + case NonSemanticShaderDebugInfoDebugMacroDef: + case NonSemanticShaderDebugInfoDebugMacroUndef: + case NonSemanticShaderDebugInfoDebugImportedEntity: + case NonSemanticShaderDebugInfoDebugSource: + break; + + // These checks are for operands that are different in + // NonSemantic.Shader.DebugInfo + case NonSemanticShaderDebugInfoDebugTypeBasic: { + CHECK_CONST_UINT_OPERAND("Flags", 8); + // Optional FPEncoding parameter (5th operand, word index 9) + if (has_optional_at(10)) { + CHECK_CONST_UINT_OPERAND("FPEncoding", 9); + } + break; + } + case NonSemanticShaderDebugInfoDebugDeclare: { + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + auto index_inst = _.FindDef(inst->word(word_index)); + auto type_id = index_inst != nullptr ? index_inst->type_id() : 0; + if (type_id == 0 || !IsIntScalar(_, type_id, false, false)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected index must be scalar integer"; + } + break; + } + case NonSemanticShaderDebugInfoDebugTypeMatrix: { + CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5); + + CHECK_CONST_UINT_OPERAND("Vector Count", 6); + + uint32_t vector_count = inst->word(6); + uint64_t const_val; + if (!_.EvalConstantValUint64(vector_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": Vector Count must be 32-bit integer OpConstant"; + } + + vector_count = const_val & 0xffffffff; + if (!vector_count || vector_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": Vector Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case NonSemanticShaderDebugInfoDebugTypeVectorIdEXT: { + CHECK_NSDI_MIN_VERSION(NonSemanticShaderDebugInfoVersion); + CHECK_DEBUG_OPERAND("Component Type", CommonDebugInfoDebugTypeBasic, 5); + // Component Count may be OpSpecConstant when the cooperative vector + // type uses a specialization constant for its size. + CHECK_CONST_OR_SPEC_UINT_OPERAND("Component Count", 6); + break; + } + case NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR: { + CHECK_NSDI_MIN_VERSION(NonSemanticShaderDebugInfoVersion); + CHECK_DEBUG_OPERAND("Component Type", CommonDebugInfoDebugTypeBasic, 5); + // Scope, Rows, Columns, and Use may be OpSpecConstant when the + // cooperative matrix type uses specialization constants. + CHECK_CONST_OR_SPEC_UINT_OPERAND("Scope", 6); + CHECK_CONST_OR_SPEC_UINT_OPERAND("Rows", 7); + CHECK_CONST_OR_SPEC_UINT_OPERAND("Columns", 8); + CHECK_CONST_OR_SPEC_UINT_OPERAND("Use", 9); + break; + } + case NonSemanticShaderDebugInfoDebugFunctionDefinition: { + CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5); + CHECK_OPERAND("Definition", spv::Op::OpFunction, 6); + const auto* current_function = inst->function(); + if (current_function->first_block()->id() != inst->block()->id()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": must be in the entry basic block of the function"; + } + + const uint32_t definition_id = inst->word(6); + if (definition_id != current_function->id()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": operand Definition must point to the OpFunction it is " + "inside"; + } + break; + } + case NonSemanticShaderDebugInfoDebugLine: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line Start", 6); + CHECK_CONST_UINT_OPERAND("Line End", 7); + CHECK_CONST_UINT_OPERAND("Column Start", 8); + CHECK_CONST_UINT_OPERAND("Column End", 9); + + // above already validates if 32-bit and non-spec constant + // but want to use EvalInt32IfConst to be consistent with other Eval + // locations + bool is_int32 = false, is_const_int32 = false; + uint32_t line_start = 0; + uint32_t line_end = 0; + uint32_t column_start = 0; + uint32_t column_end = 0; + std::tie(is_int32, is_const_int32, line_start) = + _.EvalInt32IfConst(inst->word(6)); + std::tie(is_int32, is_const_int32, line_end) = + _.EvalInt32IfConst(inst->word(7)); + std::tie(is_int32, is_const_int32, column_start) = + _.EvalInt32IfConst(inst->word(8)); + std::tie(is_int32, is_const_int32, column_end) = + _.EvalInt32IfConst(inst->word(9)); + if (line_start == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": operand Line Start (0) is not allowed, source lines " + "start at Line 1"; + } else if (line_end < line_start) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Line End (" + << line_end << ") is less than Line Start (" << line_start + << ")"; + } else if (line_start == line_end && column_end < column_start) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Column End (" + << column_end << ") is less than Column Start (" + << column_start << ") when Line Start equals Line End"; + } + // Make sure Line is found in the DebugSource + auto* debug_source_inst = _.FindDef(inst->word(5)); + const std::vector<uint32_t>& line_lengths = + _.GetDebugSourceLineLength(debug_source_inst->id()); + if (!line_lengths.empty()) { + if (line_end > line_lengths.size()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Line End (" + << line_end << ") is larger then the " << line_lengths.size() + << " lines found in the DebugSource text"; + } + if (line_start == line_end) { + const uint32_t line_length = line_lengths[line_end - 1]; + if (column_end > line_length) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected index must be scalar integer"; - } - break; - } - case NonSemanticShaderDebugInfo100DebugTypeMatrix: { - CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5); - - CHECK_CONST_UINT_OPERAND("Vector Count", 6); - - uint32_t vector_count = inst->word(6); - uint64_t const_val; - if (!_.EvalConstantValUint64(vector_count, &const_val)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << ": Vector Count must be 32-bit integer OpConstant"; - } - - vector_count = const_val & 0xffffffff; - if (!vector_count || vector_count > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Vector Count must be positive " - << "integer less than or equal to 4"; - } - break; - } - case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { - CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5); - CHECK_OPERAND("Definition", spv::Op::OpFunction, 6); - const auto* current_function = inst->function(); - if (current_function->first_block()->id() != inst->block()->id()) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << ": must be in the entry basic block of the function"; - } - - const uint32_t definition_id = inst->word(6); - if (definition_id != current_function->id()) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << ": operand Definition must point to the OpFunction it is " - "inside"; - } - break; - } - case NonSemanticShaderDebugInfo100DebugLine: { - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); - CHECK_CONST_UINT_OPERAND("Line Start", 6); - CHECK_CONST_UINT_OPERAND("Line End", 7); - CHECK_CONST_UINT_OPERAND("Column Start", 8); - CHECK_CONST_UINT_OPERAND("Column End", 9); - - // above already validates if 32-bit and non-spec constant - // but want to use EvalInt32IfConst to be consistent with other Eval - // locations - bool is_int32 = false, is_const_int32 = false; - uint32_t line_start = 0; - uint32_t line_end = 0; - uint32_t column_start = 0; - uint32_t column_end = 0; - std::tie(is_int32, is_const_int32, line_start) = - _.EvalInt32IfConst(inst->word(6)); - std::tie(is_int32, is_const_int32, line_end) = - _.EvalInt32IfConst(inst->word(7)); - std::tie(is_int32, is_const_int32, column_start) = - _.EvalInt32IfConst(inst->word(8)); - std::tie(is_int32, is_const_int32, column_end) = - _.EvalInt32IfConst(inst->word(9)); - if (line_end < line_start) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": operand Line End (" << line_end - << ") is less than Line Start (" << line_start << ")"; - } else if (column_end < column_start) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": operand Column End (" << column_end - << ") is less than Column Start (" << column_start << ")"; - } - break; - } - case NonSemanticShaderDebugInfo100DebugSourceContinued: { - CHECK_OPERAND("Text", spv::Op::OpString, 5); - break; - } - case NonSemanticShaderDebugInfo100DebugBuildIdentifier: { - CHECK_OPERAND("Identifier", spv::Op::OpString, 5); - CHECK_CONST_UINT_OPERAND("Flags", 6); - break; - } - case NonSemanticShaderDebugInfo100DebugStoragePath: { - CHECK_OPERAND("Path", spv::Op::OpString, 5); - break; - } - case NonSemanticShaderDebugInfo100DebugEntryPoint: { - CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5); - CHECK_DEBUG_OPERAND("Compilation Unit", - CommonDebugInfoDebugCompilationUnit, 6); - CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7); - CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8); - break; - } - - // Has no additional checks - case NonSemanticShaderDebugInfo100DebugNoLine: - break; - case NonSemanticShaderDebugInfo100InstructionsMax: - assert(0); - break; - } - } - - // Handle any non-common OpenCL insts, then common - if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || - OpenCLDebugInfo100Instructions(ext_inst_index) != - OpenCLDebugInfo100DebugModuleINTEL) { - const CommonDebugInfoInstructions ext_inst_key = - CommonDebugInfoInstructions(ext_inst_index); - switch (ext_inst_key) { - case CommonDebugInfoDebugInfoNone: - case CommonDebugInfoDebugNoScope: - break; - // The binary parser validates the opcode for DebugInfoNone, - // DebugNoScope, DebugOperation. We just check the parameters to - // DebugOperation are properly constants for vulkan debug info. - case CommonDebugInfoDebugOperation: { - CHECK_CONST_UINT_OPERAND("Operation", 5); - for (uint32_t i = 6; i < num_words; ++i) { - CHECK_CONST_UINT_OPERAND("Operand", i); - } - break; - } - case CommonDebugInfoDebugCompilationUnit: { - CHECK_CONST_UINT_OPERAND("Version", 5); - CHECK_CONST_UINT_OPERAND("DWARF Version", 6); - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Language", 8); - break; - } - case CommonDebugInfoDebugSource: { - CHECK_OPERAND("File", spv::Op::OpString, 5); - if (num_words == 7) CHECK_OPERAND("Text", spv::Op::OpString, 6); - break; - } - case CommonDebugInfoDebugTypeBasic: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - CHECK_OPERAND("Size", spv::Op::OpConstant, 6); - CHECK_CONST_UINT_OPERAND("Encoding", 7); - break; - } - case CommonDebugInfoDebugTypePointer: { - auto validate_base_type = ValidateOperandDebugType( - _, "Base Type", inst, 5, ext_inst_name, false); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - CHECK_CONST_UINT_OPERAND("Storage Class", 6); - CHECK_CONST_UINT_OPERAND("Flags", 7); - break; - } - case CommonDebugInfoDebugTypeQualifier: { - auto validate_base_type = ValidateOperandDebugType( - _, "Base Type", inst, 5, ext_inst_name, false); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - CHECK_CONST_UINT_OPERAND("Type Qualifier", 6); - break; - } - case CommonDebugInfoDebugTypeVector: { - auto validate_base_type = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - - CHECK_CONST_UINT_OPERAND("Component Count", 6); - uint32_t component_count = inst->word(6); - if (vulkanDebugInfo) { - uint64_t const_val; - if (!_.EvalConstantValUint64(component_count, &const_val)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() - << ": Component Count must be 32-bit integer OpConstant"; + << GetExtInstName(_, inst) << ": operand Column End (" + << column_end << ") is larger then Line " << line_end + << " column length of " << line_length + << " found in the DebugSource text"; } - component_count = const_val & 0xffffffff; + } else { + uint32_t line_length = line_lengths[line_start - 1]; + if (column_start > line_length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Column Start (" + << column_start << ") is larger then Line " << line_start + << " column length of " << line_length + << " found in the DebugSource text"; + } + line_length = line_lengths[line_end - 1]; + if (column_end > line_length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": operand Column End (" + << column_end << ") is larger then Line " << line_end + << " column length of " << line_length + << " found in the DebugSource text"; + } } - - if (!component_count || component_count > 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be positive " - << "integer less than or equal to 4"; - } - break; } - case CommonDebugInfoDebugTypeArray: { - auto validate_base_type = ValidateOperandDebugType( - _, "Base Type", inst, 5, ext_inst_name, false); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - for (uint32_t i = 6; i < num_words; ++i) { - bool invalid = false; - auto* component_count = _.FindDef(inst->word(i)); - if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { - // TODO: We need a spec discussion for the runtime array for - // OpenCL. - if (!vulkanDebugInfo && !component_count->word(3)) { + + break; + } + case NonSemanticShaderDebugInfoDebugSourceContinued: { + CHECK_OPERAND("Text", spv::Op::OpString, 5); + // OpenCL didn't have a Continued version + BuildDebugSourceLineLength(_, inst, ext_inst_index); + break; + } + case NonSemanticShaderDebugInfoDebugBuildIdentifier: { + CHECK_OPERAND("Identifier", spv::Op::OpString, 5); + CHECK_CONST_UINT_OPERAND("Flags", 6); + break; + } + case NonSemanticShaderDebugInfoDebugStoragePath: { + CHECK_OPERAND("Path", spv::Op::OpString, 5); + break; + } + case NonSemanticShaderDebugInfoDebugEntryPoint: { + CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5); + CHECK_DEBUG_OPERAND("Compilation Unit", + CommonDebugInfoDebugCompilationUnit, 6); + CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7); + CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8); + break; + } + + // Has no additional checks + case NonSemanticShaderDebugInfoDebugNoLine: + break; + case NonSemanticShaderDebugInfoInstructionsMax: + assert(0); + break; + } + } + + // Handle any non-common OpenCL insts, then common + if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + OpenCLDebugInfo100Instructions(ext_inst_index) != + OpenCLDebugInfo100DebugModuleINTEL) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case CommonDebugInfoDebugInfoNone: + case CommonDebugInfoDebugNoScope: + break; + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation. We just check the parameters to + // DebugOperation are properly constants for vulkan debug info. + case CommonDebugInfoDebugOperation: { + CHECK_CONST_UINT_OPERAND("Operation", 5); + for (uint32_t i = 6; i < num_words; ++i) { + CHECK_CONST_UINT_OPERAND("Operand", i); + } + break; + } + case CommonDebugInfoDebugCompilationUnit: { + CHECK_CONST_UINT_OPERAND("Version", 5); + CHECK_CONST_UINT_OPERAND("DWARF Version", 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Language", 8); + break; + } + case CommonDebugInfoDebugSource: { + CHECK_OPERAND("File", spv::Op::OpString, 5); + if (has_optional_at(7)) CHECK_OPERAND("Text", spv::Op::OpString, 6); + BuildDebugSourceLineLength(_, inst, ext_inst_index); + break; + } + case CommonDebugInfoDebugTypeBasic: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_OPERAND("Size", spv::Op::OpConstant, 6); + CHECK_CONST_UINT_OPERAND("Encoding", 7); + break; + } + case CommonDebugInfoDebugTypePointer: { + auto validate_base_type = + ValidateOperandDebugType(_, "Base Type", inst, 5, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Storage Class", 6); + CHECK_CONST_UINT_OPERAND("Flags", 7); + break; + } + case CommonDebugInfoDebugTypeQualifier: { + auto validate_base_type = + ValidateOperandDebugType(_, "Base Type", inst, 5, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Type Qualifier", 6); + break; + } + case CommonDebugInfoDebugTypeVector: { + auto validate_base_type = ValidateOperandBaseType(_, inst, 5); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + CHECK_CONST_UINT_OPERAND("Component Count", 6); + uint32_t component_count = inst->word(6); + if (vulkanDebugInfo) { + uint64_t const_val; + if (!_.EvalConstantValUint64(component_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": Component Count must be 32-bit integer OpConstant"; + } + component_count = const_val & 0xffffffff; + } + + if (!component_count || component_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": Component Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case CommonDebugInfoDebugTypeArray: { + auto validate_base_type = + ValidateOperandDebugType(_, "Base Type", inst, 5, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + bool invalid = false; + auto* component_count = _.FindDef(inst->word(i)); + if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { + // TODO: We need a spec discussion for the runtime array for + // OpenCL. + if (!vulkanDebugInfo && !component_count->word(3)) { + invalid = true; + } + } else if (vulkanDebugInfo && IsSpecConstIntScalarTypeWith32Or64Bits( + _, component_count)) { + // Spec constants are valid component counts for + // NonSemantic.Shader.DebugInfo. + } else if (component_count->words().size() > 6 && + (CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugGlobalVariable)) { + auto* component_count_type = _.FindDef(component_count->word(6)); + if (component_count_type->words().size() > 7) { + uint32_t encoding = component_count_type->word(7); + if (CommonDebugInfoInstructions(component_count_type->word(4)) != + CommonDebugInfoDebugTypeBasic || + (vulkanDebugInfo && !IsUint32Constant(_, encoding)) || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo + ? GetUint32Constant(_, encoding) + : encoding) != OpenCLDebugInfo100Unsigned) { invalid = true; - } - } else if (component_count->words().size() > 6 && - (CommonDebugInfoInstructions(component_count->word(4)) == - CommonDebugInfoDebugLocalVariable || - CommonDebugInfoInstructions(component_count->word(4)) == - CommonDebugInfoDebugGlobalVariable)) { - auto* component_count_type = _.FindDef(component_count->word(6)); - if (component_count_type->words().size() > 7) { - uint32_t encoding = component_count_type->word(7); - if (CommonDebugInfoInstructions(component_count_type->word( - 4)) != CommonDebugInfoDebugTypeBasic || - (vulkanDebugInfo && !IsUint32Constant(_, encoding)) || - OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( - vulkanDebugInfo - ? GetUint32Constant(_, encoding) - : encoding) != OpenCLDebugInfo100Unsigned) { - invalid = true; - } else { - // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable - // must have Unsigned encoding and 32 or 64 as its size in - // bits. - Instruction* size_in_bits = - _.FindDef(component_count_type->word(6)); - if (!_.IsIntScalarType(size_in_bits->type_id()) || - (size_in_bits->word(3) != 32 && - size_in_bits->word(3) != 64)) { - invalid = true; - } - } } else { - invalid = true; + // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable + // must have Unsigned encoding and 32 or 64 as its size in + // bits. + Instruction* size_in_bits = + _.FindDef(component_count_type->word(6)); + if (!_.IsIntScalarType(size_in_bits->type_id()) || + (size_in_bits->word(3) != 32 && + size_in_bits->word(3) != 64)) { + invalid = true; + } } } else { invalid = true; } - if (invalid) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": Component Count must be " - << "OpConstant with a 32- or 64-bits integer scalar type " - "or " - << "DebugGlobalVariable or DebugLocalVariable with a 32- " - "or " - << "64-bits unsigned integer scalar type"; - } - } - break; - } - case CommonDebugInfoDebugTypedef: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - auto validate_base_type = - ValidateOperandBaseType(_, inst, 6, ext_inst_name); - if (validate_base_type != SPV_SUCCESS) return validate_base_type; - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - break; - } - case CommonDebugInfoDebugTypeFunction: { - CHECK_CONST_UINT_OPERAND("Flags", 5); - auto* return_type = _.FindDef(inst->word(6)); - // TODO: We need a spec discussion that we have to allow return and - // parameter types of a DebugTypeFunction to have template parameter. - if (return_type->opcode() != spv::Op::OpTypeVoid) { - auto validate_return = ValidateOperandDebugType( - _, "Return Type", inst, 6, ext_inst_name, true); - if (validate_return != SPV_SUCCESS) return validate_return; - } - for (uint32_t word_index = 7; word_index < num_words; ++word_index) { - auto validate_param = ValidateOperandDebugType( - _, "Parameter Types", inst, word_index, ext_inst_name, true); - if (validate_param != SPV_SUCCESS) return validate_param; - } - break; - } - case CommonDebugInfoDebugTypeEnum: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugInfoNone; - }, - inst, 6)) { - auto validate_underlying_type = ValidateOperandDebugType( - _, "Underlying Types", inst, 6, ext_inst_name, false); - if (validate_underlying_type != SPV_SUCCESS) - return validate_underlying_type; - } - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Size", spv::Op::OpConstant, 11); - auto* size = _.FindDef(inst->word(11)); - if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Size is a " - << "positive integer"; - } - CHECK_CONST_UINT_OPERAND("Flags", 12); - for (uint32_t word_index = 13; word_index + 1 < num_words; - word_index += 2) { - CHECK_OPERAND("Value", spv::Op::OpConstant, word_index); - CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1); - } - break; - } - case CommonDebugInfoDebugTypeComposite: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugInfoNone; - }, - inst, 12)) { - CHECK_OPERAND("Size", spv::Op::OpConstant, 12); - } - CHECK_CONST_UINT_OPERAND("Flags", 13); - for (uint32_t word_index = 14; word_index < num_words; ++word_index) { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugTypeMember || - dbg_inst == CommonDebugInfoDebugFunction || - dbg_inst == CommonDebugInfoDebugTypeInheritance; - }, - inst, word_index)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Members " - << "must be DebugTypeMember, DebugFunction, or " - "DebugTypeInheritance"; - } - } - break; - } - case CommonDebugInfoDebugTypeMember: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - // TODO: We need a spec discussion that we have to allow member types - // to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - // NonSemantic.Shader.DebugInfo doesn't have the Parent operand - if (vulkanDebugInfo) { - CHECK_OPERAND("Offset", spv::Op::OpConstant, 10); - CHECK_OPERAND("Size", spv::Op::OpConstant, 11); - CHECK_CONST_UINT_OPERAND("Flags", 12); - if (num_words == 14) - CHECK_OPERAND("Value", spv::Op::OpConstant, 13); } else { - CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, - 10); - CHECK_OPERAND("Offset", spv::Op::OpConstant, 11); - CHECK_OPERAND("Size", spv::Op::OpConstant, 12); - CHECK_CONST_UINT_OPERAND("Flags", 13); - if (num_words == 15) - CHECK_OPERAND("Value", spv::Op::OpConstant, 14); + invalid = true; } - break; - } - case CommonDebugInfoDebugTypeInheritance: { - CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5); - auto* debug_inst = _.FindDef(inst->word(5)); - auto composite_type = - OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); - if (composite_type != OpenCLDebugInfo100Class && - composite_type != OpenCLDebugInfo100Structure) { + if (invalid) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Child must be class or struct debug " - "type"; + << GetExtInstName(_, inst) << ": Component Count must be " + << (vulkanDebugInfo ? "a constant instruction" + : "OpConstant") + << " with a 32- or 64-bits integer scalar type or " + << "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or " + << "64-bits unsigned integer scalar type"; } - CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6); - debug_inst = _.FindDef(inst->word(6)); - composite_type = - OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); - if (composite_type != OpenCLDebugInfo100Class && - composite_type != OpenCLDebugInfo100Structure) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Parent must be class or struct debug " - "type"; - } - CHECK_OPERAND("Offset", spv::Op::OpConstant, 7); - CHECK_OPERAND("Size", spv::Op::OpConstant, 8); - CHECK_CONST_UINT_OPERAND("Flags", 9); - break; } - case CommonDebugInfoDebugFunction: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6); - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); - CHECK_CONST_UINT_OPERAND("Flags", 12); - CHECK_CONST_UINT_OPERAND("Scope Line", 13); - // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the - // OpFunction - if (vulkanDebugInfo) { - if (num_words == 15) { - CHECK_DEBUG_OPERAND("Declaration", - CommonDebugInfoDebugFunctionDeclaration, 14); - } - } else { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugInfoNone; - }, - inst, 14)) { - CHECK_OPERAND("Function", spv::Op::OpFunction, 14); - } - if (num_words == 16) { - CHECK_DEBUG_OPERAND("Declaration", - CommonDebugInfoDebugFunctionDeclaration, 15); - } - } - break; - } - case CommonDebugInfoDebugFunctionDeclaration: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6); - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); - CHECK_CONST_UINT_OPERAND("Flags", 12); - break; - } - case CommonDebugInfoDebugLexicalBlock: { - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); - CHECK_CONST_UINT_OPERAND("Line", 6); - CHECK_CONST_UINT_OPERAND("Column", 7); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - if (num_words == 10) CHECK_OPERAND("Name", spv::Op::OpString, 9); - break; - } - case CommonDebugInfoDebugScope: { - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 7) { - CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6); - } - break; - } - case CommonDebugInfoDebugLocalVariable: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - // TODO: We need a spec discussion that we have to allow local - // variable types to have template parameter. - auto validate_type = - ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_parent = - ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); - if (validate_parent != SPV_SUCCESS) return validate_parent; - CHECK_CONST_UINT_OPERAND("Flags", 11); - if (num_words == 13) { - CHECK_CONST_UINT_OPERAND("ArgNumber", 12); - } - break; - } - case CommonDebugInfoDebugDeclare: { - CHECK_DEBUG_OPERAND("Local Variable", - CommonDebugInfoDebugLocalVariable, 5); - auto* operand = _.FindDef(inst->word(6)); - if (operand->opcode() != spv::Op::OpVariable && - operand->opcode() != spv::Op::OpFunctionParameter) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Variable must be a result id of " - "OpVariable or OpFunctionParameter"; - } - - CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); - break; - } - case CommonDebugInfoDebugExpression: { - for (uint32_t word_index = 5; word_index < num_words; ++word_index) { - CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation, - word_index); - } - break; - } - case CommonDebugInfoDebugTypeTemplate: { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugTypeComposite || - dbg_inst == CommonDebugInfoDebugFunction; - }, - inst, 5)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Target must be DebugTypeComposite " - << "or DebugFunction"; - } - for (uint32_t word_index = 6; word_index < num_words; ++word_index) { - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == - CommonDebugInfoDebugTypeTemplateParameter || - dbg_inst == - CommonDebugInfoDebugTypeTemplateTemplateParameter; - }, - inst, word_index)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Parameters must be " - << "DebugTypeTemplateParameter or " - << "DebugTypeTemplateTemplateParameter"; - } - } - break; - } - case CommonDebugInfoDebugTypeTemplateParameter: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - auto validate_actual_type = ValidateOperandDebugType( - _, "Actual Type", inst, 6, ext_inst_name, false); - if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugInfoNone; - }, - inst, 7)) { - CHECK_OPERAND("Value", spv::Op::OpConstant, 7); - } - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8); - CHECK_CONST_UINT_OPERAND("Line", 9); - CHECK_CONST_UINT_OPERAND("Column", 10); - break; - } - case CommonDebugInfoDebugGlobalVariable: { - CHECK_OPERAND("Name", spv::Op::OpString, 5); - auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, - ext_inst_name, false); - if (validate_type != SPV_SUCCESS) return validate_type; - CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); - CHECK_CONST_UINT_OPERAND("Line", 8); - CHECK_CONST_UINT_OPERAND("Column", 9); - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); - if (!DoesDebugInfoOperandMatchExpectation( - _, - [](CommonDebugInfoInstructions dbg_inst) { - return dbg_inst == CommonDebugInfoDebugInfoNone; - }, - inst, 12)) { - auto* operand = _.FindDef(inst->word(12)); - if (operand->opcode() != spv::Op::OpVariable && - operand->opcode() != spv::Op::OpConstant) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": " - << "expected operand Variable must be a result id of " - "OpVariable or OpConstant or DebugInfoNone"; - } - } - if (num_words == 15) { - CHECK_DEBUG_OPERAND("Static Member Declaration", - CommonDebugInfoDebugTypeMember, 14); - } - break; - } - case CommonDebugInfoDebugInlinedAt: { - CHECK_CONST_UINT_OPERAND("Line", 5); - auto validate_scope = - ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); - if (validate_scope != SPV_SUCCESS) return validate_scope; - if (num_words == 8) { - CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7); - } - break; - } - case CommonDebugInfoDebugValue: { - CHECK_DEBUG_OPERAND("Local Variable", - CommonDebugInfoDebugLocalVariable, 5); - CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); - - for (uint32_t word_index = 8; word_index < num_words; ++word_index) { - // TODO: The following code simply checks if it is a const int - // scalar or a DebugLocalVariable or DebugGlobalVariable, but we - // have to check it using the same validation for Indexes of - // OpAccessChain. - if (!IsConstWithIntScalarType(_, inst, word_index) && - !IsDebugVariableWithIntScalarType(_, inst, word_index)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << ext_inst_name() << ": expected operand Indexes is " - << "OpConstant, DebugGlobalVariable, or " - << "type is OpConstant with an integer scalar type"; - } - } - break; - } - - // TODO: Add validation rules for remaining cases as well. - case CommonDebugInfoDebugTypePtrToMember: - case CommonDebugInfoDebugTypeTemplateTemplateParameter: - case CommonDebugInfoDebugTypeTemplateParameterPack: - case CommonDebugInfoDebugLexicalBlockDiscriminator: - case CommonDebugInfoDebugInlinedVariable: - case CommonDebugInfoDebugMacroDef: - case CommonDebugInfoDebugMacroUndef: - case CommonDebugInfoDebugImportedEntity: - break; - case CommonDebugInfoInstructionsMax: - assert(0); - break; + break; } - } - } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { - auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2)); - const std::string name = import_inst->GetOperandAs<std::string>(1); - const std::string reflection = "NonSemantic.ClspvReflection."; - char* end_ptr; - auto version_string = name.substr(reflection.size()); - if (version_string.empty()) { - return _.diag(SPV_ERROR_INVALID_DATA, import_inst) - << "Missing NonSemantic.ClspvReflection import version"; - } - uint32_t version = static_cast<uint32_t>( - std::strtoul(version_string.c_str(), &end_ptr, 10)); - if (end_ptr && *end_ptr != '\0') { - return _.diag(SPV_ERROR_INVALID_DATA, import_inst) - << "NonSemantic.ClspvReflection import does not encode the " - "version correctly"; - } - if (version == 0 || version > NonSemanticClspvReflectionRevision) { - return _.diag(SPV_ERROR_INVALID_DATA, import_inst) - << "Unknown NonSemantic.ClspvReflection import version"; - } + case CommonDebugInfoDebugTypedef: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + auto validate_base_type = ValidateOperandBaseType(_, inst, 6); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; - return ValidateClspvReflectionInstruction(_, inst, version); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case CommonDebugInfoDebugTypeFunction: { + CHECK_CONST_UINT_OPERAND("Flags", 5); + auto* return_type = _.FindDef(inst->word(6)); + // TODO: We need a spec discussion that we have to allow return and + // parameter types of a DebugTypeFunction to have template parameter. + if (return_type->opcode() != spv::Op::OpTypeVoid) { + auto validate_return = + ValidateOperandDebugType(_, "Return Type", inst, 6, true); + if (validate_return != SPV_SUCCESS) return validate_return; + } + for (uint32_t word_index = 7; word_index < num_words; ++word_index) { + auto validate_param = ValidateOperandDebugType( + _, "Parameter Types", inst, word_index, true); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; + } + case CommonDebugInfoDebugTypeEnum: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 6)) { + auto validate_underlying_type = + ValidateOperandDebugType(_, "Underlying Types", inst, 6, false); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Size", spv::Op::OpConstant, 11); + auto* size = _.FindDef(inst->word(11)); + if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": expected operand Size is a " + << "positive integer"; + } + CHECK_CONST_UINT_OPERAND("Flags", 12); + for (uint32_t word_index = 13; word_index + 1 < num_words; + word_index += 2) { + CHECK_OPERAND("Value", spv::Op::OpConstant, word_index); + CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1); + } + break; + } + case CommonDebugInfoDebugTypeComposite: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + CHECK_OPERAND("Size", spv::Op::OpConstant, 12); + } + CHECK_CONST_UINT_OPERAND("Flags", 13); + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeMember || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case CommonDebugInfoDebugTypeMember: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + // TODO: We need a spec discussion that we have to allow member types + // to have template parameter. + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + // NonSemantic.Shader.DebugInfo doesn't have the Parent operand + if (vulkanDebugInfo) { + CHECK_OPERAND("Offset", spv::Op::OpConstant, 10); + CHECK_OPERAND("Size", spv::Op::OpConstant, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + if (has_optional_at(14)) + CHECK_OPERAND("Value", spv::Op::OpConstant, 13); + } else { + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 10); + CHECK_OPERAND("Offset", spv::Op::OpConstant, 11); + CHECK_OPERAND("Size", spv::Op::OpConstant, 12); + CHECK_CONST_UINT_OPERAND("Flags", 13); + if (has_optional_at(15)) + CHECK_OPERAND("Value", spv::Op::OpConstant, 14); + } + break; + } + case CommonDebugInfoDebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5); + auto* debug_inst = _.FindDef(inst->word(5)); + auto composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Child must be class or struct debug " + "type"; + } + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6); + debug_inst = _.FindDef(inst->word(6)); + composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Parent must be class or struct debug " + "type"; + } + CHECK_OPERAND("Offset", spv::Op::OpConstant, 7); + CHECK_OPERAND("Size", spv::Op::OpConstant, 8); + CHECK_CONST_UINT_OPERAND("Flags", 9); + break; + } + case CommonDebugInfoDebugFunction: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + CHECK_CONST_UINT_OPERAND("Scope Line", 13); + // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the + // OpFunction + if (vulkanDebugInfo) { + if (has_optional_at(15)) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 14); + } + } else { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", spv::Op::OpFunction, 14); + } + if (has_optional_at(16)) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 15); + } + } + break; + } + case CommonDebugInfoDebugFunctionDeclaration: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + break; + } + case CommonDebugInfoDebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line", 6); + CHECK_CONST_UINT_OPERAND("Column", 7); + if (auto error = + ValidateOperandDebugSource(_, inst, 5, 6, 7, ext_inst_type)) + return error; + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 8); + if (validate_parent != SPV_SUCCESS) return validate_parent; + if (has_optional_at(10)) CHECK_OPERAND("Name", spv::Op::OpString, 9); + break; + } + case CommonDebugInfoDebugScope: { + auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 5); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (has_optional_at(7)) { + CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6); + } + break; + } + case CommonDebugInfoDebugLocalVariable: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + // TODO: We need a spec discussion that we have to allow local + // variable types to have template parameter. + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_CONST_UINT_OPERAND("Flags", 11); + if (has_optional_at(13)) { + CHECK_CONST_UINT_OPERAND("ArgNumber", 12); + } + break; + } + case CommonDebugInfoDebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", CommonDebugInfoDebugLocalVariable, + 5); + auto* operand = _.FindDef(inst->word(6)); + if (operand->opcode() != spv::Op::OpVariable && + operand->opcode() != spv::Op::OpFunctionParameter) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpFunctionParameter"; + } + + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + break; + } + case CommonDebugInfoDebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation, + word_index); + } + break; + } + case CommonDebugInfoDebugTypeTemplate: { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeComposite || + dbg_inst == CommonDebugInfoDebugFunction; + }, + inst, 5)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Target must be DebugTypeComposite " + << "or DebugFunction"; + } + for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == + CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == + CommonDebugInfoDebugTypeTemplateTemplateParameter; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Parameters must be " + << "DebugTypeTemplateParameter or " + << "DebugTypeTemplateTemplateParameter"; + } + } + break; + } + case CommonDebugInfoDebugTypeTemplateParameter: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + auto validate_actual_type = + ValidateOperandDebugType(_, "Actual Type", inst, 6, false); + if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 7)) { + CHECK_OPERAND("Value", spv::Op::OpConstant, 7); + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8); + CHECK_CONST_UINT_OPERAND("Line", 9); + CHECK_CONST_UINT_OPERAND("Column", 10); + if (auto error = + ValidateOperandDebugSource(_, inst, 8, 9, 10, ext_inst_type)) + return error; + break; + } + case CommonDebugInfoDebugGlobalVariable: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + if (auto error = + ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type)) + return error; + + auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 10); + if (validate_scope != SPV_SUCCESS) return validate_scope; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + auto* operand = _.FindDef(inst->word(12)); + std::initializer_list<spv::Op> allowed_opcodes = { + spv::Op::OpVariable, + spv::Op::OpConstantTrue, + spv::Op::OpConstantFalse, + spv::Op::OpConstant, + spv::Op::OpConstantComposite, + spv::Op::OpConstantSampler, + spv::Op::OpConstantNull, + spv::Op::OpSpecConstantTrue, + spv::Op::OpSpecConstantFalse, + spv::Op::OpSpecConstant, + spv::Op::OpSpecConstantComposite, + spv::Op::OpSpecConstantOp}; + if (std::find(allowed_opcodes.begin(), allowed_opcodes.end(), + operand->opcode()) == allowed_opcodes.end()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) << ": " + << "expected operand Variable must be a result id of " + "OpVariable, OpConstant variant, OpSpecConstant variant " + "or DebugInfoNone"; + } + } + if (has_optional_at(15)) { + CHECK_DEBUG_OPERAND("Static Member Declaration", + CommonDebugInfoDebugTypeMember, 14); + } + break; + } + case CommonDebugInfoDebugInlinedAt: { + CHECK_CONST_UINT_OPERAND("Line", 5); + auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 6); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (has_optional_at(8)) { + CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7); + } + break; + } + case CommonDebugInfoDebugValue: { + CHECK_DEBUG_OPERAND("Local Variable", CommonDebugInfoDebugLocalVariable, + 5); + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + // TODO: The following code simply checks if it is a const int + // scalar or a DebugLocalVariable or DebugGlobalVariable, but we + // have to check it using the same validation for Indexes of + // OpAccessChain. + if (!IsConstWithIntScalarType(_, inst, word_index) && + !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << GetExtInstName(_, inst) + << ": expected operand Indexes is " + << "OpConstant, DebugGlobalVariable, or " + << "type is OpConstant with an integer scalar type"; + } + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case CommonDebugInfoDebugTypePtrToMember: + case CommonDebugInfoDebugTypeTemplateTemplateParameter: + case CommonDebugInfoDebugTypeTemplateParameterPack: + case CommonDebugInfoDebugLexicalBlockDiscriminator: + case CommonDebugInfoDebugInlinedVariable: + case CommonDebugInfoDebugMacroDef: + case CommonDebugInfoDebugMacroUndef: + case CommonDebugInfoDebugImportedEntity: + break; + case CommonDebugInfoInstructionsMax: + assert(0); + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInstNonsemanticClspvReflection( + ValidationState_t& _, const Instruction* inst) { + auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2)); + const std::string name = import_inst->GetOperandAs<std::string>(1); + const std::string reflection = "NonSemantic.ClspvReflection."; + char* end_ptr; + auto version_string = name.substr(reflection.size()); + if (version_string.empty()) { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "Missing NonSemantic.ClspvReflection import version"; + } + uint32_t version = + static_cast<uint32_t>(std::strtoul(version_string.c_str(), &end_ptr, 10)); + if (end_ptr && *end_ptr != '\0') { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "NonSemantic.ClspvReflection import does not encode the " + "version correctly"; + } + if (version == 0 || version > NonSemanticClspvReflectionRevision) { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "Unknown NonSemantic.ClspvReflection import version"; + } + + return ValidateClspvReflectionInstruction(_, inst, version); +} + +spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + + if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) { + return ValidateExtInstGlslStd450(_, inst); + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) { + return ValidateExtInstOpenClStd(_, inst); + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + return ValidateExtInstDebugInfo(_, inst); + } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { + return ValidateExtInstNonsemanticClspvReflection(_, inst); } return SPV_SUCCESS;
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp index 624b4e2..9892b2c 100644 --- a/source/val/validate_function.cpp +++ b/source/val/validate_function.cpp
@@ -14,8 +14,8 @@ #include <algorithm> -#include "source/enum_string_mapping.h" #include "source/opcode.h" +#include "source/table2.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" @@ -89,12 +89,15 @@ spv::Op::OpName, spv::Op::OpCooperativeMatrixPerElementOpNV, spv::Op::OpCooperativeMatrixReduceNV, - spv::Op::OpCooperativeMatrixLoadTensorNV}; + spv::Op::OpCooperativeMatrixLoadTensorNV, + spv::Op::OpConditionalEntryPointINTEL, + spv::Op::OpConstantFunctionPointerINTEL}; for (auto& pair : inst->uses()) { const auto* use = pair.first; if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) == acceptable.end() && - !use->IsNonSemantic() && !use->IsDebugInfo()) { + !use->IsNonSemantic() && !use->IsDebugInfo() && + !spvOpcodeIsDecoration(use->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, use) << "Invalid use of function result id " << _.getIdName(inst->id()) << "."; @@ -109,11 +112,6 @@ // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place. size_t param_index = 0; size_t inst_num = inst->LineNum() - 1; - if (inst_num == 0) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Function parameter cannot be the first instruction."; - } - auto func_inst = &_.ordered_instructions()[inst_num]; while (--inst_num) { func_inst = &_.ordered_instructions()[inst_num]; @@ -152,80 +150,6 @@ "type of the same index."; } - // Validate that PhysicalStorageBuffer have one of Restrict, Aliased, - // RestrictPointer, or AliasedPointer. - auto param_nonarray_type_id = param_type->id(); - while (_.GetIdOpcode(param_nonarray_type_id) == spv::Op::OpTypeArray) { - param_nonarray_type_id = - _.FindDef(param_nonarray_type_id)->GetOperandAs<uint32_t>(1u); - } - if (_.GetIdOpcode(param_nonarray_type_id) == spv::Op::OpTypePointer || - _.GetIdOpcode(param_nonarray_type_id) == - spv::Op::OpTypeUntypedPointerKHR) { - auto param_nonarray_type = _.FindDef(param_nonarray_type_id); - if (param_nonarray_type->GetOperandAs<spv::StorageClass>(1u) == - spv::StorageClass::PhysicalStorageBuffer) { - // check for Aliased or Restrict - const auto& decorations = _.id_decorations(inst->id()); - - bool foundAliased = std::any_of( - decorations.begin(), decorations.end(), [](const Decoration& d) { - return spv::Decoration::Aliased == d.dec_type(); - }); - - bool foundRestrict = std::any_of( - decorations.begin(), decorations.end(), [](const Decoration& d) { - return spv::Decoration::Restrict == d.dec_type(); - }); - - if (!foundAliased && !foundRestrict) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionParameter " << inst->id() - << ": expected Aliased or Restrict for PhysicalStorageBuffer " - "pointer."; - } - if (foundAliased && foundRestrict) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionParameter " << inst->id() - << ": can't specify both Aliased and Restrict for " - "PhysicalStorageBuffer pointer."; - } - } else if (param_nonarray_type->opcode() == spv::Op::OpTypePointer) { - const auto pointee_type_id = - param_nonarray_type->GetOperandAs<uint32_t>(2); - const auto pointee_type = _.FindDef(pointee_type_id); - if (spv::Op::OpTypePointer == pointee_type->opcode() && - pointee_type->GetOperandAs<spv::StorageClass>(1u) == - spv::StorageClass::PhysicalStorageBuffer) { - // check for AliasedPointer/RestrictPointer - const auto& decorations = _.id_decorations(inst->id()); - - bool foundAliased = std::any_of( - decorations.begin(), decorations.end(), [](const Decoration& d) { - return spv::Decoration::AliasedPointer == d.dec_type(); - }); - - bool foundRestrict = std::any_of( - decorations.begin(), decorations.end(), [](const Decoration& d) { - return spv::Decoration::RestrictPointer == d.dec_type(); - }); - - if (!foundAliased && !foundRestrict) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionParameter " << inst->id() - << ": expected AliasedPointer or RestrictPointer for " - "PhysicalStorageBuffer pointer."; - } - if (foundAliased && foundRestrict) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpFunctionParameter " << inst->id() - << ": can't specify both AliasedPointer and " - "RestrictPointer for PhysicalStorageBuffer pointer."; - } - } - } - } - return SPV_SUCCESS; } @@ -246,6 +170,34 @@ << "s type does not match Function <id> " << _.getIdName(return_type->id()) << "s return type."; } + if (!_.options()->relax_logical_pointer && + (_.addressing_model() == spv::AddressingModel::Logical || + _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64)) { + if (return_type->opcode() == spv::Op::OpTypePointer || + return_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + const auto sc = return_type->GetOperandAs<spv::StorageClass>(1); + if (sc != spv::StorageClass::PhysicalStorageBuffer) { + if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer) && + sc == spv::StorageClass::StorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, functions may only return a " + "storage buffer pointer if the " + "VariablePointersStorageBuffer capability is declared"; + } else if (!_.HasCapability(spv::Capability::VariablePointers) && + sc == spv::StorageClass::Workgroup) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, functions may only return a " + "workgroup pointer if the VariablePointers capability is " + "declared"; + } else if (sc != spv::StorageClass::StorageBuffer && + sc != spv::StorageClass::Workgroup) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, functions may not return a pointer " + "in this storage class"; + } + } + } + } const auto function_type_id = function->GetOperandAs<uint32_t>(3); const auto function_type = _.FindDef(function_type_id); @@ -292,50 +244,59 @@ } } - if (_.addressing_model() == spv::AddressingModel::Logical) { + if (_.addressing_model() == spv::AddressingModel::Logical || + _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) { if ((parameter_type->opcode() == spv::Op::OpTypePointer || parameter_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) && !_.options()->relax_logical_pointer) { spv::StorageClass sc = parameter_type->GetOperandAs<spv::StorageClass>(1u); - // Validate which storage classes can be pointer operands. - switch (sc) { - case spv::StorageClass::UniformConstant: - case spv::StorageClass::Function: - case spv::StorageClass::Private: - case spv::StorageClass::Workgroup: - case spv::StorageClass::AtomicCounter: - // These are always allowed. - break; - case spv::StorageClass::StorageBuffer: - if (!_.features().variable_pointers) { + if (sc != spv::StorageClass::PhysicalStorageBuffer) { + // Validate which storage classes can be pointer operands. + switch (sc) { + case spv::StorageClass::UniformConstant: + case spv::StorageClass::Function: + case spv::StorageClass::Private: + case spv::StorageClass::Workgroup: + case spv::StorageClass::AtomicCounter: + // SPV_EXT_tile_image + case spv::StorageClass::TileImageEXT: + // SPV_KHR_ray_tracing + case spv::StorageClass::ShaderRecordBufferKHR: + // These are always allowed. + break; + case spv::StorageClass::StorageBuffer: + if (!_.features().variable_pointers) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "StorageBuffer pointer operand " + << _.getIdName(argument_id) + << " requires a variable pointers capability"; + } + break; + default: return _.diag(SPV_ERROR_INVALID_ID, inst) - << "StorageBuffer pointer operand " - << _.getIdName(argument_id) - << " requires a variable pointers capability"; - } - break; - default: - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Invalid storage class for pointer operand " - << _.getIdName(argument_id); - } + << "Invalid storage class for pointer operand " + << _.getIdName(argument_id); + } - // Validate memory object declaration requirements. - if (argument->opcode() != spv::Op::OpVariable && - argument->opcode() != spv::Op::OpUntypedVariableKHR && - argument->opcode() != spv::Op::OpFunctionParameter) { - const bool ssbo_vptr = - _.HasCapability(spv::Capability::VariablePointersStorageBuffer) && - sc == spv::StorageClass::StorageBuffer; - const bool wg_vptr = - _.HasCapability(spv::Capability::VariablePointers) && - sc == spv::StorageClass::Workgroup; - const bool uc_ptr = sc == spv::StorageClass::UniformConstant; - if (!ssbo_vptr && !wg_vptr && !uc_ptr) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Pointer operand " << _.getIdName(argument_id) - << " must be a memory object declaration"; + // Validate memory object declaration requirements. + if (argument->opcode() != spv::Op::OpVariable && + argument->opcode() != spv::Op::OpUntypedVariableKHR && + argument->opcode() != spv::Op::OpFunctionParameter) { + const bool ssbo_vptr = + _.HasCapability( + spv::Capability::VariablePointersStorageBuffer) && + sc == spv::StorageClass::StorageBuffer; + const bool wg_vptr = + _.HasCapability(spv::Capability::VariablePointers) && + sc == spv::StorageClass::Workgroup; + const bool uc_ptr = sc == spv::StorageClass::UniformConstant; + if (!_.options()->before_hlsl_legalization && !ssbo_vptr && + !wg_vptr && !uc_ptr) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer operand " << _.getIdName(argument_id) + << " must be a memory object declaration"; + } } } } @@ -394,14 +355,14 @@ const auto param0_id = function_type->GetOperandAs<uint32_t>(2); const auto param1_id = function_type->GetOperandAs<uint32_t>(3); const auto param2_id = function_type->GetOperandAs<uint32_t>(4); - if (!_.IsIntScalarType(param0_id) || _.GetBitWidth(param0_id) != 32) { + if (!_.IsIntScalarType(param0_id, 32)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpCooperativeMatrixPerElementOpNV function type first parameter " "type <id> " << _.getIdName(param0_id) << " must be a 32-bit integer."; } - if (!_.IsIntScalarType(param1_id) || _.GetBitWidth(param1_id) != 32) { + if (!_.IsIntScalarType(param1_id, 32)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpCooperativeMatrixPerElementOpNV function type second " "parameter type <id> "
diff --git a/source/val/validate_graph.cpp b/source/val/validate_graph.cpp new file mode 100644 index 0000000..09d53ea --- /dev/null +++ b/source/val/validate_graph.cpp
@@ -0,0 +1,547 @@ +// Copyright (c) 2023-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of graph instructions. + +#include <deque> + +#include "source/opcode.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool IsTensorArray(ValidationState_t& _, uint32_t id) { + auto def = _.FindDef(id); + if (!def || (def->opcode() != spv::Op::OpTypeArray && + def->opcode() != spv::Op::OpTypeRuntimeArray)) { + return false; + } + auto tdef = _.FindDef(def->word(2)); + if (!tdef || tdef->opcode() != spv::Op::OpTypeTensorARM) { + return false; + } + return true; +} + +bool IsGraphInterfaceType(ValidationState_t& _, uint32_t id) { + return _.IsTensorType(id) || IsTensorArray(_, id); +} + +bool IsGraph(ValidationState_t& _, uint32_t id) { + auto def = _.FindDef(id); + if (!def || def->opcode() != spv::Op::OpGraphARM) { + return false; + } + return true; +} + +bool IsGraphType(ValidationState_t& _, uint32_t id) { + auto def = _.FindDef(id); + if (!def || def->opcode() != spv::Op::OpTypeGraphARM) { + return false; + } + return true; +} + +const uint32_t kGraphTypeIOStartWord = 3; + +uint32_t GraphTypeInstNumIO(const Instruction* inst) { + return static_cast<uint32_t>(inst->words().size()) - kGraphTypeIOStartWord; +} + +uint32_t GraphTypeInstNumInputs(const Instruction* inst) { + return inst->word(2); +} + +uint32_t GraphTypeInstNumOutputs(const Instruction* inst) { + return GraphTypeInstNumIO(inst) - GraphTypeInstNumInputs(inst); +} + +uint32_t GraphTypeInstGetOutputAtIndex(const Instruction* inst, + uint64_t index) { + return inst->word(kGraphTypeIOStartWord + GraphTypeInstNumInputs(inst) + + static_cast<uint32_t>(index)); +} + +uint32_t GraphTypeInstGetInputAtIndex(const Instruction* inst, uint64_t index) { + return inst->word(kGraphTypeIOStartWord + static_cast<uint32_t>(index)); +} + +spv_result_t ValidateGraphType(ValidationState_t& _, const Instruction* inst) { + // Check there are at least NumInputs types + uint32_t NumInputs = GraphTypeInstNumInputs(inst); + size_t NumIOTypes = GraphTypeInstNumIO(inst); + if (NumIOTypes < NumInputs) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << NumIOTypes << " I/O types were provided but the graph has " + << NumInputs << " inputs."; + } + + // Check there is at least one output + if (NumIOTypes == NumInputs) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "A graph type must have at least one output."; + } + + // Check all I/O types are graph interface type + for (unsigned i = kGraphTypeIOStartWord; i < inst->words().size(); i++) { + auto tid = inst->word(i); + if (!IsGraphInterfaceType(_, tid)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "I/O type " << _.getIdName(tid) + << " is not a Graph Interface Type."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGraphConstant(ValidationState_t& _, + const Instruction* inst) { + // Check Result Type + if (!_.IsTensorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " must have a Result Type that is a tensor type."; + } + + // Check the instruction is not preceded by another OpGraphConstantARM with + // the same ID + const uint32_t cst_id = inst->word(3); + size_t inst_num = inst->LineNum() - 1; + while (--inst_num) { + auto prev_inst = &_.ordered_instructions()[inst_num]; + if (prev_inst->opcode() == spv::Op::OpGraphConstantARM) { + const uint32_t prev_cst_id = prev_inst->word(3); + if (prev_cst_id == cst_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "No two OpGraphConstantARM instructions may have the same " + "GraphConstantID"; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGraphEntryPoint(ValidationState_t& _, + const Instruction* inst) { + // Graph must be an OpGraphARM + uint32_t graph = inst->GetOperandAs<uint32_t>(0); + auto graph_inst = _.FindDef(graph); + if (!IsGraph(_, graph)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " Graph must be a OpGraphARM but found " + << spvOpcodeString(graph_inst->opcode()) << "."; + } + + // Check number of Interface IDs matches number of I/Os of graph + auto graph_type_inst = _.FindDef(graph_inst->type_id()); + size_t graph_type_num_io = GraphTypeInstNumIO(graph_type_inst); + size_t graph_entry_point_num_interface_id = inst->operands().size() - 2; + if (graph_type_inst->opcode() != spv::Op::OpTypeGraphARM) { + // This is invalid but we want ValidateGraph to report a clear error + // so stop validating the graph entry point instruction + return SPV_SUCCESS; + } + if (graph_type_num_io != graph_entry_point_num_interface_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) << " Interface list contains " + << graph_entry_point_num_interface_id << " IDs but Graph's type " + << _.getIdName(graph_inst->type_id()) << " has " << graph_type_num_io + << " inputs and outputs."; + } + + // Check Interface IDs + for (uint32_t i = 2; i < inst->operands().size(); i++) { + uint32_t interface_id = inst->GetOperandAs<uint32_t>(i); + auto interface_inst = _.FindDef(interface_id); + + // Check interface IDs come from OpVariable + if ((interface_inst->opcode() != spv::Op::OpVariable) || + (interface_inst->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::UniformConstant)) { + return _.diag(SPV_ERROR_INVALID_DATA, interface_inst) + << spvOpcodeString(inst->opcode()) << " Interface ID " + << _.getIdName(interface_id) + << " must come from OpVariable with UniformConstant Storage " + "Class."; + } + + // Check type of interface variable matches type of the corresponding graph + // I/O + uint32_t corresponding_graph_io_type = + graph_type_inst->GetOperandAs<uint32_t>(i); + + uint32_t interface_ptr_type = interface_inst->type_id(); + auto interface_ptr_inst = _.FindDef(interface_ptr_type); + auto interface_pointee_type = interface_ptr_inst->GetOperandAs<uint32_t>(2); + if (interface_pointee_type != corresponding_graph_io_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) << " Interface ID type " + << _.getIdName(interface_pointee_type) + << " must match the type of the corresponding graph I/O " + << _.getIdName(corresponding_graph_io_type); + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGraph(ValidationState_t& _, const Instruction* inst) { + // Result Type must be an OpTypeGraphARM + if (!IsGraphType(_, inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " Result Type must be an OpTypeGraphARM."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGraphInput(ValidationState_t& _, const Instruction* inst) { + // Check type of InputIndex + auto input_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2)); + if (!input_index_inst || + !_.IsIntScalarType(input_index_inst->type_id(), 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " InputIndex must be a 32-bit integer."; + } + + bool has_element_index = inst->operands().size() > 3; + + // Check type of ElementIndex + if (has_element_index) { + auto element_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(3)); + if (!element_index_inst || + !_.IsIntScalarType(element_index_inst->type_id(), 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " ElementIndex must be a 32-bit integer."; + } + } + + // Find graph definition + size_t inst_num = inst->LineNum() - 1; + auto graph_inst = &_.ordered_instructions()[inst_num]; + while (--inst_num) { + graph_inst = &_.ordered_instructions()[inst_num]; + if (graph_inst->opcode() == spv::Op::OpGraphARM) { + break; + } + } + + // Can the InputIndex be evaluated? + // If not, there's nothing more we can validate here. + uint64_t input_index; + if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(2), &input_index)) { + return SPV_SUCCESS; + } + + auto const graph_type_inst = _.FindDef(graph_inst->type_id()); + size_t graph_type_num_inputs = graph_type_inst->GetOperandAs<uint32_t>(1); + + // Check InputIndex is in range + if (input_index >= graph_type_num_inputs) { + std::string disassembly = _.Disassemble(*inst); + return _.diag(SPV_ERROR_INVALID_DATA, nullptr) + << "Type " << _.getIdName(graph_type_inst->id()) << " for graph " + << _.getIdName(graph_inst->id()) << " has " << graph_type_num_inputs + << " inputs but found an OpGraphInputARM instruction with an " + "InputIndex that is " + << input_index << ": " << disassembly; + } + + uint32_t graph_type_input_type = + GraphTypeInstGetInputAtIndex(graph_type_inst, input_index); + + if (has_element_index) { + // Check ElementIndex is allowed + if (!IsTensorArray(_, graph_type_input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpGraphInputARM ElementIndex not allowed when the graph input " + "selected by " + << "InputIndex is not an OpTypeArray or OpTypeRuntimeArray"; + } + + // Check ElementIndex is in range if it can be evaluated and the input is a + // fixed-sized array whose Length can be evaluated + uint64_t element_index; + if (_.IsArrayType(graph_type_input_type) && + _.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(3), + &element_index)) { + uint64_t array_length; + auto graph_type_input_type_inst = _.FindDef(graph_type_input_type); + if (_.EvalConstantValUint64( + graph_type_input_type_inst->GetOperandAs<uint32_t>(2), + &array_length)) { + if (element_index >= array_length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpGraphInputARM ElementIndex out of range. The type of " + "the graph input being accessed " + << _.getIdName(graph_type_input_type) << " is an array of " + << array_length << " elements but " << "ElementIndex is " + << element_index; + } + } + } + } + + // Check result type matches with graph type + if (has_element_index) { + uint32_t expected_type = _.GetComponentType(graph_type_input_type); + if (inst->type_id() != expected_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type " << _.getIdName(inst->type_id()) + << " of graph input instruction " << _.getIdName(inst->id()) + << " does not match the component type " + << _.getIdName(expected_type) << " of input " << input_index + << " in the graph type."; + } + } else { + if (inst->type_id() != graph_type_input_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type " << _.getIdName(inst->type_id()) + << " of graph input instruction " << _.getIdName(inst->id()) + << " does not match the type " + << _.getIdName(graph_type_input_type) << " of input " + << input_index << " in the graph type."; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGraphSetOutput(ValidationState_t& _, + const Instruction* inst) { + // Check type of OutputIndex + auto output_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(1)); + if (!output_index_inst || + !_.IsIntScalarType(output_index_inst->type_id(), 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " OutputIndex must be a 32-bit integer."; + } + + bool has_element_index = inst->operands().size() > 2; + + // Check type of ElementIndex + if (has_element_index) { + auto element_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2)); + if (!element_index_inst || + !_.IsIntScalarType(element_index_inst->type_id(), 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) + << " ElementIndex must be a 32-bit integer."; + } + } + + // Find graph definition + size_t inst_num = inst->LineNum() - 1; + auto graph_inst = &_.ordered_instructions()[inst_num]; + while (--inst_num) { + graph_inst = &_.ordered_instructions()[inst_num]; + if (graph_inst->opcode() == spv::Op::OpGraphARM) { + break; + } + } + + // Can the OutputIndex be evaluated? + // If not, there's nothing more we can validate here. + uint64_t output_index; + if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(1), + &output_index)) { + return SPV_SUCCESS; + } + + // Check that the OutputIndex is valid with respect to the graph type + auto graph_type_inst = _.FindDef(graph_inst->type_id()); + size_t graph_type_num_outputs = GraphTypeInstNumOutputs(graph_type_inst); + + if (output_index >= graph_type_num_outputs) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(inst->opcode()) << " setting OutputIndex " + << output_index << " but graph only has " << graph_type_num_outputs + << " outputs."; + } + + uint32_t graph_type_output_type = + GraphTypeInstGetOutputAtIndex(graph_type_inst, output_index); + + if (has_element_index) { + // Check ElementIndex is allowed + if (!IsTensorArray(_, graph_type_output_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpGraphSetOutputARM ElementIndex not allowed when the graph " + "output selected by " + << "OutputIndex is not an OpTypeArray or OpTypeRuntimeArray"; + } + + // Check ElementIndex is in range if it can be evaluated and the output is a + // fixed-sized array whose Length can be evaluated + uint64_t element_index; + if (_.IsArrayType(graph_type_output_type) && + _.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(2), + &element_index)) { + uint64_t array_length; + auto graph_type_output_type_inst = _.FindDef(graph_type_output_type); + if (_.EvalConstantValUint64( + graph_type_output_type_inst->GetOperandAs<uint32_t>(2), + &array_length)) { + if (element_index >= array_length) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpGraphSetOutputARM ElementIndex out of range. The type " + "of the graph output being accessed " + << _.getIdName(graph_type_output_type) << " is an array of " + << array_length << " elements but " << "ElementIndex is " + << element_index; + } + } + } + } + + // Check Value's type matches with graph type + uint32_t value = inst->GetOperandAs<uint32_t>(0); + uint32_t value_type = _.FindDef(value)->type_id(); + if (has_element_index) { + uint32_t expected_type = _.GetComponentType(graph_type_output_type); + if (value_type != expected_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type " << _.getIdName(value_type) + << " of Value provided to the graph output instruction " + << _.getIdName(value) << " does not match the component type " + << _.getIdName(expected_type) << " of output " << output_index + << " in the graph type."; + } + } else { + if (value_type != graph_type_output_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type " << _.getIdName(value_type) + << " of Value provided to the graph output instruction " + << _.getIdName(value) << " does not match the type " + << _.getIdName(graph_type_output_type) << " of output " + << output_index << " in the graph type."; + } + } + return SPV_SUCCESS; +} + +bool InputOutputInstructionsHaveDuplicateIndices( + ValidationState_t& _, std::deque<const Instruction*>& inout_insts, + const Instruction** first_dup) { + std::set<std::pair<uint64_t, uint64_t>> inout_element_indices; + for (auto const inst : inout_insts) { + const bool is_input = inst->opcode() == spv::Op::OpGraphInputARM; + bool has_element_index = inst->operands().size() > (is_input ? 3 : 2); + uint64_t inout_index; + if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(is_input ? 2 : 1), + &inout_index)) { + continue; + } + uint64_t element_index = -1; // -1 means no ElementIndex + if (has_element_index) { + if (!_.EvalConstantValUint64( + inst->GetOperandAs<uint32_t>(is_input ? 3 : 2), &element_index)) { + continue; + } + } + auto inout_element_pair = std::make_pair(inout_index, element_index); + auto inout_noelement_pair = std::make_pair(inout_index, -1); + if (inout_element_indices.count(inout_element_pair) || + inout_element_indices.count(inout_noelement_pair)) { + *first_dup = inst; + return true; + } + inout_element_indices.insert(inout_element_pair); + } + return false; +} + +spv_result_t ValidateGraphEnd(ValidationState_t& _, const Instruction* inst) { + size_t end_inst_num = inst->LineNum() - 1; + + // Gather OpGraphInputARM and OpGraphSetOutputARM instructions + std::deque<const Instruction*> graph_inputs, graph_outputs; + size_t in_inst_num = end_inst_num; + auto graph_inst = &_.ordered_instructions()[in_inst_num]; + while (--in_inst_num) { + graph_inst = &_.ordered_instructions()[in_inst_num]; + if (graph_inst->opcode() == spv::Op::OpGraphInputARM) { + graph_inputs.push_front(graph_inst); + continue; + } + if (graph_inst->opcode() == spv::Op::OpGraphSetOutputARM) { + graph_outputs.push_front(graph_inst); + continue; + } + if (graph_inst->opcode() == spv::Op::OpGraphARM) { + break; + } + } + + const Instruction* first_dup; + + // Check that there are no duplicate InputIndex and ElementIndex values + if (InputOutputInstructionsHaveDuplicateIndices(_, graph_inputs, + &first_dup)) { + return _.diag(SPV_ERROR_INVALID_DATA, first_dup) + << "Two OpGraphInputARM instructions with the same InputIndex " + "must not be part of the same " + << "graph definition unless ElementIndex is present in both with " + "different values."; + } + + // Check that there are no duplicate OutputIndex and ElementIndex values + if (InputOutputInstructionsHaveDuplicateIndices(_, graph_outputs, + &first_dup)) { + return _.diag(SPV_ERROR_INVALID_DATA, first_dup) + << "Two OpGraphSetOutputARM instructions with the same " + "OutputIndex must not be part of the same " + << "graph definition unless ElementIndex is present in both with " + "different values."; + } + + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of graph instructions. +spv_result_t GraphPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpTypeGraphARM: + return ValidateGraphType(_, inst); + case spv::Op::OpGraphConstantARM: + return ValidateGraphConstant(_, inst); + case spv::Op::OpGraphEntryPointARM: + return ValidateGraphEntryPoint(_, inst); + case spv::Op::OpGraphARM: + return ValidateGraph(_, inst); + case spv::Op::OpGraphInputARM: + return ValidateGraphInput(_, inst); + case spv::Op::OpGraphSetOutputARM: + return ValidateGraphSetOutput(_, inst); + case spv::Op::OpGraphEndARM: + return ValidateGraphEnd(_, inst); + default: + break; + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_group.cpp b/source/val/validate_group.cpp new file mode 100644 index 0000000..cccb0ab --- /dev/null +++ b/source/val/validate_group.cpp
@@ -0,0 +1,229 @@ +// Copyright (c) 2026 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstdint> + +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateGroupAnyAll(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar type"; + } + + if (!_.IsBoolScalarType(_.GetOperandTypeId(inst, 3))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Predicate must be a boolean scalar type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupBroadcast(ValidationState_t& _, + const Instruction* inst) { + const uint32_t type_id = inst->type_id(); + if (!_.IsFloatScalarOrVectorType(type_id) && + !_.IsIntScalarOrVectorType(type_id) && + !_.IsBoolScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a scalar or vector of integer, floating-point, " + "or boolean type"; + } + + const uint32_t value_type_id = _.GetOperandTypeId(inst, 3); + if (value_type_id != type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of Value must match the Result type"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupFloat(ValidationState_t& _, const Instruction* inst) { + const uint32_t type_id = inst->type_id(); + if (!_.IsFloatScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a scalar or vector of float type"; + } + + const uint32_t x_type_id = _.GetOperandTypeId(inst, 4); + if (x_type_id != type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of X must match the Result type"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupInt(ValidationState_t& _, const Instruction* inst) { + const uint32_t type_id = inst->type_id(); + if (!_.IsIntScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a scalar or vector of integer type"; + } + + const uint32_t x_type_id = _.GetOperandTypeId(inst, 4); + if (x_type_id != type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of X must match the Result type"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupAsyncCopy(ValidationState_t& _, + const Instruction* inst) { + if (_.FindDef(inst->type_id())->opcode() != spv::Op::OpTypeEvent) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The result type must be OpTypeEvent."; + } + + const uint32_t destination = _.GetOperandTypeId(inst, 3); + const Instruction* destination_pointer = _.FindDef(destination); + if (destination_pointer->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Destination to be a pointer."; + } + const auto destination_sc = + destination_pointer->GetOperandAs<spv::StorageClass>(1); + if (destination_sc != spv::StorageClass::Workgroup && + destination_sc != spv::StorageClass::CrossWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Destination to be a pointer with storage class " + "Workgroup or CrossWorkgroup."; + } + const uint32_t destination_type = + destination_pointer->GetOperandAs<uint32_t>(2); + if (!_.IsIntScalarOrVectorType(destination_type) && + !_.IsFloatScalarOrVectorType(destination_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Destination to be a pointer to scalar or vector of " + "floating-point type or integer type."; + } + + const uint32_t source = _.GetOperandTypeId(inst, 4); + const Instruction* source_pointer = _.FindDef(source); + const auto source_sc = source_pointer->GetOperandAs<spv::StorageClass>(1); + const uint32_t source_type = source_pointer->GetOperandAs<uint32_t>(2); + if (destination_type != source_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Destination and Source to be the same type."; + } + + if (destination_sc == spv::StorageClass::Workgroup && + source_sc != spv::StorageClass::CrossWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If Destination storage class is Workgroup, then the Source " + "storage class must be CrossWorkgroup."; + } else if (destination_sc == spv::StorageClass::CrossWorkgroup && + source_sc != spv::StorageClass::Workgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If Destination storage class is CrossWorkgroup, then the Source " + "storage class must be Workgroup."; + } + + const bool is_physical_64 = + _.addressing_model() == spv::AddressingModel::Physical64; + const uint32_t bit_width = is_physical_64 ? 64 : 32; + + const uint32_t num_elements_type = + _.GetTypeId(inst->GetOperandAs<uint32_t>(5)); + if (!_.IsIntScalarType(num_elements_type, bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NumElements must be a " << bit_width + << "-bit int scalar when Addressing Model is " + << (is_physical_64 ? "Physical64" : "Physical32"); + } + + const uint32_t stride_type = _.GetTypeId(inst->GetOperandAs<uint32_t>(6)); + if (!_.IsIntScalarType(stride_type, bit_width)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Stride must be a " << bit_width + << "-bit int scalar when Addressing Model is " + << (is_physical_64 ? "Physical64" : "Physical32"); + } + + const uint32_t event = _.GetOperandTypeId(inst, 7); + const Instruction* event_type = _.FindDef(event); + if (event_type->opcode() != spv::Op::OpTypeEvent) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Event to be type OpTypeEvent."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupWaitEvents(ValidationState_t& _, + const Instruction* inst) { + const uint32_t num_events_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(num_events_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Num Events to be a 32-bit int scalar."; + } + + const uint32_t events_id = _.GetOperandTypeId(inst, 2); + const Instruction* var_pointer = _.FindDef(events_id); + if (var_pointer->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Events List to be a pointer."; + } + const Instruction* event_list_type = + _.FindDef(var_pointer->GetOperandAs<uint32_t>(2)); + if (event_list_type->opcode() != spv::Op::OpTypeEvent) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Events List to be a pointer to OpTypeEvent."; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t GroupPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + switch (opcode) { + case spv::Op::OpGroupAny: + case spv::Op::OpGroupAll: + return ValidateGroupAnyAll(_, inst); + case spv::Op::OpGroupBroadcast: + return ValidateGroupBroadcast(_, inst); + case spv::Op::OpGroupFAdd: + case spv::Op::OpGroupFMax: + case spv::Op::OpGroupFMin: + return ValidateGroupFloat(_, inst); + case spv::Op::OpGroupIAdd: + case spv::Op::OpGroupUMin: + case spv::Op::OpGroupSMin: + case spv::Op::OpGroupUMax: + case spv::Op::OpGroupSMax: + return ValidateGroupInt(_, inst); + case spv::Op::OpGroupAsyncCopy: + return ValidateGroupAsyncCopy(_, inst); + case spv::Op::OpGroupWaitEvents: + return ValidateGroupWaitEvents(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp index 00be150..312ecf7 100644 --- a/source/val/validate_id.cpp +++ b/source/val/validate_id.cpp
@@ -115,6 +115,64 @@ return SPV_SUCCESS; } +bool InstructionCanHaveTypeOperand(const Instruction* inst) { + static std::unordered_set<spv::Op> instruction_allow_set{ + spv::Op::OpSizeOf, + spv::Op::OpCooperativeMatrixLengthNV, + spv::Op::OpCooperativeMatrixLengthKHR, + spv::Op::OpUntypedArrayLengthKHR, + spv::Op::OpFunction, + spv::Op::OpAsmINTEL, + spv::Op::OpConstantSizeOfEXT, + spv::Op::OpBufferPointerEXT, + spv::Op::OpUntypedImageTexelPointerEXT, + spv::Op::OpAbortKHR, + }; + const auto opcode = inst->opcode(); + bool type_instruction = spvOpcodeGeneratesType(opcode); + bool debug_instruction = spvOpcodeIsDebug(opcode) || inst->IsDebugInfo(); + bool coop_matrix_spec_constant_op_length = + (opcode == spv::Op::OpSpecConstantOp) && + (spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthNV || + spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthKHR); + return type_instruction || debug_instruction || inst->IsNonSemantic() || + spvOpcodeIsDecoration(opcode) || instruction_allow_set.count(opcode) || + spvOpcodeGeneratesUntypedPointer(opcode) || + coop_matrix_spec_constant_op_length; +} + +bool InstructionRequiresTypeOperand(const Instruction* inst) { + static std::unordered_set<spv::Op> instruction_deny_set{ + spv::Op::OpExtInst, + spv::Op::OpExtInstWithForwardRefsKHR, + spv::Op::OpExtInstImport, + spv::Op::OpSelectionMerge, + spv::Op::OpLoopMerge, + spv::Op::OpFunction, + spv::Op::OpSizeOf, + spv::Op::OpCooperativeMatrixLengthNV, + spv::Op::OpCooperativeMatrixLengthKHR, + spv::Op::OpPhi, + spv::Op::OpUntypedArrayLengthKHR, + spv::Op::OpAsmINTEL, + spv::Op::OpAliasScopeDeclINTEL, + spv::Op::OpAliasScopeListDeclINTEL, + spv::Op::OpAbortKHR, + }; + const auto opcode = inst->opcode(); + bool debug_instruction = spvOpcodeIsDebug(opcode) || inst->IsDebugInfo(); + bool coop_matrix_spec_constant_op_length = + opcode == spv::Op::OpSpecConstantOp && + (spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthNV || + spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthKHR); + + return !debug_instruction && !inst->IsNonSemantic() && + !spvOpcodeIsDecoration(opcode) && !spvOpcodeIsBranch(opcode) && + !instruction_deny_set.count(opcode) && + !spvOpcodeGeneratesUntypedPointer(opcode) && + !coop_matrix_spec_constant_op_length; +} + // Performs SSA validation on the IDs of an instruction. The // can_have_forward_declared_ids functor should return true if the // instruction operand's ID can be forward referenced. @@ -158,44 +216,15 @@ case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: case SPV_OPERAND_TYPE_SCOPE_ID: if (const auto def = _.FindDef(operand_word)) { - const auto opcode = inst->opcode(); if (spvOpcodeGeneratesType(def->opcode()) && - !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && - !inst->IsDebugInfo() && !inst->IsNonSemantic() && - !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction && - opcode != spv::Op::OpSizeOf && - opcode != spv::Op::OpCooperativeMatrixLengthNV && - opcode != spv::Op::OpCooperativeMatrixLengthKHR && - !spvOpcodeGeneratesUntypedPointer(opcode) && - opcode != spv::Op::OpUntypedArrayLengthKHR && - !(opcode == spv::Op::OpSpecConstantOp && - (spv::Op(inst->word(3)) == - spv::Op::OpCooperativeMatrixLengthNV || - spv::Op(inst->word(3)) == - spv::Op::OpCooperativeMatrixLengthKHR))) { + !InstructionCanHaveTypeOperand(inst)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Operand " << _.getIdName(operand_word) << " cannot be a type"; - } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && - !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() && - !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && - !spvOpcodeIsBranch(opcode) && opcode != spv::Op::OpPhi && - opcode != spv::Op::OpExtInst && - opcode != spv::Op::OpExtInstWithForwardRefsKHR && - opcode != spv::Op::OpExtInstImport && - opcode != spv::Op::OpSelectionMerge && - opcode != spv::Op::OpLoopMerge && - opcode != spv::Op::OpFunction && - opcode != spv::Op::OpSizeOf && - opcode != spv::Op::OpCooperativeMatrixLengthNV && - opcode != spv::Op::OpCooperativeMatrixLengthKHR && - !spvOpcodeGeneratesUntypedPointer(opcode) && - opcode != spv::Op::OpUntypedArrayLengthKHR && - !(opcode == spv::Op::OpSpecConstantOp && - (spv::Op(inst->word(3)) == - spv::Op::OpCooperativeMatrixLengthNV || - spv::Op(inst->word(3)) == - spv::Op::OpCooperativeMatrixLengthKHR))) { + } else if (def->type_id() == 0 && + !spvOpcodeGeneratesType(def->opcode()) && + InstructionRequiresTypeOperand(inst) && + InstructionRequiresTypeOperand(def)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Operand " << _.getIdName(operand_word) << " requires a type";
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 04100dd..68d8056 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp
@@ -178,6 +178,118 @@ return false; } +// Signed or Unsigned Integer Format +bool IsIntImageFormat(spv::ImageFormat format) { + switch (format) { + case spv::ImageFormat::Rgba32i: + case spv::ImageFormat::Rgba16i: + case spv::ImageFormat::Rgba8i: + case spv::ImageFormat::R32i: + case spv::ImageFormat::Rg32i: + case spv::ImageFormat::Rg16i: + case spv::ImageFormat::Rg8i: + case spv::ImageFormat::R16i: + case spv::ImageFormat::R8i: + case spv::ImageFormat::Rgba32ui: + case spv::ImageFormat::Rgba16ui: + case spv::ImageFormat::Rgba8ui: + case spv::ImageFormat::R32ui: + case spv::ImageFormat::Rgb10a2ui: + case spv::ImageFormat::Rg32ui: + case spv::ImageFormat::Rg16ui: + case spv::ImageFormat::Rg8ui: + case spv::ImageFormat::R16ui: + case spv::ImageFormat::R8ui: + case spv::ImageFormat::R64ui: + case spv::ImageFormat::R64i: + return true; + default: + break; + } + return false; +} + +bool IsInt64ImageFormat(spv::ImageFormat format) { + switch (format) { + case spv::ImageFormat::R64ui: + case spv::ImageFormat::R64i: + return true; + default: + break; + } + return false; +} + +bool IsSignedIntImageFormat(spv::ImageFormat format) { + switch (format) { + case spv::ImageFormat::Rgba32i: + case spv::ImageFormat::Rgba16i: + case spv::ImageFormat::Rgba8i: + case spv::ImageFormat::R32i: + case spv::ImageFormat::Rg32i: + case spv::ImageFormat::Rg16i: + case spv::ImageFormat::Rg8i: + case spv::ImageFormat::R16i: + case spv::ImageFormat::R8i: + case spv::ImageFormat::R64i: + return true; + default: + break; + } + return false; +} + +bool IsFloatImageFormat(spv::ImageFormat format) { + switch (format) { + case spv::ImageFormat::Rgba32f: + case spv::ImageFormat::Rgba16f: + case spv::ImageFormat::R32f: + case spv::ImageFormat::Rgba8: + case spv::ImageFormat::Rgba8Snorm: + case spv::ImageFormat::Rg32f: + case spv::ImageFormat::Rg16f: + case spv::ImageFormat::R11fG11fB10f: + case spv::ImageFormat::R16f: + case spv::ImageFormat::Rgba16: + case spv::ImageFormat::Rgb10A2: + case spv::ImageFormat::Rg16: + case spv::ImageFormat::Rg8: + case spv::ImageFormat::R16: + case spv::ImageFormat::R8: + case spv::ImageFormat::Rgba16Snorm: + case spv::ImageFormat::Rg16Snorm: + case spv::ImageFormat::Rg8Snorm: + case spv::ImageFormat::R16Snorm: + case spv::ImageFormat::R8Snorm: + return true; + default: + break; + } + return false; +} + +bool IsImageSparse(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageSparseTexelsResident: + case spv::Op::OpImageSparseRead: + return true; + default: + break; + } + return false; +} + // Returns true if the opcode is a Image instruction which applies // homogenous projection to the coordinates. bool IsProj(spv::Op opcode) { @@ -237,6 +349,23 @@ return 3; } + if (opcode == spv::Op::OpImageQueryLod) { + return GetPlaneCoordSize(info); + } + + if (opcode == spv::Op::OpImageTexelPointer) { + if (info.arrayed == 0) { + return GetPlaneCoordSize(info); + } else if (info.dim == spv::Dim::Dim1D) { + return 2; + } else if (info.dim == spv::Dim::Cube || info.dim == spv::Dim::Dim2D) { + return 3; + } else { + assert(false); + return 0; // caught elsewhere + } + } + return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0); } @@ -287,6 +416,37 @@ "multi-sampled image"; } + // The following OpTypeImage checks are done here as they depend of if the + // SignExtend and ZeroExtend are used to override the signedness + const bool is_sign_extend = + mask & uint32_t(spv::ImageOperandsMask::SignExtend); + const bool is_zero_extend = + mask & uint32_t(spv::ImageOperandsMask::ZeroExtend); + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.format != spv::ImageFormat::Unknown && + _.IsIntScalarType(info.sampled_type)) { + const bool is_format_signed = IsSignedIntImageFormat(info.format); + const bool is_sampled_type_signed = + _.IsSignedIntScalarType(info.sampled_type); + // (vkspec.html#spirvenv-image-signedness) has order signedness is set by + const bool effective_sampled_type_signed = + is_sign_extend ? true + : (is_zero_extend ? false : is_sampled_type_signed); + if (is_format_signed != effective_sampled_type_signed) { + std::string err_info = ""; + if (is_sign_extend) { + err_info = " (SignExtend makes the access as signed)"; + } else if (is_zero_extend) { + err_info = " (ZeroExtend makes the access as unsigned)"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4965) << "Image Format signedness (" + << (is_format_signed ? "signed" : "unsigned") + << ") does not match Sample Type operand" << err_info << "."; + } + } + } + // After this point, only set bits in the image operands mask can cause // the module to be invalid. if (mask == 0) return SPV_SUCCESS; @@ -314,10 +474,10 @@ << "Image Operand Bias can only be used with ImplicitLod opcodes"; } - const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); - if (!_.IsFloatScalarType(type_id)) { + const uint32_t bias_type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarType(bias_type_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand Bias to be float scalar"; + << "Expected Image Operand Bias to be a 32-bit float scalar"; } if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && @@ -327,7 +487,10 @@ "or Cube"; } - // Multisampled is already checked. + // - |Sample| operand is required to have MS != 0 + // - |Sample| is only allowed with [Fetch, Write, or Read] + // - |Bias| can only be used with |ImplicitLod| opcodes + // Multisampled is already checked in all cases } if (mask & uint32_t(spv::ImageOperandsMask::Lod)) { @@ -345,17 +508,19 @@ "time"; } - const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + const uint32_t lod_type_id = _.GetTypeId(inst->word(word_index++)); if (is_explicit_lod || is_valid_gather_lod_bias_amd) { - if (!_.IsFloatScalarType(type_id)) { + if (!_.IsFloatScalarType(lod_type_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand Lod to be float scalar when used " + << "Expected Image Operand Lod to be a 32-bit float scalar when " + "used " << "with ExplicitLod"; } } else { - if (!_.IsIntScalarType(type_id)) { + if (!_.IsIntScalarType(lod_type_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand Lod to be int scalar when used with " + << "Expected Image Operand Lod to be a 32-bit int scalar when " + "used with " << "OpImageFetch"; } } @@ -367,7 +532,10 @@ "or Cube"; } - // Multisampled is already checked. + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Lod requires 'MS' parameter to be 0"; + } } if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { @@ -379,9 +547,12 @@ const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++)); const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++)); if (!_.IsFloatScalarOrVectorType(dx_type_id) || - !_.IsFloatScalarOrVectorType(dy_type_id)) { + _.GetBitWidth(dx_type_id) != 32 || + !_.IsFloatScalarOrVectorType(dy_type_id) || + _.GetBitWidth(dy_type_id) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected both Image Operand Grad ids to be float scalars or " + << "Expected both Image Operand Grad ids to be 32-bit float " + "scalars or " << "vectors"; } @@ -400,7 +571,10 @@ << " components, but given " << dy_size; } - // Multisampled is already checked. + // - |Sample| operand is required to have MS != 0 + // - |Sample| is only allowed with [Fetch, Write, or Read] + // - |Grad| can only be used with |ExplicitLod| opcodes + // Multisampled is already checked in all cases } if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { @@ -410,21 +584,23 @@ "'Dim'"; } - const uint32_t id = inst->word(word_index++); - const uint32_t type_id = _.GetTypeId(id); - if (!_.IsIntScalarOrVectorType(type_id)) { + const uint32_t offset_id = inst->word(word_index++); + const uint32_t offset_type_id = _.GetTypeId(offset_id); + if (!_.IsIntScalarOrVectorType(offset_type_id) || + _.GetBitWidth(offset_type_id) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand ConstOffset to be int scalar or " + << "Expected Image Operand ConstOffset to be a 32-bit int scalar " + "or " << "vector"; } - if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + if (!spvOpcodeIsConstant(_.GetIdOpcode(offset_id))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Image Operand ConstOffset to be a const object"; } const uint32_t plane_size = GetPlaneCoordSize(info); - const uint32_t offset_size = _.GetDimension(type_id); + const uint32_t offset_size = _.GetDimension(offset_type_id); if (plane_size != offset_size) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Image Operand ConstOffset to have " << plane_size @@ -438,16 +614,17 @@ << "Image Operand Offset cannot be used with Cube Image 'Dim'"; } - const uint32_t id = inst->word(word_index++); - const uint32_t type_id = _.GetTypeId(id); - if (!_.IsIntScalarOrVectorType(type_id)) { + const uint32_t offset_id = inst->word(word_index++); + const uint32_t offset_type_id = _.GetTypeId(offset_id); + if (!_.IsIntScalarOrVectorType(offset_type_id) || + _.GetBitWidth(offset_type_id) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand Offset to be int scalar or " + << "Expected Image Operand Offset to be a 32-bit int scalar or " << "vector"; } const uint32_t plane_size = GetPlaneCoordSize(info); - const uint32_t offset_size = _.GetDimension(type_id); + const uint32_t offset_size = _.GetDimension(offset_type_id); if (plane_size != offset_size) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Image Operand Offset to have " << plane_size @@ -455,15 +632,18 @@ } if (!_.options()->before_hlsl_legalization && - spvIsVulkanEnv(_.context()->target_env)) { + spvIsVulkanEnv(_.context()->target_env) && + !_.options()->allow_offset_texture_operand) { if (opcode != spv::Op::OpImageGather && opcode != spv::Op::OpImageDrefGather && opcode != spv::Op::OpImageSparseGather && opcode != spv::Op::OpImageSparseDrefGather) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4663) + << _.VkErrorID(10213) << "Image Operand Offset can only be used with " - "OpImage*Gather operations"; + "OpImage*Gather operations." + << _.MissingFeature("maintenance8 feature", + "--allow-offset-texture-operand", false); } } } @@ -484,9 +664,9 @@ "'Dim'"; } - const uint32_t id = inst->word(word_index++); - const uint32_t type_id = _.GetTypeId(id); - const Instruction* type_inst = _.FindDef(type_id); + const uint32_t offset_id = inst->word(word_index++); + const uint32_t offset_type_id = _.GetTypeId(offset_id); + const Instruction* type_inst = _.FindDef(offset_type_id); assert(type_inst); if (type_inst->opcode() != spv::Op::OpTypeArray) { @@ -506,13 +686,14 @@ const uint32_t component_type = type_inst->word(2); if (!_.IsIntVectorType(component_type) || - _.GetDimension(component_type) != 2) { + _.GetDimension(component_type) != 2 || + _.GetBitWidth(component_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand ConstOffsets array components to be " - "int vectors of size 2"; + << "Expected Image Operand ConstOffsets array components to be a " + "32-bit int vectors of size 2"; } - if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + if (!spvOpcodeIsConstant(_.GetIdOpcode(offset_id))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Image Operand ConstOffsets to be a const object"; } @@ -534,10 +715,10 @@ << "Image Operand Sample requires non-zero 'MS' parameter"; } - const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); - if (!_.IsIntScalarType(type_id)) { + const uint32_t sample_type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsIntScalarType(sample_type_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand Sample to be int scalar"; + << "Expected Image Operand Sample to be a 32-bit int scalar"; } } @@ -548,10 +729,10 @@ << "opcodes or together with Image Operand Grad"; } - const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); - if (!_.IsFloatScalarType(type_id)) { + const uint32_t minlod_type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarType(minlod_type_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand MinLod to be float scalar"; + << "Expected Image Operand MinLod to be a 32-bit float scalar"; } if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && @@ -610,30 +791,31 @@ if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error; } - if (mask & uint32_t(spv::ImageOperandsMask::SignExtend)) { - // Checked elsewhere: SPIR-V 1.4 version or later. - - // "The texel value is converted to the target value via sign extension. - // Only valid when the texel type is a scalar or vector of integer type." - // - // We don't have enough information to know what the texel type is. + // Checked elsewhere: SPIR-V 1.4 version or later. + if (is_sign_extend || is_zero_extend) { + // We don't have enough information to know what the |texel value type| is. // In OpenCL, knowledge is deferred until runtime: the image SampledType is // void, and the Format is Unknown. // In Vulkan, the texel type is only known in all cases by the pipeline // setup. - } - if (mask & uint32_t(spv::ImageOperandsMask::ZeroExtend)) { - // Checked elsewhere: SPIR-V 1.4 version or later. - - // "The texel value is converted to the target value via zero extension. - // Only valid when the texel type is a scalar or vector of integer type." - // - // We don't have enough information to know what the texel type is. - // In OpenCL, knowledge is deferred until runtime: the image SampledType is - // void, and the Format is Unknown. - // In Vulkan, the texel type is only known in all cases by the pipeline - // setup. + if (opcode == spv::Op::OpImageWrite) { + // OpImageWrite has no result type. + // TODO - Add Validation + } else if (IsImageSparse(opcode)) { + // Sparse image read/sample return a struct. + // TODO - Add Validation + } else { + if (is_sign_extend && !_.IsIntScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using SignExtend, but result type is not a scalar or vector " + "integer type."; + } else if (is_zero_extend && + !_.IsUnsignedIntScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using ZeroExtend, but result type is a signed integer type."; + } + } } if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) { @@ -779,8 +961,7 @@ << "Corrupt image type definition"; } - if (_.IsIntScalarType(info.sampled_type) && - (64 == _.GetBitWidth(info.sampled_type)) && + if (_.IsIntScalarType(info.sampled_type, 64) && !_.HasCapability(spv::Capability::Int64ImageEXT)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Capability Int64ImageEXT is required when using Sampled Type of " @@ -789,12 +970,9 @@ const auto target_env = _.context()->target_env; if (spvIsVulkanEnv(target_env)) { - if ((!_.IsFloatScalarType(info.sampled_type) && - !_.IsIntScalarType(info.sampled_type)) || - ((32 != _.GetBitWidth(info.sampled_type)) && - (64 != _.GetBitWidth(info.sampled_type))) || - ((64 == _.GetBitWidth(info.sampled_type)) && - _.IsFloatScalarType(info.sampled_type))) { + if (!_.IsFloatScalarType(info.sampled_type, 32) && + !_.IsIntScalarType(info.sampled_type, 32) && + !_.IsIntScalarType(info.sampled_type, 64)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4656) << "Expected Sampled Type to be a 32-bit int, 64-bit int or " @@ -924,6 +1102,33 @@ << _.VkErrorID(9638) << "Dim must not be Rect in the Vulkan environment"; } + + // Can't check signedness here due to image operands (SignExtend or + // ZeroExtend) ability to overridesampled type + if (info.format != spv::ImageFormat::Unknown) { + // validated above so can assume this is a + // 32-bit float, 32-bit int, or 64-bit int + const bool is_int = _.IsIntScalarType(info.sampled_type); + const bool is_float = !is_int; + if ((is_float && !IsFloatImageFormat(info.format)) || + (is_int && !IsIntImageFormat(info.format))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4965) + << "Image Format type does not match Sample Type operand (" + << (is_int ? "integer" : "float") << ")"; + } else if (is_int) { + const uint32_t bit_width = _.GetBitWidth(info.sampled_type); + // format check above to be int + if ((bit_width == 32 && IsInt64ImageFormat(info.format)) || + (bit_width == 64 && !IsInt64ImageFormat(info.format))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4965) + << "Image Format width does not match Sample Type " + "operand (bit width of " + << bit_width << ")"; + } + } + } } return SPV_SUCCESS; @@ -994,6 +1199,7 @@ case spv::Op::OpImageBlockMatchWindowSSDQCOM: case spv::Op::OpImageBlockMatchGatherSADQCOM: case spv::Op::OpImageBlockMatchGatherSSDQCOM: + case spv::Op::OpImageSampleFootprintNV: return true; case spv::Op::OpStore: if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true; @@ -1003,6 +1209,110 @@ } } +spv_result_t ValidateImageCoordinate(ValidationState_t& _, + const Instruction* inst, + const ImageTypeInfo& info, + uint32_t word_index) { + const spv::Op opcode = inst->opcode(); + const uint32_t coord_type = _.GetOperandTypeId(inst, word_index); + + const bool float_only = + opcode == spv::Op::OpImageSampleImplicitLod || + opcode == spv::Op::OpImageSampleDrefImplicitLod || + opcode == spv::Op::OpImageSampleDrefExplicitLod || + opcode == spv::Op::OpImageSampleProjImplicitLod || + opcode == spv::Op::OpImageSampleProjExplicitLod || + opcode == spv::Op::OpImageSampleProjDrefImplicitLod || + opcode == spv::Op::OpImageSampleProjDrefExplicitLod || + opcode == spv::Op::OpImageGather || + opcode == spv::Op::OpImageDrefGather || + opcode == spv::Op::OpImageQueryLod || + opcode == spv::Op::OpImageSparseSampleImplicitLod || + opcode == spv::Op::OpImageSparseSampleDrefImplicitLod || + opcode == spv::Op::OpImageSparseSampleDrefExplicitLod || + opcode == spv::Op::OpImageSparseGather || + opcode == spv::Op::OpImageSparseDrefGather; + + const bool int_only = opcode == spv::Op::OpImageFetch || + opcode == spv::Op::OpImageSparseFetch || + opcode == spv::Op::OpImageTexelPointer || + opcode == spv::Op::OpUntypedImageTexelPointerEXT; + + const bool int_or_float = opcode == spv::Op::OpImageSampleExplicitLod || + opcode == spv::Op::OpImageSparseSampleExplicitLod || + opcode == spv::Op::OpImageRead || + opcode == spv::Op::OpImageWrite || + opcode == spv::Op::OpImageSparseRead; + + assert(float_only || int_only || int_or_float); + + if (float_only && !_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be a 32-bit float scalar or vector"; + } else if (int_only && !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be a 32-bit integer scalar or vector"; + } else if (int_or_float) { + if (!_.IsFloatScalarOrVectorType(coord_type) && + !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be a 32-bit integer or float scalar or " + "vector"; + } + } + + // Needs to be after we validate the scalar/vector + if (_.GetBitWidth(coord_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be a 32-bit scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + + if (opcode == spv::Op::OpImageTexelPointer) { + if (min_coord_size != actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have " << min_coord_size + << " components, but given " << actual_coord_size; + } + } else if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (info.dim == spv::Dim::SubpassData) { + const Instruction* coord_inst = + _.FindDef(inst->GetOperandAs<uint32_t>(word_index)); + + bool is_zero_vector = false; + if (coord_inst->opcode() == spv::Op::OpConstantNull) { + is_zero_vector = true; + } else if (coord_inst->opcode() == spv::Op::OpConstantComposite) { + // There is zero reason we should be allowing a OpSpecConstantComposite + if (coord_inst->words().size() == 5) { + uint64_t val_0 = 0; + uint64_t val_1 = 0; + if (_.EvalConstantValUint64(coord_inst->word(3), &val_0) && + _.EvalConstantValUint64(coord_inst->word(4), &val_1) && + val_0 == 0 && val_1 == 0) { + is_zero_vector = true; + } + } + } + + if (!is_zero_vector) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4660) + << "Expected Coordinate for a SubpassData image to be a " + "OpConstantComposite of (0,0) or OpConstantNull"; + } + } + + return SPV_SUCCESS; +} + spv_result_t ValidateSampledImage(ValidationState_t& _, const Instruction* inst) { auto type_inst = _.FindDef(inst->type_id()); @@ -1096,7 +1406,7 @@ << "Result <id> from OpSampledImage instruction must not appear " "as " "operands of Op" - << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "." + << spvOpcodeString(consumer_opcode) << "." << " Found result <id> " << _.getIdName(inst->id()) << " as an operand of <id> " << _.getIdName(consumer_instr->id()) << "."; @@ -1106,12 +1416,11 @@ return _.diag(SPV_ERROR_INVALID_ID, inst) << "Result <id> from OpSampledImage instruction must not appear " "as operand for Op" - << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) + << spvOpcodeString(consumer_opcode) << ", since it is not specified as taking an " - << "OpTypeSampledImage." - << " Found result <id> " << _.getIdName(inst->id()) - << " as an operand of <id> " << _.getIdName(consumer_instr->id()) - << "."; + << "OpTypeSampledImage." << " Found result <id> " + << _.getIdName(inst->id()) << " as an operand of <id> " + << _.getIdName(consumer_instr->id()) << "."; } } } @@ -1132,6 +1441,8 @@ spv_result_t ValidateImageTexelPointer(ValidationState_t& _, const Instruction* inst) { + bool isUntyped = (inst->opcode() == spv::Op::OpUntypedImageTexelPointerEXT); + const auto result_type = _.FindDef(inst->type_id()); if (result_type->opcode() != spv::Op::OpTypePointer && result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) { @@ -1162,16 +1473,23 @@ } } - const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2)); - if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) { + const auto image_ptr = + _.FindDef(_.GetOperandTypeId(inst, (isUntyped ? 3 : 2))); + if (!image_ptr || + (isUntyped && image_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR) || + (!isUntyped && image_ptr->opcode() != spv::Op::OpTypePointer)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image to be OpTypePointer"; + << "Expected Image to be " + << (isUntyped ? "OpTypeUntypedPointerKHR" : "OpTypePointer"); } - const auto image_type = image_ptr->GetOperandAs<uint32_t>(2); + const auto image_type = isUntyped ? inst->GetOperandAs<uint32_t>(2) + : image_ptr->GetOperandAs<uint32_t>(2); if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image to be OpTypePointer with Type OpTypeImage"; + << "Expected Image to be " + << (isUntyped ? "OpTypeUntypedPointerKHR" : "OpTypePointer ") + << "with Type OpTypeImage"; } ImageTypeInfo info; @@ -1196,49 +1514,23 @@ if (info.dim == spv::Dim::SubpassData) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Image Dim SubpassData cannot be used with OpImageTexelPointer"; + << "Image Dim SubpassData cannot be used with " + << (isUntyped ? "OpUntypedImageTexelPointerEXT" + : "OpImageTexelPointer"); } if (info.dim == spv::Dim::TileImageDataEXT) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image Dim TileImageDataEXT cannot be used with " - "OpImageTexelPointer"; + << (isUntyped ? "OpUntypedImageTexelPointerEXT" + : "OpImageTexelPointer"); } - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be integer scalar or vector"; - } + if (spv_result_t result = ValidateImageCoordinate( + _, inst, info, /* word_index = */ (isUntyped ? 4 : 3))) + return result; - uint32_t expected_coord_size = 0; - if (info.arrayed == 0) { - expected_coord_size = GetPlaneCoordSize(info); - } else if (info.arrayed == 1) { - switch (info.dim) { - case spv::Dim::Dim1D: - expected_coord_size = 2; - break; - case spv::Dim::Cube: - case spv::Dim::Dim2D: - expected_coord_size = 3; - break; - default: - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when " - "Arrayed is 1"; - break; - } - } - - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (expected_coord_size != actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have " << expected_coord_size - << " components, but given " << actual_coord_size; - } - - const uint32_t sample_type = _.GetOperandTypeId(inst, 4); + const uint32_t sample_type = _.GetOperandTypeId(inst, (isUntyped ? 5 : 4)); if (!sample_type || !_.IsIntScalarType(sample_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Sample to be integer scalar"; @@ -1246,7 +1538,8 @@ if (info.multisampled == 0) { uint64_t ms = 0; - if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) || + if (!_.EvalConstantValUint64( + inst->GetOperandAs<uint32_t>(isUntyped ? 5 : 4), &ms) || ms != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Sample for Image with MS 0 to be a valid <id> for " @@ -1255,18 +1548,24 @@ } if (spvIsVulkanEnv(_.context()->target_env)) { - if ((info.format != spv::ImageFormat::R64i) && - (info.format != spv::ImageFormat::R64ui) && - (info.format != spv::ImageFormat::R32f) && - (info.format != spv::ImageFormat::R32i) && - (info.format != spv::ImageFormat::R32ui) && - !((info.format == spv::ImageFormat::Rg16f || - info.format == spv::ImageFormat::Rgba16f) && - _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) { + bool valid_format = info.format == spv::ImageFormat::R64i || + info.format == spv::ImageFormat::R64ui || + info.format == spv::ImageFormat::R32f || + info.format == spv::ImageFormat::R32i || + info.format == spv::ImageFormat::R32ui; + if (!valid_format && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV)) { + valid_format = info.format == spv::ImageFormat::Rg16f || + info.format == spv::ImageFormat::Rgba16f; + } + + if (!valid_format) { + const uint32_t vuid = isUntyped ? 11416 : 4658; return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4658) + << _.VkErrorID(vuid) << "Expected the Image Format in Image to be R64i, R64ui, R32f, " - "R32i, or R32ui for Vulkan environment"; + "R32i, or R32ui for Vulkan environment using Op" + << spvOpcodeString(inst->opcode()); } } @@ -1327,29 +1626,9 @@ } } - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if ((opcode == spv::Op::OpImageSampleExplicitLod || - opcode == spv::Op::OpImageSparseSampleExplicitLod) && - _.HasCapability(spv::Capability::Kernel)) { - if (!_.IsFloatScalarOrVectorType(coord_type) && - !_.IsIntScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be int or float scalar or vector"; - } - } else { - if (!_.IsFloatScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be float scalar or vector"; - } - } - - const uint32_t min_coord_size = GetMinCoordSize(opcode, info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 3)) + return result; const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); @@ -1374,7 +1653,7 @@ spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst, const ImageTypeInfo& info) { const uint32_t dref_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { + if (!_.IsFloatScalarType(dref_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Dref to be of 32-bit float type"; } @@ -1436,19 +1715,9 @@ << GetActualResultTypeStr(opcode); } - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if (!_.IsFloatScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be float scalar or vector"; - } - - const uint32_t min_coord_size = GetMinCoordSize(opcode, info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 3)) + return result; if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; @@ -1510,19 +1779,9 @@ << "Expected Image 'Sampled' parameter to be 1"; } - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if (!_.IsIntScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be int scalar or vector"; - } - - const uint32_t min_coord_size = GetMinCoordSize(opcode, info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 3)) + return result; if (spv_result_t result = ValidateImageOperands(_, inst, info, /* word_index = */ 6)) @@ -1590,26 +1849,15 @@ << "Expected Image 'Dim' to be 2D, Cube, or Rect"; } - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if (!_.IsFloatScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be float scalar or vector"; - } - - const uint32_t min_coord_size = GetMinCoordSize(opcode, info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 3)) + return result; if (opcode == spv::Op::OpImageGather || opcode == spv::Op::OpImageSparseGather) { const uint32_t component = inst->GetOperandAs<uint32_t>(4); const uint32_t component_index_type = _.GetTypeId(component); - if (!_.IsIntScalarType(component_index_type) || - _.GetBitWidth(component_index_type) != 32) { + if (!_.IsIntScalarType(component_index_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Component to be 32-bit int scalar"; } @@ -1733,19 +1981,9 @@ if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) return result; - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if (!_.IsIntScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be int scalar or vector"; - } - - const uint32_t min_coord_size = GetMinCoordSize(opcode, info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 3)) + return result; if (spvIsVulkanEnv(_.context()->target_env)) { if (info.format == spv::ImageFormat::Unknown && @@ -1790,19 +2028,9 @@ if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) return result; - const uint32_t coord_type = _.GetOperandTypeId(inst, 1); - if (!_.IsIntScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be int scalar or vector"; - } - - const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 1)) + return result; // because it needs to match with 'Sampled Type' the Texel can't be a boolean const uint32_t texel_type = _.GetOperandTypeId(inst, 2); @@ -1930,9 +2158,9 @@ } const uint32_t lod_type = _.GetOperandTypeId(inst, 3); - if (!_.IsIntScalarType(lod_type)) { + if (!_.IsIntScalarType(lod_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Level of Detail to be int scalar"; + << "Expected Level of Detail to be a 32-bit int scalar"; } return SPV_SUCCESS; } @@ -2093,27 +2321,9 @@ << "Image 'Dim' must be 1D, 2D, 3D or Cube"; } - const uint32_t coord_type = _.GetOperandTypeId(inst, 3); - if (_.HasCapability(spv::Capability::Kernel)) { - if (!_.IsFloatScalarOrVectorType(coord_type) && - !_.IsIntScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be int or float scalar or vector"; - } - } else { - if (!_.IsFloatScalarOrVectorType(coord_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to be float scalar or vector"; - } - } - - const uint32_t min_coord_size = GetPlaneCoordSize(info); - const uint32_t actual_coord_size = _.GetDimension(coord_type); - if (min_coord_size > actual_coord_size) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Coordinate to have at least " << min_coord_size - << " components, but given only " << actual_coord_size; - } + if (spv_result_t result = + ValidateImageCoordinate(_, inst, info, /* word_index = */ 3)) + return result; // The operand is a sampled image. // The sampled image type is already checked to be parameterized by an image @@ -2316,6 +2526,80 @@ return res; } +spv_result_t ValidateTileImageEXT(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + const uint32_t result_type = inst->type_id(); + const char* result_type_str = GetActualResultTypeStr(opcode); + if (opcode == spv::Op::OpColorAttachmentReadEXT) { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << result_type_str + << " to be int or float scalar or vector type"; + } + + const uint32_t attachment_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(attachment_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, attachment_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { + const uint32_t result_component_type = _.GetComponentType(result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (info.dim != spv::Dim::TileImageDataEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be TileImageDataEXT"; + } + } else if (opcode == spv::Op::OpDepthAttachmentReadEXT) { + if (!_.IsFloatScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << result_type_str + << " to be a 32-bit floating-point type scalar"; + } + } else { + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << result_type_str + << " to be a 32-bit integer type scalar"; + } + } + + size_t sample_word_index = + opcode == spv::Op::OpColorAttachmentReadEXT ? 4 : 3; + + if (inst->words().size() == sample_word_index + 1) { + const uint32_t sample_id = inst->word(sample_word_index); + const uint32_t sample_type = _.GetTypeId(sample_id); + if (!_.IsIntScalarType(sample_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample to be a 32-bit int scalar"; + } + } + + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + spvOpcodeString(opcode) + + std::string(" requires Fragment execution model")); + + return SPV_SUCCESS; +} + } // namespace // Validates correctness of image instructions. @@ -2378,6 +2662,7 @@ case spv::Op::OpSampledImage: return ValidateSampledImage(_, inst); case spv::Op::OpImageTexelPointer: + case spv::Op::OpUntypedImageTexelPointerEXT: return ValidateImageTexelPointer(_, inst); case spv::Op::OpImageSampleImplicitLod: @@ -2450,6 +2735,11 @@ case spv::Op::OpImageBlockMatchGatherSSDQCOM: return ValidateImageProcessingQCOM(_, inst); + case spv::Op::OpColorAttachmentReadEXT: + case spv::Op::OpDepthAttachmentReadEXT: + case spv::Op::OpStencilAttachmentReadEXT: + return ValidateTileImageEXT(_, inst); + default: break; }
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp index 39b1c02..7b3b3af 100644 --- a/source/val/validate_instruction.cpp +++ b/source/val/validate_instruction.cpp
@@ -20,13 +20,13 @@ #include <vector> #include "source/enum_set.h" -#include "source/enum_string_mapping.h" #include "source/extensions.h" #include "source/opcode.h" #include "source/operand.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" +#include "source/table2.h" #include "source/util/string_utils.h" #include "source/val/validate.h" #include "source/val/validation_state.h" @@ -35,14 +35,13 @@ namespace val { namespace { -std::string ToString(const CapabilitySet& capabilities, - const AssemblyGrammar& grammar) { +std::string ToString(const CapabilitySet& capabilities) { std::stringstream ss; for (auto capability : capabilities) { - spv_operand_desc desc; - if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, - uint32_t(capability), &desc)) - ss << desc->name << " "; + const spvtools::OperandDesc* desc = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + uint32_t(capability), &desc)) + ss << desc->name().data() << " "; else ss << uint32_t(capability) << " "; } @@ -72,10 +71,11 @@ break; } // Look it up in the grammar - spv_opcode_desc opcode_desc = {}; - if (SPV_SUCCESS == state.grammar().lookupOpcode(opcode, &opcode_desc)) { + const spvtools::InstructionDesc* opcode_desc = nullptr; + if (SPV_SUCCESS == + LookupOpcodeForEnv(state.context()->target_env, opcode, &opcode_desc)) { return state.grammar().filterCapsAgainstTargetEnv( - opcode_desc->capabilities, opcode_desc->numCapabilities); + opcode_desc->capabilities()); } return CapabilitySet(); } @@ -86,7 +86,7 @@ // return an error code. spv_result_t OperandVersionExtensionCheck( ValidationState_t& _, const Instruction* inst, size_t which_operand, - const spv_operand_desc_t& operand_desc, uint32_t word) { + const spvtools::OperandDesc& operand_desc, uint32_t word) { const uint32_t module_version = _.version(); const uint32_t operand_min_version = operand_desc.minVersion; const uint32_t operand_last_version = operand_desc.lastVersion; @@ -103,27 +103,29 @@ return _.diag(SPV_ERROR_WRONG_VERSION, inst) << spvtools::utils::CardinalToOrdinal(which_operand) << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " - << operand_desc.name << "(" << word << ") requires SPIR-V version " + << operand_desc.name().data() << "(" << word + << ") requires SPIR-V version " << SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "." << SPV_SPIRV_VERSION_MINOR_PART(operand_last_version) << " or earlier"; } - if (!reserved && operand_desc.numExtensions == 0) { + if (!reserved && operand_desc.extensions_range.empty()) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) << spvtools::utils::CardinalToOrdinal(which_operand) << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " - << operand_desc.name << "(" << word << ") requires SPIR-V version " + << operand_desc.name().data() << "(" << word + << ") requires SPIR-V version " << SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "." << SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later"; } else { - ExtensionSet required_extensions(operand_desc.numExtensions, - operand_desc.extensions); + ExtensionSet required_extensions(operand_desc.extensions_range.count(), + operand_desc.extensions().data()); if (!_.HasAnyOfExtensions(required_extensions)) { return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) << spvtools::utils::CardinalToOrdinal(which_operand) << " operand of " << spvOpcodeString(inst->opcode()) - << ": operand " << operand_desc.name << "(" << word + << ": operand " << operand_desc.name().data() << "(" << word << ") requires one of these extensions: " << ExtensionSetToString(required_extensions); } @@ -166,9 +168,9 @@ } CapabilitySet enabling_capabilities; - spv_operand_desc operand_desc = nullptr; + const spvtools::OperandDesc* operand_desc = nullptr; const auto lookup_result = - state.grammar().lookupOperand(operand.type, word, &operand_desc); + spvtools::LookupOperand(operand.type, word, &operand_desc); if (lookup_result == SPV_SUCCESS) { // Allow FPRoundingMode decoration if requested. if (operand.type == SPV_OPERAND_TYPE_DECORATION && @@ -186,14 +188,15 @@ } } else { enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv( - operand_desc->capabilities, operand_desc->numCapabilities); + operand_desc->capabilities()); } // When encountering an OpCapability instruction, the instruction pass // registers a capability with the module *before* checking capabilities. // So in the case of an OpCapability instruction, don't bother checking // enablement by another capability. - if (inst->opcode() != spv::Op::OpCapability) { + if (inst->opcode() != spv::Op::OpCapability && + inst->opcode() != spv::Op::OpConditionalCapabilityINTEL) { const bool enabled_by_cap = state.HasAnyOfCapabilities(enabling_capabilities); if (!enabling_capabilities.empty() && !enabled_by_cap) { @@ -201,7 +204,7 @@ << "Operand " << which_operand << " of " << spvOpcodeString(inst->opcode()) << " requires one of these capabilities: " - << ToString(enabling_capabilities, state.grammar()); + << ToString(enabling_capabilities); } } return OperandVersionExtensionCheck(state, inst, which_operand, @@ -222,10 +225,10 @@ case spv::Op::OpImageSparseSampleProjExplicitLod: case spv::Op::OpImageSparseSampleProjDrefImplicitLod: case spv::Op::OpImageSparseSampleProjDrefExplicitLod: { - spv_opcode_desc inst_desc; - _.grammar().lookupOpcode(opcode, &inst_desc); + const spvtools::InstructionDesc* inst_desc = nullptr; + spvtools::LookupOpcode(opcode, &inst_desc); return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "Invalid Opcode name 'Op" << inst_desc->name << "'"; + << "Invalid Opcode name 'Op" << inst_desc->name().data() << "'"; } default: break; @@ -242,8 +245,7 @@ if (!_.HasAnyOfCapabilities(opcode_caps)) { return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) << "Opcode " << spvOpcodeString(opcode) - << " requires one of these capabilities: " - << ToString(opcode_caps, _.grammar()); + << " requires one of these capabilities: " << ToString(opcode_caps); } for (size_t i = 0; i < inst->operands().size(); ++i) { const auto& operand = inst->operand(i); @@ -276,8 +278,8 @@ // dependencies for the opcode. spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { const auto opcode = inst->opcode(); - spv_opcode_desc inst_desc; - const spv_result_t r = _.grammar().lookupOpcode(opcode, &inst_desc); + const spvtools::InstructionDesc* inst_desc = nullptr; + const spv_result_t r = spvtools::LookupOpcode(opcode, &inst_desc); assert(r == SPV_SUCCESS); (void)r; @@ -297,13 +299,14 @@ const bool capability_check_is_sufficient = inst->opcode() != spv::Op::OpTerminateInvocation; - if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) { + if (capability_check_is_sufficient && !inst_desc->capabilities().empty()) { // We already checked that the direct capability dependency has been // satisfied. We don't need to check any further. return SPV_SUCCESS; } - ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions); + ExtensionSet exts(inst_desc->extensions().begin(), + inst_desc->extensions().end()); if (exts.empty()) { // If no extensions can enable this instruction, then emit error // messages only concerning core SPIR-V versions if errors happen. @@ -459,10 +462,13 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) { const spv::Op opcode = inst->opcode(); - if (opcode == spv::Op::OpExtension) { + if (opcode == spv::Op::OpExtension || + opcode == spv::Op::OpConditionalExtensionINTEL) { CheckIfKnownExtension(_, inst); } else if (opcode == spv::Op::OpCapability) { _.RegisterCapability(inst->GetOperandAs<spv::Capability>(0)); + } else if (opcode == spv::Op::OpConditionalCapabilityINTEL) { + _.RegisterCapability(inst->GetOperandAs<spv::Capability>(1)); } else if (opcode == spv::Op::OpMemoryModel) { if (_.has_memory_model_specified()) { return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) @@ -481,6 +487,14 @@ spv::ExecutionMode::LocalSizeId) { _.RegisterEntryPointLocalSize(entry_point, inst); } + if (inst->GetOperandAs<spv::ExecutionMode>(1) == + spv::ExecutionMode::OutputPrimitivesEXT) { + _.RegisterEntryPointOutputPrimitivesEXT(entry_point, inst); + } + if (inst->GetOperandAs<spv::ExecutionMode>(1) == + spv::ExecutionMode::OutputVertices) { + _.RegisterEntryPointOutputVertices(entry_point, inst); + } } else if (opcode == spv::Op::OpVariable) { const auto storage_class = inst->GetOperandAs<spv::StorageClass>(2); if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 8b96dc8..3c62960 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp
@@ -48,6 +48,29 @@ } } +// Special validation for varibles that are between shader stages +spv_result_t ValidateInputOutputInterfaceVariables(ValidationState_t& _, + const Instruction* var) { + auto var_pointer = _.FindDef(var->GetOperandAs<uint32_t>(0)); + uint32_t pointer_id = var_pointer->GetOperandAs<uint32_t>(2); + + const auto isPhysicalStorageBuffer = [](const Instruction* insn) { + return insn->opcode() == spv::Op::OpTypePointer && + insn->GetOperandAs<spv::StorageClass>(1) == + spv::StorageClass::PhysicalStorageBuffer; + }; + + if (_.ContainsType(pointer_id, isPhysicalStorageBuffer)) { + return _.diag(SPV_ERROR_INVALID_ID, var) + << _.VkErrorID(9557) << "Input/Output interface variable id <" + << var->id() + << "> contains a PhysicalStorageBuffer pointer, which is not " + "allowed. If you want to interface shader stages with a " + "PhysicalStorageBuffer, cast to a uint64 or uvec2 instead."; + } + return SPV_SUCCESS; +} + // Checks that \c var is listed as an interface in all the entry points that use // it. spv_result_t check_interface_variable(ValidationState_t& _, @@ -107,6 +130,14 @@ } } + if (spvIsVulkanEnv(_.context()->target_env)) { + if (var->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Input || + var->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Output) { + if (auto error = ValidateInputOutputInterfaceVariables(_, var)) + return error; + } + } + return SPV_SUCCESS; } @@ -124,11 +155,12 @@ *num_locations = 1; break; case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: // 3- and 4-component 64-bit vectors consume two locations. if ((_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) || _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat, 64)) && - (type->GetOperandAs<uint32_t>(2) > 2)) { + (_.GetDimension(type->id()) > 2)) { *num_locations = 2; } else { *num_locations = 1; @@ -208,12 +240,13 @@ } break; case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: // Vectors consume components equal to the underlying type's consumption // times the number of elements in the vector. Note that 3- and 4-element // vectors cannot have a component decoration (i.e. assumed to be zero). num_components = NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1))); - num_components *= type->GetOperandAs<uint32_t>(2); + num_components *= _.GetDimension(type->id()); break; case spv::Op::OpTypeArray: // Skip the array. @@ -518,12 +551,21 @@ return SPV_SUCCESS; } + const bool is_geometry = entry_point->GetOperandAs<spv::ExecutionModel>(0) == + spv::ExecutionModel::Geometry; + const bool has_geometry_streams = + is_geometry && _.HasCapability(spv::Capability::GeometryStreams); + // Locations are stored as a combined location and component values. std::unordered_set<uint32_t> input_locations; std::unordered_set<uint32_t> output_locations_index0; std::unordered_set<uint32_t> output_locations_index1; std::unordered_set<uint32_t> patch_locations_index0; std::unordered_set<uint32_t> patch_locations_index1; + std::unordered_map<uint32_t, std::unordered_set<uint32_t>> + output_locations_per_stream; + std::unordered_map<uint32_t, std::unordered_set<uint32_t>> + output_index1_locations_per_stream; std::unordered_set<uint32_t> seen; for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { auto interface_id = entry_point->GetOperandAs<uint32_t>(i); @@ -561,12 +603,31 @@ continue; } - auto locations = (storage_class == spv::StorageClass::Input) - ? &input_locations - : &output_locations_index0; - if (auto error = GetLocationsForVariable( - _, entry_point, interface_var, locations, &output_locations_index1)) - return error; + // For geometry shader outputs with GeometryStreams, + // use per-stream location sets since each stream + // has an independent location namespace. + if (has_geometry_streams && storage_class == spv::StorageClass::Output) { + uint32_t stream = 0; + for (auto& dec : _.id_decorations(interface_var->id())) { + if (dec.dec_type() == spv::Decoration::Stream) { + stream = dec.params()[0]; + break; + } + } + if (auto error = GetLocationsForVariable( + _, entry_point, interface_var, + &output_locations_per_stream[stream], + &output_index1_locations_per_stream[stream])) + return error; + } else { + auto locations = (storage_class == spv::StorageClass::Input) + ? &input_locations + : &output_locations_index0; + if (auto error = + GetLocationsForVariable(_, entry_point, interface_var, locations, + &output_locations_index1)) + return error; + } } return SPV_SUCCESS; @@ -584,7 +645,8 @@ auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2); switch (storage_class) { case spv::StorageClass::PushConstant: { - if (has_push_constant) { + if (has_push_constant && + !(_.HasCapability(spv::Capability::PushConstantBanksNV))) { return _.diag(SPV_ERROR_INVALID_DATA, entry_point) << _.VkErrorID(6673) << "Entry-point has more than one variable with the " @@ -623,6 +685,47 @@ has_callable_data = true; break; } + case spv::StorageClass::Input: + case spv::StorageClass::Output: { + auto result_type = _.FindDef(interface_var->type_id()); + if (_.ContainsType(result_type->GetOperandAs<uint32_t>(2), + [](const Instruction* inst) { + if (inst && + inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->words().size() > 3) { + if (inst->GetOperandAs<spv::FPEncoding>(2) == + spv::FPEncoding::BFloat16KHR) { + return true; + } + } + } + return false; + })) { + return _.diag(SPV_ERROR_INVALID_ID, interface_var) + << _.VkErrorID(10370) << "Bfloat16 OpVariable <id> " + << _.getIdName(interface_var->id()) << " must not be declared " + << "with a Storage Class of Input or Output."; + } + if (_.ContainsType( + result_type->GetOperandAs<uint32_t>(2), + [](const Instruction* inst) { + if (inst && inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->words().size() > 3) { + auto encoding = inst->GetOperandAs<spv::FPEncoding>(2); + if ((encoding == spv::FPEncoding::Float8E4M3EXT) || + (encoding == spv::FPEncoding::Float8E5M2EXT)) { + return true; + } + } + } + return false; + })) { + return _.diag(SPV_ERROR_INVALID_ID, interface_var) + << _.VkErrorID(10823) << "FP8 E4M3/E5M2 OpVariable <id> " + << _.getIdName(interface_var->id()) << " must not be declared " + << "with a Storage Class of Input or Output."; + } + } default: break; }
diff --git a/source/val/validate_invalid_type.cpp b/source/val/validate_invalid_type.cpp new file mode 100644 index 0000000..05c09e2 --- /dev/null +++ b/source/val/validate_invalid_type.cpp
@@ -0,0 +1,177 @@ +// Copyright (c) 2025 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. + +// Performs validation of invalid type instructions. + +#include <vector> + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of certain special type instructions. +spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + switch (opcode) { + // OpExtInst + case spv::Op::OpExtInst: + // Arithmetic Instructions + case spv::Op::OpFAdd: + case spv::Op::OpFSub: + case spv::Op::OpFMul: + case spv::Op::OpFDiv: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpFNegate: + // Derivative Instructions + case spv::Op::OpDPdx: + case spv::Op::OpDPdy: + case spv::Op::OpFwidth: + case spv::Op::OpDPdxFine: + case spv::Op::OpDPdyFine: + case spv::Op::OpFwidthFine: + case spv::Op::OpDPdxCoarse: + case spv::Op::OpDPdyCoarse: + case spv::Op::OpFwidthCoarse: + // Atomic Instructions + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicFMaxEXT: + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + // Group and Subgroup Instructions + case spv::Op::OpGroupNonUniformRotateKHR: + case spv::Op::OpGroupNonUniformBroadcast: + case spv::Op::OpGroupNonUniformShuffle: + case spv::Op::OpGroupNonUniformShuffleXor: + case spv::Op::OpGroupNonUniformShuffleUp: + case spv::Op::OpGroupNonUniformShuffleDown: + case spv::Op::OpGroupNonUniformQuadBroadcast: + case spv::Op::OpGroupNonUniformQuadSwap: + case spv::Op::OpGroupNonUniformBroadcastFirst: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformFMul: + case spv::Op::OpGroupNonUniformFMin: { + const uint32_t result_type = inst->type_id(); + if (_.IsBfloat16Type(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + if (_.IsFP8Type(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support FP8 E4M3/E5M2 types."; + } + break; + } + + case spv::Op::OpAtomicStore: { + uint32_t data_type = + _.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id(); + if (_.IsBfloat16VectorType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + if (_.IsFP8VectorType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support FP8 E4M3/E5M2 types."; + } + break; + } + // Relational and Logical Instructions + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpSignBitSet: { + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (_.IsBfloat16Type(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + if (_.IsFP8Type(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support FP8 E4M3/E5M2 types."; + } + break; + } + + case spv::Op::OpGroupNonUniformAllEqual: { + const auto value_type = _.GetOperandTypeId(inst, 3); + if (_.IsBfloat16Type(value_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + if (_.IsFP8Type(value_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support FP8 E4M3/E5M2 types."; + } + + break; + } + + case spv::Op::OpMatrixTimesMatrix: { + const uint32_t result_type = inst->type_id(); + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) { + if (_.IsBfloat16Type(res_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support BFloat16 type."; + } + if (_.IsFP8Type(res_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support FP8 E4M3/E5M2 types."; + } + } + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp index baf36b8..9a44e79 100644 --- a/source/val/validate_layout.cpp +++ b/source/val/validate_layout.cpp
@@ -15,7 +15,6 @@ // Source code for logical layout validation as described in section 2.4 #include "DebugInfo.h" -#include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" #include "source/opcode.h" #include "source/operand.h" @@ -23,6 +22,7 @@ #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" +#include "spirv/unified1/NonSemanticShaderDebugInfo.h" namespace spvtools { namespace val { @@ -50,16 +50,16 @@ } } else if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { - const NonSemanticShaderDebugInfo100Instructions ext_inst_key = - NonSemanticShaderDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || - ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || - ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || - ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || - ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || - ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + const NonSemanticShaderDebugInfoInstructions ext_inst_key = + NonSemanticShaderDebugInfoInstructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfoDebugScope || + ext_inst_key == NonSemanticShaderDebugInfoDebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfoDebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfoDebugValue || + ext_inst_key == NonSemanticShaderDebugInfoDebugLine || + ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine || ext_inst_key == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + NonSemanticShaderDebugInfoDebugFunctionDefinition) { local_debug_info = true; } } else { @@ -259,16 +259,16 @@ } } else if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { - const NonSemanticShaderDebugInfo100Instructions ext_inst_key = - NonSemanticShaderDebugInfo100Instructions(ext_inst_index); - if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || - ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || - ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || - ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || - ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || - ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + const NonSemanticShaderDebugInfoInstructions ext_inst_key = + NonSemanticShaderDebugInfoInstructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfoDebugScope || + ext_inst_key == NonSemanticShaderDebugInfoDebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfoDebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfoDebugValue || + ext_inst_key == NonSemanticShaderDebugInfoDebugLine || + ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine || ext_inst_key == - NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + NonSemanticShaderDebugInfoDebugFunctionDefinition) { local_debug_info = true; } } else { @@ -342,13 +342,84 @@ break; } } else { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << spvOpcodeString(opcode) - << " cannot appear in a function declaration"; + _.ProgressToNextLayoutSectionOrder(); + // All function sections have been processed. Recursively call + // ModuleLayoutPass to process the next section of the module + return ModuleLayoutPass(_, inst); } return SPV_SUCCESS; } +spv_result_t GraphScopedInstructions(ValidationState_t& _, + const Instruction* inst, spv::Op opcode) { + if (_.IsOpcodeInCurrentLayoutSection(opcode)) { + switch (opcode) { + case spv::Op::OpGraphARM: { + if (_.graph_definition_region() > kGraphDefinitionOutside) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Cannot define a graph in a graph"; + } + _.SetGraphDefinitionRegion(kGraphDefinitionBegin); + } break; + case spv::Op::OpGraphInputARM: { + if ((_.graph_definition_region() != kGraphDefinitionBegin) && + (_.graph_definition_region() != kGraphDefinitionInputs)) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "OpGraphInputARM" + << " must immediately follow an OpGraphARM or OpGraphInputARM " + "instruction."; + } + _.SetGraphDefinitionRegion(kGraphDefinitionInputs); + } break; + case spv::Op::OpGraphSetOutputARM: { + if ((_.graph_definition_region() != kGraphDefinitionBegin) && + (_.graph_definition_region() != kGraphDefinitionInputs) && + (_.graph_definition_region() != kGraphDefinitionBody) && + (_.graph_definition_region() != kGraphDefinitionOutputs)) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Op" << spvOpcodeString(opcode) + << " must immediately precede an OpGraphEndARM or " + "OpGraphSetOutputARM instruction."; + } + _.SetGraphDefinitionRegion(kGraphDefinitionOutputs); + } break; + case spv::Op::OpGraphEndARM: { + if (_.graph_definition_region() != kGraphDefinitionOutputs) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " must be preceded by at least one OpGraphSetOutputARM " + "instruction"; + } + _.SetGraphDefinitionRegion(kGraphDefinitionOutside); + } break; + case spv::Op::OpGraphEntryPointARM: + if (_.graph_definition_region() != kGraphDefinitionOutside) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " cannot appear in the definition of a graph"; + } + break; + default: + if (_.graph_definition_region() == kGraphDefinitionOutside) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Op" << spvOpcodeString(opcode) + << " must appear in a graph body"; + } + if (_.graph_definition_region() == kGraphDefinitionOutputs) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " cannot appear after a graph output instruction"; + } + _.SetGraphDefinitionRegion(kGraphDefinitionBody); + break; + } + } else { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Op" << spvOpcodeString(opcode) + << " cannot appear in the graph definitions section"; + } + return SPV_SUCCESS; +} } // namespace // TODO(umar): Check linkage capabilities for function declarations @@ -379,6 +450,11 @@ return error; } break; + case kLayoutGraphDefinitions: + if (auto error = GraphScopedInstructions(_, inst, opcode)) { + return error; + } + break; } return SPV_SUCCESS; }
diff --git a/source/val/validate_logical_pointers.cpp b/source/val/validate_logical_pointers.cpp new file mode 100644 index 0000000..b484312 --- /dev/null +++ b/source/val/validate_logical_pointers.cpp
@@ -0,0 +1,1023 @@ +// Copyright (c) 2025 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 <iostream> +#include <unordered_map> +#include <unordered_set> + +#include "source/opcode.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if inst is a logical pointer. +bool IsLogicalPointer(const ValidationState_t& _, const Instruction* inst) { + if (!_.IsPointerType(inst->type_id())) { + return false; + } + + // Physical storage buffer pointers are not logical pointers. + auto type_inst = _.FindDef(inst->type_id()); + auto sc = type_inst->GetOperandAs<spv::StorageClass>(1); + if (sc == spv::StorageClass::PhysicalStorageBuffer) { + return false; + } + + return true; +} + +// Returns true if inst is a variable pointer. +// Caches the result in variable_pointers. +bool IsVariablePointer(const ValidationState_t& _, + std::unordered_map<uint32_t, bool>& variable_pointers, + const Instruction* inst) { + const auto iter = variable_pointers.find(inst->id()); + if (iter != variable_pointers.end()) { + return iter->second; + } + + // Temporarily mark the instruction as NOT a variable pointer. + variable_pointers[inst->id()] = false; + + bool is_var_ptr = false; + switch (inst->opcode()) { + case spv::Op::OpPtrAccessChain: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: + case spv::Op::OpLoad: + case spv::Op::OpSelect: + case spv::Op::OpPhi: + case spv::Op::OpFunctionCall: + case spv::Op::OpConstantNull: + is_var_ptr = true; + break; + case spv::Op::OpFunctionParameter: + // Special case: skip to function calls. + if (IsLogicalPointer(_, inst)) { + auto func = inst->function(); + auto func_inst = _.FindDef(func->id()); + + const auto param_inst_num = inst - &_.ordered_instructions()[0]; + uint32_t param_index = 0; + uint32_t inst_index = 1; + while (_.ordered_instructions()[param_inst_num - inst_index].opcode() != + spv::Op::OpFunction) { + if (_.ordered_instructions()[param_inst_num - inst_index].opcode() == + spv::Op::OpFunctionParameter) { + param_index++; + } + ++inst_index; + } + + for (const auto& use_pair : func_inst->uses()) { + const auto use_inst = use_pair.first; + if (use_inst->opcode() == spv::Op::OpFunctionCall) { + const auto arg_id = + use_inst->GetOperandAs<uint32_t>(3 + param_index); + const auto arg_inst = _.FindDef(arg_id); + is_var_ptr |= IsVariablePointer(_, variable_pointers, arg_inst); + } + } + } + break; + default: { + for (uint32_t i = 0; i < inst->operands().size(); ++i) { + if (inst->operands()[i].type != SPV_OPERAND_TYPE_ID) { + continue; + } + + auto op_inst = _.FindDef(inst->GetOperandAs<uint32_t>(i)); + if (IsLogicalPointer(_, op_inst)) { + is_var_ptr |= IsVariablePointer(_, variable_pointers, op_inst); + } + } + break; + } + } + variable_pointers[inst->id()] = is_var_ptr; + return is_var_ptr; +} + +spv_result_t ValidateLogicalPointerOperands(ValidationState_t& _, + const Instruction* inst) { + bool has_pointer_operand = false; + spv::StorageClass sc = spv::StorageClass::Function; + for (uint32_t i = 0; i < inst->operands().size(); ++i) { + if (inst->operands()[i].type != SPV_OPERAND_TYPE_ID) { + continue; + } + + auto op_inst = _.FindDef(inst->GetOperandAs<uint32_t>(i)); + if (IsLogicalPointer(_, op_inst)) { + has_pointer_operand = true; + + // Assume that there are not mixed storage classes in the instruction. + // This is not true for OpCopyMemory and OpCopyMemorySized, but they allow + // all storage classes. + auto type_inst = _.FindDef(op_inst->type_id()); + sc = type_inst->GetOperandAs<spv::StorageClass>(1); + break; + } + } + + if (!has_pointer_operand) { + return SPV_SUCCESS; + } + + switch (inst->opcode()) { + // The following instructions allow logical pointer operands in all cases + // without capabilities. + case spv::Op::OpLoad: + case spv::Op::OpStore: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpFunctionCall: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyObject: + case spv::Op::OpArrayLength: + case spv::Op::OpExtInst: + // Core spec bugs + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpGroupDecorate: + case spv::Op::OpEntryPoint: + case spv::Op::OpName: + case spv::Op::OpDecorateString: + // SPV_KHR_untyped_pointers + case spv::Op::OpUntypedArrayLengthKHR: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpCopyMemorySized: + // Cooperative matrix KHR/NV + case spv::Op::OpCooperativeMatrixLoadKHR: + case spv::Op::OpCooperativeMatrixLoadNV: + case spv::Op::OpCooperativeMatrixStoreKHR: + case spv::Op::OpCooperativeMatrixStoreNV: + // SPV_KHR_ray_tracing + case spv::Op::OpTraceRayKHR: + case spv::Op::OpExecuteCallableKHR: + // SPV_KHR_ray_query + case spv::Op::OpRayQueryConfirmIntersectionKHR: + case spv::Op::OpRayQueryInitializeKHR: + case spv::Op::OpRayQueryTerminateKHR: + case spv::Op::OpRayQueryGenerateIntersectionKHR: + case spv::Op::OpRayQueryProceedKHR: + case spv::Op::OpRayQueryGetIntersectionTypeKHR: + case spv::Op::OpRayQueryGetRayTMinKHR: + case spv::Op::OpRayQueryGetRayFlagsKHR: + case spv::Op::OpRayQueryGetIntersectionTKHR: + case spv::Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR: + case spv::Op::OpRayQueryGetIntersectionInstanceIdKHR: + case spv::Op:: + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: + case spv::Op::OpRayQueryGetIntersectionGeometryIndexKHR: + case spv::Op::OpRayQueryGetIntersectionPrimitiveIndexKHR: + case spv::Op::OpRayQueryGetIntersectionBarycentricsKHR: + case spv::Op::OpRayQueryGetIntersectionFrontFaceKHR: + case spv::Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: + case spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR: + case spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR: + case spv::Op::OpRayQueryGetWorldRayDirectionKHR: + case spv::Op::OpRayQueryGetWorldRayOriginKHR: + case spv::Op::OpRayQueryGetIntersectionObjectToWorldKHR: + case spv::Op::OpRayQueryGetIntersectionWorldToObjectKHR: + // SPV_KHR_ray_tracing_position_fetch + case spv::Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR: + // SPV_NV_cluster_acceleration_structure + case spv::Op::OpRayQueryGetClusterIdNV: + case spv::Op::OpHitObjectGetClusterIdNV: + // SPV_NV_ray_tracing_motion_blur + case spv::Op::OpTraceMotionNV: + case spv::Op::OpTraceRayMotionNV: + // SPV_NV_linear_swept_spheres + case spv::Op::OpRayQueryGetIntersectionSpherePositionNV: + case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV: + case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV: + case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV: + case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV: + case spv::Op::OpRayQueryIsSphereHitNV: + case spv::Op::OpRayQueryIsLSSHitNV: + case spv::Op::OpHitObjectGetSpherePositionNV: + case spv::Op::OpHitObjectGetSphereRadiusNV: + case spv::Op::OpHitObjectGetLSSPositionsNV: + case spv::Op::OpHitObjectGetLSSRadiiNV: + case spv::Op::OpHitObjectIsSphereHitNV: + case spv::Op::OpHitObjectIsLSSHitNV: + // SPV_NV_shader_invocation_reorder + case spv::Op::OpReorderThreadWithHitObjectNV: + case spv::Op::OpHitObjectTraceRayNV: + case spv::Op::OpHitObjectTraceRayMotionNV: + case spv::Op::OpHitObjectRecordHitNV: + case spv::Op::OpHitObjectRecordHitMotionNV: + case spv::Op::OpHitObjectRecordHitWithIndexNV: + case spv::Op::OpHitObjectRecordHitWithIndexMotionNV: + case spv::Op::OpHitObjectRecordMissNV: + case spv::Op::OpHitObjectRecordMissMotionNV: + case spv::Op::OpHitObjectRecordEmptyNV: + case spv::Op::OpHitObjectExecuteShaderNV: + case spv::Op::OpHitObjectGetCurrentTimeNV: + case spv::Op::OpHitObjectGetAttributesNV: + case spv::Op::OpHitObjectGetHitKindNV: + case spv::Op::OpHitObjectGetPrimitiveIndexNV: + case spv::Op::OpHitObjectGetGeometryIndexNV: + case spv::Op::OpHitObjectGetInstanceIdNV: + case spv::Op::OpHitObjectGetInstanceCustomIndexNV: + case spv::Op::OpHitObjectGetObjectRayOriginNV: + case spv::Op::OpHitObjectGetObjectRayDirectionNV: + case spv::Op::OpHitObjectGetWorldRayDirectionNV: + case spv::Op::OpHitObjectGetWorldRayOriginNV: + case spv::Op::OpHitObjectGetObjectToWorldNV: + case spv::Op::OpHitObjectGetWorldToObjectNV: + case spv::Op::OpHitObjectGetRayTMaxNV: + case spv::Op::OpHitObjectGetRayTMinNV: + case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexNV: + case spv::Op::OpHitObjectGetShaderRecordBufferHandleNV: + case spv::Op::OpHitObjectIsEmptyNV: + case spv::Op::OpHitObjectIsHitNV: + case spv::Op::OpHitObjectIsMissNV: + // SPV_EXT_shader_invocation_reorder + case spv::Op::OpHitObjectRecordFromQueryEXT: + case spv::Op::OpHitObjectRecordMissEXT: + case spv::Op::OpHitObjectRecordMissMotionEXT: + case spv::Op::OpHitObjectGetIntersectionTriangleVertexPositionsEXT: + case spv::Op::OpHitObjectGetRayFlagsEXT: + case spv::Op::OpHitObjectSetShaderBindingTableRecordIndexEXT: + case spv::Op::OpHitObjectReorderExecuteShaderEXT: + case spv::Op::OpHitObjectTraceReorderExecuteEXT: + case spv::Op::OpHitObjectTraceMotionReorderExecuteEXT: + case spv::Op::OpReorderThreadWithHintEXT: + case spv::Op::OpReorderThreadWithHitObjectEXT: + case spv::Op::OpHitObjectTraceRayEXT: + case spv::Op::OpHitObjectTraceRayMotionEXT: + case spv::Op::OpHitObjectRecordEmptyEXT: + case spv::Op::OpHitObjectExecuteShaderEXT: + case spv::Op::OpHitObjectGetCurrentTimeEXT: + case spv::Op::OpHitObjectGetAttributesEXT: + case spv::Op::OpHitObjectGetHitKindEXT: + case spv::Op::OpHitObjectGetPrimitiveIndexEXT: + case spv::Op::OpHitObjectGetGeometryIndexEXT: + case spv::Op::OpHitObjectGetInstanceIdEXT: + case spv::Op::OpHitObjectGetInstanceCustomIndexEXT: + case spv::Op::OpHitObjectGetObjectRayOriginEXT: + case spv::Op::OpHitObjectGetObjectRayDirectionEXT: + case spv::Op::OpHitObjectGetWorldRayDirectionEXT: + case spv::Op::OpHitObjectGetWorldRayOriginEXT: + case spv::Op::OpHitObjectGetObjectToWorldEXT: + case spv::Op::OpHitObjectGetWorldToObjectEXT: + case spv::Op::OpHitObjectGetRayTMaxEXT: + case spv::Op::OpHitObjectGetRayTMinEXT: + case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexEXT: + case spv::Op::OpHitObjectGetShaderRecordBufferHandleEXT: + case spv::Op::OpHitObjectIsEmptyEXT: + case spv::Op::OpHitObjectIsHitEXT: + case spv::Op::OpHitObjectIsMissEXT: + // SPV_NV_raw_access_chains + case spv::Op::OpRawAccessChainNV: + // SPV_NV_cooperative_matrix2 + case spv::Op::OpCooperativeMatrixLoadTensorNV: + case spv::Op::OpCooperativeMatrixStoreTensorNV: + // SPV_NV_cooperative_vector + case spv::Op::OpCooperativeVectorLoadNV: + case spv::Op::OpCooperativeVectorStoreNV: + case spv::Op::OpCooperativeVectorMatrixMulNV: + case spv::Op::OpCooperativeVectorMatrixMulAddNV: + case spv::Op::OpCooperativeVectorOuterProductAccumulateNV: + case spv::Op::OpCooperativeVectorReduceSumAccumulateNV: + // SPV_EXT_mesh_shader + case spv::Op::OpEmitMeshTasksEXT: + // SPV_AMD_shader_enqueue (spec bugs) + case spv::Op::OpEnqueueNodePayloadsAMDX: + case spv::Op::OpNodePayloadArrayLengthAMDX: + case spv::Op::OpIsNodePayloadValidAMDX: + case spv::Op::OpFinishWritingNodePayloadAMDX: + // SPV_ARM_graph + case spv::Op::OpGraphEntryPointARM: + return SPV_SUCCESS; + // SPV_EXT_descriptor_heap + case spv::Op::OpBufferPointerEXT: + case spv::Op::OpUntypedImageTexelPointerEXT: + return SPV_SUCCESS; + // The following cases require a variable pointer capability. Since all + // instructions are for variable pointers, the storage class and capability + // are also checked. + case spv::Op::OpReturnValue: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpPtrEqual: + case spv::Op::OpPtrNotEqual: + case spv::Op::OpPtrDiff: + // Core spec bugs + case spv::Op::OpSelect: + case spv::Op::OpPhi: + case spv::Op::OpVariable: + // SPV_KHR_untyped_pointers + case spv::Op::OpUntypedPtrAccessChainKHR: + if ((_.HasCapability(spv::Capability::VariablePointersStorageBuffer) && + sc == spv::StorageClass ::StorageBuffer) || + (_.HasCapability(spv::Capability::VariablePointers) && + sc == spv::StorageClass::Workgroup)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instruction may only have a logical pointer operand in the " + "StorageBuffer or Workgroup storage classes with appropriate " + "variable pointers capability"; + default: + if (spvOpcodeIsAtomicOp(inst->opcode())) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instruction may not have a logical pointer operand"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateLogicalPointerReturns(ValidationState_t& _, + const Instruction* inst) { + if (!IsLogicalPointer(_, inst)) { + return SPV_SUCCESS; + } + + const auto type_inst = _.FindDef(inst->type_id()); + const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u); + + switch (inst->opcode()) { + // Core spec without an variable pointer capability. + case spv::Op::OpVariable: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpFunctionParameter: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpCopyObject: + // Core spec bugs + case spv::Op::OpUndef: + // SPV_INTEL_function_pointers + case spv::Op::OpConstantFunctionPointerINTEL: + // SPV_KHR_untyped_pointers + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedVariableKHR: + // SPV_NV_raw_access_chains + case spv::Op::OpRawAccessChainNV: + // SPV_AMD_shader_enqueue (spec bugs) + case spv::Op::OpAllocateNodePayloadsAMDX: + return SPV_SUCCESS; + // SPV_EXT_descriptor_heap + case spv::Op::OpBufferPointerEXT: + case spv::Op::OpUntypedImageTexelPointerEXT: + return SPV_SUCCESS; + // Core spec with variable pointer capability. Check storage classes since + // variable pointers can only be in certain storage classes. + case spv::Op::OpSelect: + case spv::Op::OpPhi: + case spv::Op::OpFunctionCall: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpLoad: + case spv::Op::OpConstantNull: + case spv::Op::OpFunction: + // SPV_KHR_untyped_pointers + case spv::Op::OpUntypedPtrAccessChainKHR: + if ((_.HasCapability(spv::Capability::VariablePointersStorageBuffer) && + sc == spv::StorageClass ::StorageBuffer) || + (_.HasCapability(spv::Capability::VariablePointers) && + sc == spv::StorageClass::Workgroup)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instruction may only return a logical pointer in the " + "StorageBuffer or Workgroup storage classes with appropriate " + "variable pointers capability"; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instruction may not return a logical pointer"; + } + + return SPV_SUCCESS; +} + +spv_result_t IsBlockArray(ValidationState_t& _, const Instruction* type) { + if (type->opcode() == spv::Op::OpTypeArray || + type->opcode() == spv::Op::OpTypeRuntimeArray) { + const auto element_type = _.FindDef(type->GetOperandAs<uint32_t>(1)); + if (element_type->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(element_type->id(), spv::Decoration::Block) || + _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) { + return SPV_ERROR_INVALID_DATA; + } + } + return SPV_SUCCESS; +} + +spv_result_t CheckMatrixElementTyped(ValidationState_t& _, + const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: { + // Get the type of the base operand. + uint32_t start_index = + inst->opcode() == spv::Op::OpPtrAccessChain ? 4 : 3; + const auto access_type_id = _.GetOperandTypeId(inst, 2); + auto access_type = _.FindDef(access_type_id); + access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(2)); + + // If the base operand is a matrix, then it was definitely pointing to a + // sub-component. + if (access_type->opcode() == spv::Op::OpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to a column or a " + "component of a column of a matrix"; + } + + // Otherwise, step through the indices to see if we pass a matrix. + for (uint32_t i = start_index; i < inst->operands().size(); ++i) { + const auto index = inst->GetOperandAs<uint32_t>(i); + if (access_type->opcode() == spv::Op::OpTypeStruct) { + uint64_t val = 0; + _.EvalConstantValUint64(index, &val); + access_type = _.FindDef(access_type->GetOperandAs<uint32_t>( + 1 + static_cast<uint32_t>(val))); + } else { + access_type = _.FindDef(_.GetComponentType(access_type->id())); + } + + if (access_type->opcode() == spv::Op::OpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to a column or a " + "component of a column of a matrix"; + } + } + break; + } + default: + break; + } + return SPV_SUCCESS; +} + +spv_result_t CheckMatrixElementUntyped(ValidationState_t& _, + const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: { + const bool untyped = spvOpcodeGeneratesUntypedPointer(inst->opcode()); + uint32_t start_index; + Instruction* access_type = nullptr; + if (untyped) { + // Get the type of the base operand. + start_index = + inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR ? 5 : 4; + const auto access_type_id = inst->GetOperandAs<uint32_t>(2); + access_type = _.FindDef(access_type_id); + } else { + start_index = inst->opcode() == spv::Op::OpPtrAccessChain ? 4 : 3; + const auto access_type_id = _.GetOperandTypeId(inst, 2); + access_type = _.FindDef(access_type_id); + access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(2)); + } + + // If the base operand is a matrix, then it was definitely pointing to a + // sub-component. + if (access_type->opcode() == spv::Op::OpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to a column or a " + "component of a column of a matrix."; + } + + // Otherwise, step through the indices to see if we pass a matrix. + for (uint32_t i = start_index; i < inst->operands().size(); ++i) { + const auto index = inst->GetOperandAs<uint32_t>(i); + if (access_type->opcode() == spv::Op::OpTypeStruct) { + uint64_t val = 0; + _.EvalConstantValUint64(index, &val); + access_type = _.FindDef(access_type->GetOperandAs<uint32_t>( + 1 + static_cast<uint32_t>(val))); + } else { + access_type = _.FindDef(_.GetComponentType(access_type->id())); + } + + if (access_type->opcode() == spv::Op::OpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to a column or a " + "component of a column of a matrix."; + } + } + break; + } + default: + break; + } + return SPV_SUCCESS; +} + +// Traces the variable pointer inst backwards. +// checker is called on each visited instruction. +spv_result_t TraceVariablePointers( + ValidationState_t& _, const Instruction* inst, + const std::function<spv_result_t(ValidationState_t&, const Instruction*)>& + checker) { + std::vector<const Instruction*> stack; + std::unordered_set<const Instruction*> seen; + stack.push_back(inst); + while (!stack.empty()) { + const Instruction* trace_inst = stack.back(); + stack.pop_back(); + + if (!seen.insert(trace_inst).second) { + continue; + } + + if (auto error = checker(_, trace_inst)) { + return error; + } + + const auto untyped = spvOpcodeGeneratesUntypedPointer(trace_inst->opcode()); + switch (trace_inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2))); + break; + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3))); + break; + case spv::Op::OpPhi: + for (uint32_t i = 2; i < trace_inst->operands().size(); i += 2) { + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(i))); + } + break; + case spv::Op::OpSelect: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3))); + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(4))); + break; + case spv::Op::OpFunctionParameter: { + // Jump to function calls + auto func = trace_inst->function(); + auto func_inst = _.FindDef(func->id()); + + const auto param_inst_num = trace_inst - &_.ordered_instructions()[0]; + uint32_t param_index = 0; + uint32_t inst_index = 1; + while (_.ordered_instructions()[param_inst_num - inst_index].opcode() != + spv::Op::OpFunction) { + if (_.ordered_instructions()[param_inst_num - inst_index].opcode() == + spv::Op::OpFunctionParameter) { + param_index++; + } + ++inst_index; + } + + for (const auto& use_pair : func_inst->uses()) { + const auto use_inst = use_pair.first; + if (use_inst->opcode() == spv::Op::OpFunctionCall) { + const auto arg_id = + use_inst->GetOperandAs<uint32_t>(3 + param_index); + const auto arg_inst = _.FindDef(arg_id); + stack.push_back(arg_inst); + } + } + break; + } + case spv::Op::OpFunctionCall: { + // Jump to return values. + const auto* func = _.function(trace_inst->GetOperandAs<uint32_t>(2)); + for (auto* bb : func->ordered_blocks()) { + const auto* terminator = bb->terminator(); + if (terminator->opcode() == spv::Op::OpReturnValue) { + stack.push_back(terminator); + } + } + break; + } + case spv::Op::OpReturnValue: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0))); + break; + case spv::Op::OpCopyObject: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2))); + break; + case spv::Op::OpLoad: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2))); + break; + case spv::Op::OpStore: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0))); + break; + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: { + const auto sc = trace_inst->GetOperandAs<spv::StorageClass>(2); + if (sc == spv::StorageClass::Function || + sc == spv::StorageClass::Private) { + // Add the initializer + const uint32_t init_operand = untyped ? 4 : 3; + if (trace_inst->operands().size() > init_operand) { + stack.push_back( + _.FindDef(trace_inst->GetOperandAs<uint32_t>(init_operand))); + } + // Jump to stores + std::vector<std::pair<const Instruction*, uint32_t>> store_stack( + trace_inst->uses()); + std::unordered_set<const Instruction*> store_seen; + while (!store_stack.empty()) { + const auto use = store_stack.back(); + store_stack.pop_back(); + + if (!store_seen.insert(use.first).second) { + continue; + } + + // If the use is a store pointer, trace the store object. + // Note: use.second is a word index. + if (use.first->opcode() == spv::Op::OpStore && use.second == 1) { + stack.push_back(_.FindDef(use.first->GetOperandAs<uint32_t>(1))); + } else { + // Most likely a gep so keep tracing. + for (auto& next_use : use.first->uses()) { + store_stack.push_back(next_use); + } + } + } + } + break; + } + default: + break; + } + } + + return SPV_SUCCESS; +} + +// Traces the variable pointer inst backwards, but only unmodified pointers. +// checker is called on each visited instruction. +spv_result_t TraceUnmodifiedVariablePointers( + ValidationState_t& _, const Instruction* inst, + const std::function<spv_result_t(ValidationState_t&, const Instruction*)>& + checker) { + std::vector<const Instruction*> stack; + std::unordered_set<const Instruction*> seen; + stack.push_back(inst); + while (!stack.empty()) { + const Instruction* trace_inst = stack.back(); + stack.pop_back(); + + if (!seen.insert(trace_inst).second) { + continue; + } + + if (auto error = checker(_, trace_inst)) { + return error; + } + + const auto untyped = spvOpcodeGeneratesUntypedPointer(trace_inst->opcode()); + switch (trace_inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + if (trace_inst->operands().size() == 2) { + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2))); + } + break; + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + if (trace_inst->operands().size() == 3) { + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3))); + } + break; + case spv::Op::OpPhi: + for (uint32_t i = 2; i < trace_inst->operands().size(); i += 2) { + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(i))); + } + break; + case spv::Op::OpSelect: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3))); + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(4))); + break; + case spv::Op::OpFunctionParameter: { + // Jump to function calls + auto func = trace_inst->function(); + auto func_inst = _.FindDef(func->id()); + + const auto param_inst_num = trace_inst - &_.ordered_instructions()[0]; + uint32_t param_index = 0; + uint32_t inst_index = 1; + while (_.ordered_instructions()[param_inst_num - inst_index].opcode() != + spv::Op::OpFunction) { + if (_.ordered_instructions()[param_inst_num - inst_index].opcode() == + spv::Op::OpFunctionParameter) { + param_index++; + } + ++inst_index; + } + + for (const auto& use_pair : func_inst->uses()) { + const auto use_inst = use_pair.first; + if (use_inst->opcode() == spv::Op::OpFunctionCall) { + const auto arg_id = + use_inst->GetOperandAs<uint32_t>(3 + param_index); + const auto arg_inst = _.FindDef(arg_id); + stack.push_back(arg_inst); + } + } + break; + } + case spv::Op::OpFunctionCall: { + // Jump to return values. + const auto* func = _.function(trace_inst->GetOperandAs<uint32_t>(2)); + for (auto* bb : func->ordered_blocks()) { + const auto* terminator = bb->terminator(); + if (terminator->opcode() == spv::Op::OpReturnValue) { + stack.push_back(terminator); + } + } + break; + } + case spv::Op::OpReturnValue: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0))); + break; + case spv::Op::OpCopyObject: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2))); + break; + case spv::Op::OpLoad: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2))); + break; + case spv::Op::OpStore: + stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0))); + break; + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: { + const auto sc = trace_inst->GetOperandAs<spv::StorageClass>(2); + if (sc == spv::StorageClass::Function || + sc == spv::StorageClass::Private) { + // Add the initializer + const uint32_t init_operand = untyped ? 4 : 3; + if (trace_inst->operands().size() > init_operand) { + stack.push_back( + _.FindDef(trace_inst->GetOperandAs<uint32_t>(init_operand))); + } + // Jump to stores + std::vector<std::pair<const Instruction*, uint32_t>> store_stack( + trace_inst->uses()); + std::unordered_set<const Instruction*> store_seen; + while (!store_stack.empty()) { + const auto use = store_stack.back(); + store_stack.pop_back(); + + if (!store_seen.insert(use.first).second) { + continue; + } + + // If the use is a store pointer, trace the store object. + // Note: use.second is a word index. + if (use.first->opcode() == spv::Op::OpStore && use.second == 1) { + stack.push_back(_.FindDef(use.first->GetOperandAs<uint32_t>(1))); + } else { + // Most likely a gep so keep tracing. + for (auto& next_use : use.first->uses()) { + store_stack.push_back(next_use); + } + } + } + } + break; + } + default: + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariablePointers( + ValidationState_t& _, std::unordered_map<uint32_t, bool>& variable_pointers, + const Instruction* inst) { + // Variable pointers cannot be operands to array length. + if (inst->opcode() == spv::Op::OpArrayLength || + inst->opcode() == spv::Op::OpUntypedArrayLengthKHR) { + const auto ptr_index = inst->opcode() == spv::Op::OpArrayLength ? 2 : 3; + const auto ptr_id = inst->GetOperandAs<uint32_t>(ptr_index); + const auto ptr_inst = _.FindDef(ptr_id); + if (IsVariablePointer(_, variable_pointers, ptr_inst)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer operand must not be a variable pointer"; + } + return SPV_SUCCESS; + } + + // Check untyped loads and stores of variable pointers for matrix types. + // Neither instruction would be a variable pointer in a such a case. + if (inst->opcode() == spv::Op::OpLoad) { + const auto pointer = _.FindDef(inst->GetOperandAs<uint32_t>(2)); + const auto pointer_type = _.FindDef(pointer->type_id()); + if (pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR && + IsVariablePointer(_, variable_pointers, pointer)) { + const auto data_type = _.FindDef(inst->type_id()); + if (_.ContainsType( + data_type->id(), + [](const Instruction* type_inst) { + return type_inst->opcode() == spv::Op::OpTypeMatrix; + }, + /* traverse_all_types = */ false)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to an object that is or " + "contains a matrix"; + } + } + } else if (inst->opcode() == spv::Op::OpStore) { + const auto pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0)); + const auto pointer_type = _.FindDef(pointer->type_id()); + if (pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR && + IsVariablePointer(_, variable_pointers, pointer)) { + const auto data_type_id = _.GetOperandTypeId(inst, 1); + const auto data_type = _.FindDef(data_type_id); + if (_.ContainsType( + data_type->id(), + [](const Instruction* type_inst) { + return type_inst->opcode() == spv::Op::OpTypeMatrix; + }, + /* traverse_all_types = */ false)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to an object that is or " + "contains a matrix"; + } + } + } + + if (!IsLogicalPointer(_, inst) || + !IsVariablePointer(_, variable_pointers, inst)) { + return SPV_SUCCESS; + } + + const auto result_type = _.FindDef(inst->type_id()); + const auto untyped = + result_type->opcode() == spv::Op::OpTypeUntypedPointerKHR; + + // Pointers must be selected from the same buffer unless the VariablePointers + // capability is declared. + if (!_.HasCapability(spv::Capability::VariablePointers) && + (inst->opcode() == spv::Op::OpSelect || + inst->opcode() == spv::Op::OpPhi)) { + std::unordered_set<const Instruction*> sources; + const auto checker = [&sources, &inst]( + ValidationState_t& vstate, + const Instruction* check_inst) -> spv_result_t { + switch (check_inst->opcode()) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: + if (check_inst->GetOperandAs<spv::StorageClass>(2) == + spv::StorageClass::StorageBuffer || + check_inst->GetOperandAs<spv::StorageClass>(2) == + spv::StorageClass::Workgroup) { + sources.insert(check_inst); + } + if (sources.size() > 1) { + return vstate.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointers must point into the same structure " + "(or OpConstantNull)"; + } + break; + default: + break; + } + return SPV_SUCCESS; + }; + if (auto error = TraceVariablePointers(_, inst, checker)) { + return error; + } + } + + // Variable pointers must not: + // * point to array of Block- or BufferBlock-decorated structs + // * point to an object that is or contains a matrix + // * point to a column, or component in a column, of a matrix + if (untyped) { + if (auto error = + TraceVariablePointers(_, inst, CheckMatrixElementUntyped)) { + return error; + } + + // Block arrays can only really appear as the top most type so only look at + // unmodified pointers to determine if one is used. + const auto num_operands = inst->operands().size(); + if (!(num_operands == 3 && + (inst->opcode() == spv::Op::OpUntypedAccessChainKHR || + inst->opcode() == spv::Op::OpUntypedInBoundsAccessChainKHR || + inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR))) { + const auto checker = [&inst]( + ValidationState_t& vstate, + const Instruction* check_inst) -> spv_result_t { + bool fail = false; + if (check_inst->opcode() == spv::Op::OpUntypedVariableKHR) { + if (check_inst->operands().size() > 3) { + const auto type = + vstate.FindDef(check_inst->GetOperandAs<uint32_t>(3)); + fail = IsBlockArray(vstate, type); + } + } else if (check_inst->opcode() == spv::Op::OpVariable) { + const auto res_type = vstate.FindDef(check_inst->type_id()); + const auto pointee_type = + vstate.FindDef(res_type->GetOperandAs<uint32_t>(2)); + fail = IsBlockArray(vstate, pointee_type); + } + + if (fail) { + return vstate.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to an array of Block- or " + "BufferBlock-decorated structs"; + } + return SPV_SUCCESS; + }; + + if (auto error = TraceUnmodifiedVariablePointers(_, inst, checker)) { + return error; + } + } + } else { + const auto pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); + if (IsBlockArray(_, pointee_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to an array of Block- or " + "BufferBlock-decorated structs"; + } else if (_.ContainsType( + pointee_type->id(), + [](const Instruction* type_inst) { + return type_inst->opcode() == spv::Op::OpTypeMatrix; + }, + /* traverse_all_types = */ false)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Variable pointer must not point to an object that is or " + "contains a matrix"; + } else if (_.IsFloatScalarOrVectorType(pointee_type->id())) { + // Pointing to a column or component in a column is trickier to detect. + // Trace backwards and check encountered access chains to determine if + // this pointer is pointing into a matrix. + if (auto error = + TraceVariablePointers(_, inst, CheckMatrixElementTyped)) { + return error; + } + } + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateLogicalPointers(ValidationState_t& _) { + // Only the following addressing models have logical pointers. + if (_.addressing_model() != spv::AddressingModel::Logical && + _.addressing_model() != spv::AddressingModel::PhysicalStorageBuffer64) { + return SPV_SUCCESS; + } + + if (_.options()->relax_logical_pointer) { + return SPV_SUCCESS; + } + + // Cache all variable pointers + std::unordered_map<uint32_t, bool> variable_pointers; + for (auto& inst : _.ordered_instructions()) { + if (!IsLogicalPointer(_, &inst)) { + continue; + } + + IsVariablePointer(_, variable_pointers, &inst); + } + + for (auto& inst : _.ordered_instructions()) { + if (auto error = ValidateLogicalPointerOperands(_, &inst)) { + return error; + } + if (auto error = ValidateLogicalPointerReturns(_, &inst)) { + return error; + } + if (auto error = ValidateVariablePointers(_, variable_pointers, &inst)) { + return error; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp index 8a2e5d8..5f3161a 100644 --- a/source/val/validate_logicals.cpp +++ b/source/val/validate_logicals.cpp
@@ -22,54 +22,258 @@ namespace spvtools { namespace val { -// Validates correctness of logical instructions. -spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { +spv_result_t ValidateAnyAll(ValidationState_t& _, const Instruction* inst) { const spv::Op opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar type as Result Type: " + << spvOpcodeString(opcode); - switch (opcode) { - case spv::Op::OpAny: - case spv::Op::OpAll: { - if (!_.IsBoolScalarType(result_type)) + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + if (!vector_type || !_.IsBoolVectorType(vector_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be vector bool: " << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateSingleStatus(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (!operand_type || (!_.IsFloatScalarType(operand_type) && + !_.IsFloatVectorType(operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operand to be " + "equal: " + << spvOpcodeString(opcode); + + return SPV_SUCCESS; +} + +spv_result_t ValidateFloatCompare(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2); + if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) && + !_.IsFloatVectorType(left_operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be " + "equal: " + << spvOpcodeString(opcode); + + if (left_operand_type != _.GetOperandTypeId(inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected left and right operands to have the same type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateLogicalCompare(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t operand_1 = _.GetOperandTypeId(inst, operand_index); + const uint32_t operand_2 = _.GetOperandTypeId(inst, operand_index + 1); + if (result_type != operand_1 || result_type != operand_2) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to be of Result Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateLogicalNot(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != _.GetOperandTypeId(inst, operand_index)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be of Result Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateSelect(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + uint32_t dimension = 1; + const Instruction* type_inst = _.FindDef(result_type); + assert(type_inst); + + const auto composites = _.features().select_between_composites; + auto fail = [&_, composites, inst, opcode]() -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar or " << (composites ? "composite" : "vector") + << " type as Result Type: " << spvOpcodeString(opcode); + }; + + const spv::Op type_opcode = type_inst->opcode(); + switch (type_opcode) { + case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypePointer: { + if (_.addressing_model() == spv::AddressingModel::Logical && + !_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t vector_type = _.GetOperandTypeId(inst, 2); - if (!vector_type || !_.IsBoolVectorType(vector_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operand to be vector bool: " - << spvOpcodeString(opcode); - + << "Using pointers with OpSelect requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; break; } + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: { + if (!_.HasCapability(spv::Capability::BindlessTextureNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using image/sampler with OpSelect requires capability " + << "BindlessTextureNV"; + break; + } + + case spv::Op::OpTypeVector: { + dimension = type_inst->word(3); + break; + } + case spv::Op::OpTypeVectorIdEXT: { + dimension = _.GetDimension(result_type); + break; + } + + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: { + break; + } + + // Not RuntimeArray because of other rules. + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeStruct: { + if (!composites) return fail(); + break; + } + + default: + return fail(); + } + + const uint32_t condition_type = _.GetOperandTypeId(inst, operand_index); + const uint32_t left_type = _.GetOperandTypeId(inst, operand_index + 1); + const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 2); + + if (!condition_type || (!_.IsBoolScalarType(condition_type) && + !_.IsBoolVectorType(condition_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as condition: " + << spvOpcodeString(opcode); + + if (_.GetDimension(condition_type) != dimension) { + // If the condition is a vector type, then the result must also be a + // vector with matching dimensions. In SPIR-V 1.4, a scalar condition + // can be used to select between vector types. |composites| is a + // proxy for SPIR-V 1.4 functionality. + if (!composites || _.IsBoolVectorType(condition_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the condition " + "to be equal: " + << spvOpcodeString(opcode); + } + } + + if (result_type != left_type || result_type != right_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both objects to be of Result Type: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +spv_result_t ValidateIntCompare(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_type = _.GetOperandTypeId(inst, operand_index); + const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 1); + + if (!left_type || + (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (!right_type || + (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to have the same component bit " + "width: " + << spvOpcodeString(opcode); + return SPV_SUCCESS; +} + +// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpAny: + case spv::Op::OpAll: + return ValidateAnyAll(_, inst); case spv::Op::OpIsNan: case spv::Op::OpIsInf: case spv::Op::OpIsFinite: case spv::Op::OpIsNormal: - case spv::Op::OpSignBitSet: { - if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t operand_type = _.GetOperandTypeId(inst, 2); - if (!operand_type || (!_.IsFloatScalarType(operand_type) && - !_.IsFloatVectorType(operand_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operand to be scalar or vector float: " - << spvOpcodeString(opcode); - - if (_.GetDimension(result_type) != _.GetDimension(operand_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected vector sizes of Result Type and the operand to be " - "equal: " - << spvOpcodeString(opcode); - - break; - } - + case spv::Op::OpSignBitSet: + return ValidateSingleStatus(_, inst); case spv::Op::OpFOrdEqual: case spv::Op::OpFUnordEqual: case spv::Op::OpFOrdNotEqual: @@ -84,157 +288,17 @@ case spv::Op::OpFUnordGreaterThanEqual: case spv::Op::OpLessOrGreater: case spv::Op::OpOrdered: - case spv::Op::OpUnordered: { - if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2); - if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) && - !_.IsFloatVectorType(left_operand_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to be scalar or vector float: " - << spvOpcodeString(opcode); - - if (_.GetDimension(result_type) != _.GetDimension(left_operand_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected vector sizes of Result Type and the operands to be " - "equal: " - << spvOpcodeString(opcode); - - if (left_operand_type != _.GetOperandTypeId(inst, 3)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected left and right operands to have the same type: " - << spvOpcodeString(opcode); - - break; - } - + case spv::Op::OpUnordered: + return ValidateFloatCompare(_, inst); case spv::Op::OpLogicalEqual: case spv::Op::OpLogicalNotEqual: case spv::Op::OpLogicalOr: - case spv::Op::OpLogicalAnd: { - if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - if (result_type != _.GetOperandTypeId(inst, 2) || - result_type != _.GetOperandTypeId(inst, 3)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected both operands to be of Result Type: " - << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpLogicalNot: { - if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - - if (result_type != _.GetOperandTypeId(inst, 2)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operand to be of Result Type: " - << spvOpcodeString(opcode); - - break; - } - - case spv::Op::OpSelect: { - uint32_t dimension = 1; - { - const Instruction* type_inst = _.FindDef(result_type); - assert(type_inst); - - const auto composites = _.features().select_between_composites; - auto fail = [&_, composites, inst, opcode]() -> spv_result_t { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected scalar or " - << (composites ? "composite" : "vector") - << " type as Result Type: " << spvOpcodeString(opcode); - }; - - const spv::Op type_opcode = type_inst->opcode(); - switch (type_opcode) { - case spv::Op::OpTypeUntypedPointerKHR: - case spv::Op::OpTypePointer: { - if (_.addressing_model() == spv::AddressingModel::Logical && - !_.HasCapability( - spv::Capability::VariablePointersStorageBuffer)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Using pointers with OpSelect requires capability " - << "VariablePointers or VariablePointersStorageBuffer"; - break; - } - - case spv::Op::OpTypeSampledImage: - case spv::Op::OpTypeImage: - case spv::Op::OpTypeSampler: { - if (!_.HasCapability(spv::Capability::BindlessTextureNV)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Using image/sampler with OpSelect requires capability " - << "BindlessTextureNV"; - break; - } - - case spv::Op::OpTypeVector: { - dimension = type_inst->word(3); - break; - } - - case spv::Op::OpTypeBool: - case spv::Op::OpTypeInt: - case spv::Op::OpTypeFloat: { - break; - } - - // Not RuntimeArray because of other rules. - case spv::Op::OpTypeArray: - case spv::Op::OpTypeMatrix: - case spv::Op::OpTypeStruct: { - if (!composites) return fail(); - break; - } - - default: - return fail(); - } - - const uint32_t condition_type = _.GetOperandTypeId(inst, 2); - const uint32_t left_type = _.GetOperandTypeId(inst, 3); - const uint32_t right_type = _.GetOperandTypeId(inst, 4); - - if (!condition_type || (!_.IsBoolScalarType(condition_type) && - !_.IsBoolVectorType(condition_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar or vector type as condition: " - << spvOpcodeString(opcode); - - if (_.GetDimension(condition_type) != dimension) { - // If the condition is a vector type, then the result must also be a - // vector with matching dimensions. In SPIR-V 1.4, a scalar condition - // can be used to select between vector types. |composites| is a - // proxy for SPIR-V 1.4 functionality. - if (!composites || _.IsBoolVectorType(condition_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected vector sizes of Result Type and the condition " - "to be equal: " - << spvOpcodeString(opcode); - } - } - - if (result_type != left_type || result_type != right_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected both objects to be of Result Type: " - << spvOpcodeString(opcode); - - break; - } - } - + case spv::Op::OpLogicalAnd: + return ValidateLogicalCompare(_, inst); + case spv::Op::OpLogicalNot: + return ValidateLogicalNot(_, inst); + case spv::Op::OpSelect: + return ValidateSelect(_, inst); case spv::Op::OpIEqual: case spv::Op::OpINotEqual: case spv::Op::OpUGreaterThan: @@ -244,43 +308,34 @@ case spv::Op::OpSGreaterThan: case spv::Op::OpSGreaterThanEqual: case spv::Op::OpSLessThan: - case spv::Op::OpSLessThanEqual: { - if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected bool scalar or vector type as Result Type: " - << spvOpcodeString(opcode); + case spv::Op::OpSLessThanEqual: + return ValidateIntCompare(_, inst); - const uint32_t left_type = _.GetOperandTypeId(inst, 2); - const uint32_t right_type = _.GetOperandTypeId(inst, 3); - - if (!left_type || - (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to be scalar or vector int: " - << spvOpcodeString(opcode); - - if (_.GetDimension(result_type) != _.GetDimension(left_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected vector sizes of Result Type and the operands to be" - << " equal: " << spvOpcodeString(opcode); - - if (!right_type || - (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to be scalar or vector int: " - << spvOpcodeString(opcode); - - if (_.GetDimension(result_type) != _.GetDimension(right_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected vector sizes of Result Type and the operands to be" - << " equal: " << spvOpcodeString(opcode); - - if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected both operands to have the same component bit " - "width: " - << spvOpcodeString(opcode); - + case spv::Op::OpSpecConstantOp: { + switch (inst->GetOperandAs<spv::Op>(2u)) { + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + return ValidateLogicalCompare(_, inst, 3); + case spv::Op::OpLogicalNot: + return ValidateLogicalNot(_, inst, 3); + case spv::Op::OpSelect: + return ValidateSelect(_, inst, 3); + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSGreaterThan: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpSLessThan: + case spv::Op::OpSLessThanEqual: + return ValidateIntCompare(_, inst, 3); + default: + break; + } break; }
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index 32f6427..47d1a3c 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp
@@ -1,6 +1,6 @@ // Copyright (c) 2018 Google LLC. -// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights -// reserved. +// Modifications Copyright (C) 2020-2024 Advanced Micro Devices, Inc. All +// rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,11 +15,13 @@ // limitations under the License. #include <algorithm> +#include <cstdint> #include <string> #include <vector> #include "source/opcode.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validate_scopes.h" @@ -38,15 +40,15 @@ bool HasConflictingMemberOffsets(const std::set<Decoration>&, const std::set<Decoration>&); -bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type, +bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction& type, std::initializer_list<spv::Op> allowed) { - if (std::find(allowed.begin(), allowed.end(), type->opcode()) != + if (std::find(allowed.begin(), allowed.end(), type.opcode()) != allowed.end()) { return true; } - if (type->opcode() == spv::Op::OpTypeArray || - type->opcode() == spv::Op::OpTypeRuntimeArray) { - auto elem_type = _.FindDef(type->word(2)); + if (type.opcode() == spv::Op::OpTypeArray || + type.opcode() == spv::Op::OpTypeRuntimeArray) { + auto elem_type = _.FindDef(type.word(2)); return std::find(allowed.begin(), allowed.end(), elem_type->opcode()) != allowed.end(); } @@ -196,75 +198,43 @@ return false; } -bool ContainsCooperativeMatrix(ValidationState_t& _, - const Instruction* storage) { - const size_t elem_type_index = 1; - uint32_t elem_type_id; - Instruction* elem_type; - - switch (storage->opcode()) { - case spv::Op::OpTypeCooperativeMatrixNV: - case spv::Op::OpTypeCooperativeMatrixKHR: - return true; - case spv::Op::OpTypeArray: - case spv::Op::OpTypeRuntimeArray: - elem_type_id = storage->GetOperandAs<uint32_t>(elem_type_index); - elem_type = _.FindDef(elem_type_id); - return ContainsCooperativeMatrix(_, elem_type); - case spv::Op::OpTypeStruct: - for (size_t member_type_index = 1; - member_type_index < storage->operands().size(); - ++member_type_index) { - auto member_type_id = - storage->GetOperandAs<uint32_t>(member_type_index); - auto member_type = _.FindDef(member_type_id); - if (ContainsCooperativeMatrix(_, member_type)) return true; - } - break; - default: - break; - } - return false; -} - -std::pair<spv::StorageClass, spv::StorageClass> GetStorageClass( - ValidationState_t& _, const Instruction* inst) { - spv::StorageClass dst_sc = spv::StorageClass::Max; - spv::StorageClass src_sc = spv::StorageClass::Max; +std::pair<Instruction*, Instruction*> GetPointerTypes(ValidationState_t& _, + const Instruction* inst) { + Instruction* dst_pointer_type = nullptr; + Instruction* src_pointer_type = nullptr; switch (inst->opcode()) { case spv::Op::OpCooperativeMatrixLoadNV: case spv::Op::OpCooperativeMatrixLoadTensorNV: case spv::Op::OpCooperativeMatrixLoadKHR: + case spv::Op::OpCooperativeVectorLoadNV: case spv::Op::OpLoad: { auto load_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(2)); - auto load_pointer_type = _.FindDef(load_pointer->type_id()); - dst_sc = load_pointer_type->GetOperandAs<spv::StorageClass>(1); + dst_pointer_type = _.FindDef(load_pointer->type_id()); break; } case spv::Op::OpCooperativeMatrixStoreNV: case spv::Op::OpCooperativeMatrixStoreTensorNV: case spv::Op::OpCooperativeMatrixStoreKHR: + case spv::Op::OpCooperativeVectorStoreNV: case spv::Op::OpStore: { auto store_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0)); - auto store_pointer_type = _.FindDef(store_pointer->type_id()); - dst_sc = store_pointer_type->GetOperandAs<spv::StorageClass>(1); + dst_pointer_type = _.FindDef(store_pointer->type_id()); break; } + // Spec: "Matching Storage Class is not required" case spv::Op::OpCopyMemory: case spv::Op::OpCopyMemorySized: { - auto dst = _.FindDef(inst->GetOperandAs<uint32_t>(0)); - auto dst_type = _.FindDef(dst->type_id()); - dst_sc = dst_type->GetOperandAs<spv::StorageClass>(1); - auto src = _.FindDef(inst->GetOperandAs<uint32_t>(1)); - auto src_type = _.FindDef(src->type_id()); - src_sc = src_type->GetOperandAs<spv::StorageClass>(1); + auto dst_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0)); + dst_pointer_type = _.FindDef(dst_pointer->type_id()); + auto src_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(1)); + src_pointer_type = _.FindDef(src_pointer->type_id()); break; } default: break; } - return std::make_pair(dst_sc, src_sc); + return std::make_pair(dst_pointer_type, src_pointer_type); } // Returns the number of instruction words taken up by a memory access @@ -280,8 +250,9 @@ // Returns the scope ID operand for MakeAvailable memory access with mask // at the given operand index. // This function is only called for OpLoad, OpStore, OpCopyMemory and -// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and -// OpCooperativeMatrixStoreNV. +// OpCopyMemorySized, OpCooperativeMatrixLoadNV, +// OpCooperativeMatrixStoreNV, OpCooperativeVectorLoadNV, +// OpCooperativeVectorStoreNV. uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask, uint32_t mask_index) { assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)); @@ -292,8 +263,9 @@ } // This function is only called for OpLoad, OpStore, OpCopyMemory, -// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and -// OpCooperativeMatrixStoreNV. +// OpCopyMemorySized, OpCooperativeMatrixLoadNV, +// OpCooperativeMatrixStoreNV, OpCooperativeVectorLoadNV, +// OpCooperativeVectorStoreNV. uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask, uint32_t mask_index) { assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)); @@ -315,8 +287,17 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, uint32_t index) { - spv::StorageClass dst_sc, src_sc; - std::tie(dst_sc, src_sc) = GetStorageClass(_, inst); + Instruction* dst_pointer_type = nullptr; + Instruction* src_pointer_type = nullptr; // only used for OpCopyMemory + std::tie(dst_pointer_type, src_pointer_type) = GetPointerTypes(_, inst); + + const spv::StorageClass dst_sc = + dst_pointer_type ? dst_pointer_type->GetOperandAs<spv::StorageClass>(1) + : spv::StorageClass::Max; + const spv::StorageClass src_sc = + src_pointer_type ? src_pointer_type->GetOperandAs<spv::StorageClass>(1) + : spv::StorageClass::Max; + if (inst->operands().size() <= index) { // Cases where lack of some operand is invalid if (src_sc == spv::StorageClass::PhysicalStorageBuffer || @@ -333,7 +314,8 @@ if (inst->opcode() == spv::Op::OpLoad || inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV || inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV || - inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) { + inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR || + inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "MakePointerAvailableKHR cannot be used with OpLoad."; } @@ -354,7 +336,8 @@ if (inst->opcode() == spv::Op::OpStore || inst->opcode() == spv::Op::OpCooperativeMatrixStoreNV || inst->opcode() == spv::Op::OpCooperativeMatrixStoreKHR || - inst->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV) { + inst->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV || + inst->opcode() == spv::Op::OpCooperativeVectorStoreNV) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "MakePointerVisibleKHR cannot be used with OpStore."; } @@ -405,74 +388,65 @@ << _.VkErrorID(4708) << "Memory accesses with PhysicalStorageBuffer must use Aligned."; } + } else { + // even if there are other masks, the Aligned operand will be next + const uint32_t aligned_value = inst->GetOperandAs<uint32_t>(index + 1); + const bool is_power_of_two = + aligned_value && !(aligned_value & (aligned_value - 1)); + if (!is_power_of_two) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Memory accesses Aligned operand value " << aligned_value + << " is not a power of two."; + } + + uint32_t largest_scalar = 0; + if (dst_sc == spv::StorageClass::PhysicalStorageBuffer) { + if (dst_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + largest_scalar = + _.GetLargestScalarType(dst_pointer_type->GetOperandAs<uint32_t>(2)); + } else if (inst->type_id() != 0) { + largest_scalar = _.GetLargestScalarType(inst->type_id()); + } else { + // TODO need to handle cases like OpStore and OpCopyMemorySized which + // don't have a result type + } + } + // TODO - Handle Untyped in OpCopyMemory + if (src_sc == spv::StorageClass::PhysicalStorageBuffer && + src_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + largest_scalar = std::max( + largest_scalar, + _.GetLargestScalarType(src_pointer_type->GetOperandAs<uint32_t>(2))); + } + if (aligned_value < largest_scalar) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6314) << "Memory accesses Aligned operand value " + << aligned_value << " is too small, the largest scalar type is " + << largest_scalar << " bytes."; + } } return SPV_SUCCESS; } -spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { +spv_result_t ValidateVariableInitializer(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + uint32_t value_id) { const bool untyped_pointer = inst->opcode() == spv::Op::OpUntypedVariableKHR; - - auto result_type = _.FindDef(inst->type_id()); - if (untyped_pointer) { - if (!result_type || - result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Result type must be an untyped pointer"; - } else { - if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable Result Type <id> " << _.getIdName(inst->type_id()) - << " is not a pointer type."; - } - } - - const auto storage_class_index = 2u; - auto storage_class = - inst->GetOperandAs<spv::StorageClass>(storage_class_index); - uint32_t value_id = 0; - if (untyped_pointer) { - const auto has_data_type = 3u < inst->operands().size(); - if (has_data_type) { - value_id = inst->GetOperandAs<uint32_t>(3u); - auto data_type = _.FindDef(value_id); - if (!data_type || !spvOpcodeGeneratesType(data_type->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Data type must be a type instruction"; - } - } else { - if (storage_class == spv::StorageClass::Function || - storage_class == spv::StorageClass::Private || - storage_class == spv::StorageClass::Workgroup) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Data type must be specified for Function, Private, and " - "Workgroup storage classes"; - } - if (spvIsVulkanEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Vulkan requires that data type be specified"; - } - } - } - - // For OpVariable the data type comes from pointee type of the result type, - // while for OpUntypedVariableKHR the data type comes from the operand. - if (!untyped_pointer) { - value_id = result_type->GetOperandAs<uint32_t>(2); - } - auto value_type = value_id == 0 ? nullptr : _.FindDef(value_id); - - const auto initializer_index = untyped_pointer ? 4u : 3u; + const uint32_t initializer_index = untyped_pointer ? 4u : 3u; if (initializer_index < inst->operands().size()) { - const auto initializer_id = inst->GetOperandAs<uint32_t>(initializer_index); - const auto initializer = _.FindDef(initializer_id); - const auto is_module_scope_var = + const uint32_t initializer_id = + inst->GetOperandAs<uint32_t>(initializer_index); + const Instruction* initializer = _.FindDef(initializer_id); + const uint32_t storage_class_index = 2u; + const bool is_module_scope_var = initializer && (initializer->opcode() == spv::Op::OpVariable || initializer->opcode() == spv::Op::OpUntypedVariableKHR) && (initializer->GetOperandAs<spv::StorageClass>(storage_class_index) != spv::StorageClass::Function); - const auto is_constant = + const bool is_constant = initializer && spvOpcodeIsConstant(initializer->opcode()); if (!initializer || !(is_constant || is_module_scope_var)) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -485,188 +459,6 @@ } } - if (storage_class != spv::StorageClass::Workgroup && - storage_class != spv::StorageClass::CrossWorkgroup && - storage_class != spv::StorageClass::Private && - storage_class != spv::StorageClass::Function && - storage_class != spv::StorageClass::UniformConstant && - storage_class != spv::StorageClass::RayPayloadKHR && - storage_class != spv::StorageClass::IncomingRayPayloadKHR && - storage_class != spv::StorageClass::HitAttributeKHR && - storage_class != spv::StorageClass::CallableDataKHR && - storage_class != spv::StorageClass::IncomingCallableDataKHR && - storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT && - storage_class != spv::StorageClass::HitObjectAttributeNV) { - bool storage_input_or_output = storage_class == spv::StorageClass::Input || - storage_class == spv::StorageClass::Output; - bool builtin = false; - if (storage_input_or_output) { - for (const Decoration& decoration : _.id_decorations(inst->id())) { - if (decoration.dec_type() == spv::Decoration::BuiltIn) { - builtin = true; - break; - } - } - } - if (!builtin && value_type && - ContainsInvalidBool(_, value_type, storage_input_or_output)) { - if (storage_input_or_output) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(7290) - << "If OpTypeBool is stored in conjunction with OpVariable " - "using Input or Output Storage Classes it requires a BuiltIn " - "decoration"; - - } else { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "If OpTypeBool is stored in conjunction with OpVariable, it " - "can only be used with non-externally visible shader Storage " - "Classes: Workgroup, CrossWorkgroup, Private, Function, " - "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, " - "HitAttributeKHR, CallableDataKHR, " - "IncomingCallableDataKHR, or UniformConstant"; - } - } - } - - if (!_.IsValidStorageClass(storage_class)) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << _.VkErrorID(4643) - << "Invalid storage class for target environment"; - } - - if (storage_class == spv::StorageClass::Generic) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "Variable storage class cannot be Generic"; - } - - if (inst->function() && storage_class != spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Variables must have a function[7] storage class inside" - " of a function"; - } - - if (!inst->function() && storage_class == spv::StorageClass::Function) { - return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) - << "Variables can not have a function[7] storage class " - "outside of a function"; - } - - // SPIR-V 3.32.8: Check that pointer type and variable type have the same - // storage class. - const auto result_storage_class_index = 1; - const auto result_storage_class = - result_type->GetOperandAs<spv::StorageClass>(result_storage_class_index); - if (storage_class != result_storage_class) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Storage class must match result type storage class"; - } - - // Variable pointer related restrictions. - const auto pointee = untyped_pointer - ? value_id == 0 ? nullptr : _.FindDef(value_id) - : _.FindDef(result_type->word(3)); - if (_.addressing_model() == spv::AddressingModel::Logical && - !_.options()->relax_logical_pointer) { - // VariablePointersStorageBuffer is implied by VariablePointers. - if (pointee && pointee->opcode() == spv::Op::OpTypePointer) { - if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "In Logical addressing, variables may not allocate a pointer " - << "type"; - } else if (storage_class != spv::StorageClass::Function && - storage_class != spv::StorageClass::Private) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "In Logical addressing with variable pointers, variables " - << "that allocate pointers must be in Function or Private " - << "storage classes"; - } - } - } - - if (spvIsVulkanEnv(_.context()->target_env)) { - // Vulkan Push Constant Interface section: Check type of PushConstant - // variables. - if (storage_class == spv::StorageClass::PushConstant) { - if (pointee && pointee->opcode() != spv::Op::OpTypeStruct) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(6808) << "PushConstant OpVariable <id> " - << _.getIdName(inst->id()) << " has illegal type.\n" - << "From Vulkan spec, Push Constant Interface section:\n" - << "Such variables must be typed as OpTypeStruct"; - } - } - - // Vulkan Descriptor Set Interface: Check type of UniformConstant and - // Uniform variables. - if (storage_class == spv::StorageClass::UniformConstant) { - if (pointee && !IsAllowedTypeOrArrayOfSame( - _, pointee, - {spv::Op::OpTypeImage, spv::Op::OpTypeSampler, - spv::Op::OpTypeSampledImage, - spv::Op::OpTypeAccelerationStructureKHR})) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4655) << "UniformConstant OpVariable <id> " - << _.getIdName(inst->id()) << " has illegal type.\n" - << "Variables identified with the UniformConstant storage class " - << "are used only as handles to refer to opaque resources. Such " - << "variables must be typed as OpTypeImage, OpTypeSampler, " - << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " - << "or an array of one of these types."; - } - } - - if (storage_class == spv::StorageClass::Uniform) { - if (pointee && - !IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(6807) << "Uniform OpVariable <id> " - << _.getIdName(inst->id()) << " has illegal type.\n" - << "From Vulkan spec:\n" - << "Variables identified with the Uniform storage class are " - << "used to access transparent buffer backed resources. Such " - << "variables must be typed as OpTypeStruct, or an array of " - << "this type"; - } - } - - if (storage_class == spv::StorageClass::StorageBuffer) { - if (pointee && - !IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> " - << _.getIdName(inst->id()) << " has illegal type.\n" - << "From Vulkan spec:\n" - << "Variables identified with the StorageBuffer storage class " - "are used to access transparent buffer backed resources. " - "Such variables must be typed as OpTypeStruct, or an array " - "of this type"; - } - } - - // Check for invalid use of Invariant - if (storage_class != spv::StorageClass::Input && - storage_class != spv::StorageClass::Output) { - if (_.HasDecoration(inst->id(), spv::Decoration::Invariant)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4677) - << "Variable decorated with Invariant must only be identified " - "with the Input or Output storage class in Vulkan " - "environment."; - } - // Need to check if only the members in a struct are decorated - if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) { - if (_.HasDecoration(value_id, spv::Decoration::Invariant)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4677) - << "Variable struct member decorated with Invariant must only " - "be identified with the Input or Output storage class in " - "Vulkan environment."; - } - } - } - } - // Vulkan Appendix A: Check that if contains initializer, then // storage class is Output, Private, or Function. if (inst->operands().size() > initializer_index && @@ -718,108 +510,275 @@ << "OpVariable, <id> " << _.getIdName(inst->id()) << ", initializer are not allowed for HitObjectAttributeNV"; } + if (storage_class == spv::StorageClass::HitObjectAttributeEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, <id> " << _.getIdName(inst->id()) + << ", initializer are not allowed for HitObjectAttributeEXT"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariableStorageClass(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction* value_type) { + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::CrossWorkgroup && + storage_class != spv::StorageClass::Private && + storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::UniformConstant && + storage_class != spv::StorageClass::RayPayloadKHR && + storage_class != spv::StorageClass::IncomingRayPayloadKHR && + storage_class != spv::StorageClass::HitAttributeKHR && + storage_class != spv::StorageClass::CallableDataKHR && + storage_class != spv::StorageClass::IncomingCallableDataKHR && + storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT && + storage_class != spv::StorageClass::HitObjectAttributeNV && + storage_class != spv::StorageClass::HitObjectAttributeEXT && + storage_class != spv::StorageClass::NodePayloadAMDX) { + bool storage_input_or_output = storage_class == spv::StorageClass::Input || + storage_class == spv::StorageClass::Output; + bool builtin = false; + if (storage_input_or_output) { + for (const Decoration& decoration : _.id_decorations(inst->id())) { + if (decoration.dec_type() == spv::Decoration::BuiltIn) { + builtin = true; + break; + } + } + } + if (!builtin && value_type && + ContainsInvalidBool(_, value_type, storage_input_or_output)) { + if (storage_input_or_output) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(7290) + << "If OpTypeBool is stored in conjunction with OpVariable " + "using Input or Output Storage Classes it requires a BuiltIn " + "decoration"; + + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "If OpTypeBool is stored in conjunction with OpVariable, it " + "can only be used with non-externally visible shader Storage " + "Classes: Workgroup, CrossWorkgroup, Private, Function, " + "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, " + "HitAttributeKHR, CallableDataKHR, " + "IncomingCallableDataKHR, NodePayloadAMDX, or " + "UniformConstant"; + } + } + } + + if (!_.IsValidStorageClass(storage_class)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << _.VkErrorID(4643) + << "Invalid storage class for target environment"; + } + + if (storage_class == spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Variable storage class cannot be Generic"; + } + + if (inst->function() && storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Variables must have a function[7] storage class inside" + " of a function"; + } + + if (!inst->function() && storage_class == spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Variables can not have a function[7] storage class " + "outside of a function"; + } + + // SPIR-V 3.32.8: Check that pointer type and variable type have the same + // storage class. + auto result_type = _.FindDef(inst->type_id()); + const auto result_storage_class_index = 1; + const auto result_storage_class = + result_type->GetOperandAs<spv::StorageClass>(result_storage_class_index); + if (storage_class != result_storage_class) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Storage class must match result type storage class"; } if (storage_class == spv::StorageClass::PhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "PhysicalStorageBuffer must not be used with OpVariable."; } + return SPV_SUCCESS; +} - auto pointee_base = pointee; - while (pointee_base && pointee_base->opcode() == spv::Op::OpTypeArray) { - pointee_base = _.FindDef(pointee_base->GetOperandAs<uint32_t>(1u)); - } - if (pointee_base && pointee_base->opcode() == spv::Op::OpTypePointer) { - if (pointee_base->GetOperandAs<spv::StorageClass>(1u) == - spv::StorageClass::PhysicalStorageBuffer) { - // check for AliasedPointer/RestrictPointer - bool foundAliased = - _.HasDecoration(inst->id(), spv::Decoration::AliasedPointer); - bool foundRestrict = - _.HasDecoration(inst->id(), spv::Decoration::RestrictPointer); - if (!foundAliased && !foundRestrict) { +spv_result_t ValidateVariablePointer(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction& pointee) { + if ((_.addressing_model() == spv::AddressingModel::Logical || + _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) && + !_.options()->relax_logical_pointer) { + spv_result_t error = SPV_SUCCESS; + bool contains_logical_pointer = _.ContainsType( + pointee.id(), + [&_, inst, &error](const Instruction* type) { + if (type->opcode() == spv::Op::OpTypePointer || + type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + const auto sc = type->GetOperandAs<spv::StorageClass>(1u); + if (sc != spv::StorageClass::PhysicalStorageBuffer) { + if (sc != spv::StorageClass::StorageBuffer && + sc != spv::StorageClass::Workgroup) { + error = + _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, variables can only allocate a " + "pointer to the StorageBuffer or Workgroup storage " + "classes"; + } else if (!_.HasCapability( + spv::Capability::VariablePointersStorageBuffer) && + sc == spv::StorageClass::StorageBuffer) { + error = + _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, variables can only allocate a " + "storage buffer pointer if the " + "VariablePointersStorageBuffer capability is declared"; + } else if (!_.HasCapability(spv::Capability::VariablePointers) && + sc == spv::StorageClass::Workgroup) { + error = + _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, variables can only allocate a " + "workgroup pointer if the VariablePointers capability " + "is " + "declared"; + } + return true; + } + } + return false; + }, + /* traverse_all_types = */ false); + + if (error != SPV_SUCCESS) return error; + + if (contains_logical_pointer) { + if (storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::Private) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable " << inst->id() - << ": expected AliasedPointer or RestrictPointer for " - << "PhysicalStorageBuffer pointer."; - } - if (foundAliased && foundRestrict) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable " << inst->id() - << ": can't specify both AliasedPointer and " - << "RestrictPointer for PhysicalStorageBuffer pointer."; + << "In Logical addressing with variable pointers, variables " + << "that allocate pointers must be in Function or Private " + << "storage classes"; } } } + return SPV_SUCCESS; +} - // Vulkan specific validation rules for OpTypeRuntimeArray - if (spvIsVulkanEnv(_.context()->target_env)) { - // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct, - // so should never appear as a bare variable. - // Unless the module has the RuntimeDescriptorArrayEXT capability. - if (value_type && value_type->opcode() == spv::Op::OpTypeRuntimeArray) { - if (!_.HasCapability(spv::Capability::RuntimeDescriptorArrayEXT)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4680) << "OpVariable, <id> " - << _.getIdName(inst->id()) - << ", is attempting to create memory for an illegal type, " - << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only " - << "appear as the final member of an OpTypeStruct, thus cannot " - << "be instantiated via OpVariable"; - } else { - // A bare variable OpTypeRuntimeArray is allowed in this context, but - // still need to check the storage class. - if (storage_class != spv::StorageClass::StorageBuffer && - storage_class != spv::StorageClass::Uniform && - storage_class != spv::StorageClass::UniformConstant) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4680) - << "For Vulkan with RuntimeDescriptorArrayEXT, a variable " - << "containing OpTypeRuntimeArray must have storage class of " - << "StorageBuffer, Uniform, or UniformConstant."; - } - } +spv_result_t ValidateVariableVulkanDescriptor(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction& pointee) { + // Vulkan Push Constant Interface section: Check type of PushConstant + // variables. + if (storage_class == spv::StorageClass::PushConstant) { + if (pointee.opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6808) << "PushConstant OpVariable <id> " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec, Push Constant Interface section:\n" + << "Such variables must be typed as OpTypeStruct"; } + } - // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it - // must either have the storage class StorageBuffer and be decorated - // with Block, or it must be in the Uniform storage class and be decorated - // as BufferBlock. + // Vulkan Descriptor Set Interface: Check type of UniformConstant and + // Uniform variables. + if (storage_class == spv::StorageClass::UniformConstant) { + if (!IsAllowedTypeOrArrayOfSame( + _, pointee, + {spv::Op::OpTypeImage, spv::Op::OpTypeSampler, + spv::Op::OpTypeSampledImage, spv::Op::OpTypeTensorARM, + spv::Op::OpTypeAccelerationStructureKHR})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4655) << "UniformConstant OpVariable <id> " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "Variables identified with the UniformConstant storage class " + << "are used only as handles to refer to opaque resources. Such " + << "variables must be typed as OpTypeImage, OpTypeSampler, " + << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " + << "or an array of one of these types."; + } + } + + if (storage_class == spv::StorageClass::Uniform) { + if (!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6807) << "Uniform OpVariable <id> " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec:\n" + << "Variables identified with the Uniform storage class are " + << "used to access transparent buffer backed resources. Such " + << "variables must be typed as OpTypeStruct, or an array of " + << "this type"; + } + } + + if (storage_class == spv::StorageClass::StorageBuffer) { + if (!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec:\n" + << "Variables identified with the StorageBuffer storage class " + "are used to access transparent buffer backed resources. " + "Such variables must be typed as OpTypeStruct, or an array " + "of this type"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariableVulkanInterface(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction* value_type, + uint32_t value_id) { + // Check for invalid use of Invariant + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + if (_.HasDecoration(inst->id(), spv::Decoration::Invariant)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4677) + << "Variable decorated with Invariant must only be identified " + "with the Input or Output storage class in Vulkan " + "environment."; + } + // Need to check if only the members in a struct are decorated if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) { - if (DoesStructContainRTA(_, value_type)) { - if (storage_class == spv::StorageClass::StorageBuffer || - storage_class == spv::StorageClass::PhysicalStorageBuffer) { - if (!_.HasDecoration(value_id, spv::Decoration::Block)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4680) - << "For Vulkan, an OpTypeStruct variable containing an " - << "OpTypeRuntimeArray must be decorated with Block if it " - << "has storage class StorageBuffer or " - "PhysicalStorageBuffer."; - } - } else if (storage_class == spv::StorageClass::Uniform) { - if (!_.HasDecoration(value_id, spv::Decoration::BufferBlock)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4680) - << "For Vulkan, an OpTypeStruct variable containing an " - << "OpTypeRuntimeArray must be decorated with BufferBlock " - << "if it has storage class Uniform."; - } - } else { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4680) - << "For Vulkan, OpTypeStruct variables containing " - << "OpTypeRuntimeArray must have storage class of " - << "StorageBuffer, PhysicalStorageBuffer, or Uniform."; - } + if (_.HasDecoration(value_id, spv::Decoration::Invariant)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4677) + << "Variable struct member decorated with Invariant must only " + "be identified with the Input or Output storage class in " + "Vulkan environment."; } } } + return SPV_SUCCESS; +} + +spv_result_t ValidateVariableCoopMat(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction& pointee) { // Cooperative matrix types can only be allocated in Function or Private if ((storage_class != spv::StorageClass::Function && storage_class != spv::StorageClass::Private) && - pointee && ContainsCooperativeMatrix(_, pointee)) { + _.ContainsType(pointee.id(), [](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + return opcode == spv::Op::OpTypeCooperativeMatrixNV || + opcode == spv::Op::OpTypeCooperativeMatrixKHR; + })) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Cooperative matrix types (or types containing them) can only be " "allocated " @@ -827,135 +786,460 @@ "parameters"; } + return SPV_SUCCESS; +} + +// Vulkan specific validation rules for OpTypeRuntimeArray +spv_result_t ValidateVariableVulkanArray(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction& value_type, + uint32_t value_id) { + // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct, + // so should never appear as a bare variable. + // Unless the module has the RuntimeDescriptorArray capability. + if (value_type.opcode() == spv::Op::OpTypeRuntimeArray) { + if (!_.HasCapability(spv::Capability::RuntimeDescriptorArray)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "OpVariable, <id> " + << _.getIdName(inst->id()) + << ", is attempting to create memory for an illegal type, " + << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only " + << "appear as the final member of an OpTypeStruct, thus cannot " + << "be instantiated via OpVariable, unless the " + "RuntimeDescriptorArray Capability is declared"; + } else { + // A bare variable OpTypeRuntimeArray is allowed in this context, but + // still need to check the storage class. + if (storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::Uniform && + storage_class != spv::StorageClass::UniformConstant) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan with RuntimeDescriptorArray, a variable " + << "containing OpTypeRuntimeArray must have storage class of " + << "StorageBuffer, Uniform, or UniformConstant."; + } + } + } + + // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it + // must either have the storage class StorageBuffer and be decorated + // with Block, or it must be in the Uniform storage class + if (value_type.opcode() == spv::Op::OpTypeStruct) { + if (DoesStructContainRTA(_, &value_type)) { + if (storage_class == spv::StorageClass::StorageBuffer || + storage_class == spv::StorageClass::PhysicalStorageBuffer) { + if (!_.HasDecoration(value_id, spv::Decoration::Block)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan, an OpTypeStruct variable containing an " + << "OpTypeRuntimeArray must be decorated with Block if it " + << "has storage class StorageBuffer or " + "PhysicalStorageBuffer."; + } + } else if (storage_class == spv::StorageClass::Uniform) { + // BufferBlock Uniform were always allowed. + // + // Block Uniform use to be invalid, but Vulkan added + // VK_EXT_shader_uniform_buffer_unsized_array and now this is + // validated at runtime + // + // The uniform must have either the Block or BufferBlock decoration + // (see VUID-StandaloneSpirv-Uniform-06676) + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan, OpTypeStruct variables containing " + << "OpTypeRuntimeArray must have storage class of " + << "StorageBuffer, PhysicalStorageBuffer, or Uniform."; + } + } + } + + return SPV_SUCCESS; +} + +// Vulkan-specific validation for long vectors +spv_result_t ValidateVariableVulkanLongVector(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction& pointee) { + if (_.HasCapability(spv::Capability::LongVectorEXT)) { + if ((storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::Private && + storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer && + storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::Uniform && + storage_class != spv::StorageClass::PushConstant && + storage_class != spv::StorageClass::ShaderRecordBufferKHR) && + _.ContainsType(pointee.id(), [&](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + if (opcode == spv::Op::OpTypeVector || + opcode == spv::Op::OpTypeVectorIdEXT) { + uint32_t dim = _.GetDimension(type_inst->id()); + return dim > 4; + } + return false; + })) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(12297) + << "Long vector types with more than 4 components (or types " + "containing them) not supported in storage class " + << StorageClassToString(storage_class); + } + + if ((storage_class == spv::StorageClass::StorageBuffer || + storage_class == spv::StorageClass::PhysicalStorageBuffer || + storage_class == spv::StorageClass::Uniform || + storage_class == spv::StorageClass::PushConstant || + storage_class == spv::StorageClass::ShaderRecordBufferKHR || + (storage_class == spv::StorageClass::Workgroup && + _.HasDecoration(pointee.id(), spv::Decoration::Block))) && + _.ContainsType(pointee.id(), [&](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + if (opcode == spv::Op::OpTypeVectorIdEXT) { + auto component_count = + _.FindDef(type_inst->GetOperandAs<uint32_t>(2u)); + return (bool)spvOpcodeIsSpecConstant(component_count->opcode()); + } + return false; + })) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(12294) + << "Long vector types with spec constant component count " + "not supported in storage class with explicit layout " + << StorageClassToString(storage_class); + } + } else { + if ((storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::Private) && + _.ContainsType(pointee.id(), [](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + return opcode == spv::Op::OpTypeVectorIdEXT; + })) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cooperative vector types (or types containing them) can " + "only be " + "allocated " + << "in Function or Private storage classes or as function " + "parameters"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariableShader(ValidationState_t& _, + const Instruction* inst, + spv::StorageClass storage_class, + const Instruction* value_type, + uint32_t value_id) { + // Don't allow variables containing 16-bit elements without the appropriate + // capabilities. + if ((!_.HasCapability(spv::Capability::Int16) && + _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 16)) || + (!_.HasCapability(spv::Capability::Float16) && + _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeFloat, 16))) { + auto underlying_type = value_type; + while (underlying_type && + underlying_type->opcode() == spv::Op::OpTypePointer) { + storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u); + underlying_type = _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u)); + } + bool storage_class_ok = true; + std::string sc_name = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class)); + switch (storage_class) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Uniform: + if (underlying_type && + !_.HasCapability( + spv::Capability::UniformAndStorageBuffer16BitAccess)) { + if (underlying_type->opcode() == spv::Op::OpTypeArray || + underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) { + underlying_type = + _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u)); + } + if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess) || + !_.HasDecoration(underlying_type->id(), + spv::Decoration::BufferBlock)) { + storage_class_ok = false; + } + } + break; + case spv::StorageClass::PushConstant: + if (!_.HasCapability(spv::Capability::StoragePushConstant16)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Input: + case spv::StorageClass::Output: + if (!_.HasCapability(spv::Capability::StorageInputOutput16)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Workgroup: + if (!_.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR)) { + storage_class_ok = false; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot allocate a variable containing a 16-bit type in " + << sc_name << " storage class"; + } + if (!storage_class_ok) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Allocating a variable containing a 16-bit element in " + << sc_name << " storage class requires an additional capability"; + } + } + // Don't allow variables containing 8-bit elements without the appropriate + // capabilities. + if (!_.HasCapability(spv::Capability::Int8) && + _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) { + auto underlying_type = value_type; + while (underlying_type && + underlying_type->opcode() == spv::Op::OpTypePointer) { + storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u); + underlying_type = _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u)); + } + bool storage_class_ok = true; + std::string sc_name = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class)); + switch (storage_class) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Uniform: + if (underlying_type && + !_.HasCapability( + spv::Capability::UniformAndStorageBuffer8BitAccess)) { + if (underlying_type->opcode() == spv::Op::OpTypeArray || + underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) { + underlying_type = + _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u)); + } + if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess) || + !_.HasDecoration(underlying_type->id(), + spv::Decoration::BufferBlock)) { + storage_class_ok = false; + } + } + break; + case spv::StorageClass::PushConstant: + if (!_.HasCapability(spv::Capability::StoragePushConstant8)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Workgroup: + if (!_.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR)) { + storage_class_ok = false; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot allocate a variable containing a 8-bit type in " + << sc_name << " storage class"; + } + if (!storage_class_ok) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Allocating a variable containing a 8-bit element in " + << sc_name << " storage class requires an additional capability"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateVariableTileShadingQCOM(ValidationState_t& _, + const Instruction* inst) { + auto result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() == spv::Op::OpTypePointer) { + const auto pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2)); + if (pointee_type && pointee_type->opcode() == spv::Op::OpTypeImage) { + spv::Dim dim = static_cast<spv::Dim>(pointee_type->word(3)); + if (dim != spv::Dim::Dim2D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Any OpTypeImage variable in the TileAttachmentQCOM " + "Storage Class must " + "have 2D as its dimension"; + } + unsigned sampled = pointee_type->word(7); + if (sampled != 1 && sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Any OpyTpeImage variable in the TileAttachmentQCOM " + "Storage Class must " + "have 1 or 2 as Image 'Sampled' parameter"; + } + for (const auto& pair_o : inst->uses()) { + const auto* use_inst_o = pair_o.first; + if (use_inst_o->opcode() == spv::Op::OpLoad) { + for (const auto& pair_i : use_inst_o->uses()) { + const auto* use_inst_i = pair_i.first; + switch (use_inst_i->opcode()) { + case spv::Op::OpImageQueryFormat: + case spv::Op::OpImageQueryOrder: + case spv::Op::OpImageQuerySizeLod: + case spv::Op::OpImageQuerySize: + case spv::Op::OpImageQueryLod: + case spv::Op::OpImageQueryLevels: + case spv::Op::OpImageQuerySamples: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Any variable in the TileAttachmentQCOM Storage " + "Class must " + "not be consumed by an OpImageQuery* instruction"; + default: + break; + } + } + } + } + } + } + + if (!(_.HasDecoration(inst->id(), spv::Decoration::DescriptorSet) && + _.HasDecoration(inst->id(), spv::Decoration::Binding))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Any variable in the TileAttachmentQCOM Storage Class must " + "be decorated with DescriptorSet and Binding"; + } + if (_.HasDecoration(inst->id(), spv::Decoration::Component)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Any variable in the TileAttachmentQCOM Storage Class must " + "not be decorated with Component decoration"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { + const bool untyped_pointer = inst->opcode() == spv::Op::OpUntypedVariableKHR; + + auto result_type = _.FindDef(inst->type_id()); + if (untyped_pointer) { + if (!result_type || + result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type must be an untyped pointer"; + } else { + if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable Result Type <id> " << _.getIdName(inst->type_id()) + << " is not a pointer type."; + } + } + + const auto storage_class_index = 2u; + auto storage_class = + inst->GetOperandAs<spv::StorageClass>(storage_class_index); + uint32_t value_id = 0; + if (untyped_pointer) { + const bool has_data_type = 3u < inst->operands().size(); + if (has_data_type) { + value_id = inst->GetOperandAs<uint32_t>(3u); + auto data_type = _.FindDef(value_id); + if (!data_type || !spvOpcodeGeneratesType(data_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Data type must be a type instruction"; + } + } else { + if (storage_class == spv::StorageClass::Function || + storage_class == spv::StorageClass::Private || + storage_class == spv::StorageClass::Workgroup) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Data type must be specified for Function, Private, and " + "Workgroup storage classes"; + } + // Added from SPV_EXT_descriptor_heap + // Vulkan allows untyped pointer without |Data Type| but only for heap + // decorated variable that are in UniformConstant + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class != spv::StorageClass::UniformConstant) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(11167) << "Storage class is " + << StorageClassToString(storage_class) + << ", but Vulkan requires that Data Type be specified when " + "not using UniformConstant storage class"; + } else if (!(_.IsDescriptorHeapBaseVariable(inst))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(11347) + << "Storage class is UniformConstant, but Vulkan requires " + "that Data Type be specified if the variable is not " + "decorated with SamplerHeapEXT or ResourceHeapEXT"; + } + } + } + } + + // For OpVariable the data type comes from pointee type of the result type, + // while for OpUntypedVariableKHR the data type comes from the operand. + if (!untyped_pointer) { + value_id = result_type->GetOperandAs<uint32_t>(2); + } + const Instruction* value_type = value_id == 0 ? nullptr : _.FindDef(value_id); + + if (auto error = + ValidateVariableInitializer(_, inst, storage_class, value_id)) + return error; + + if (auto error = + ValidateVariableStorageClass(_, inst, storage_class, value_type)) + return error; + + // Variable pointer related restrictions. + const Instruction* pointee = + untyped_pointer ? value_id == 0 ? nullptr : _.FindDef(value_id) + : _.FindDef(result_type->word(3)); + + if (pointee) { + if (auto error = ValidateVariablePointer(_, inst, storage_class, *pointee)) + return error; + if (auto error = ValidateVariableCoopMat(_, inst, storage_class, *pointee)) + return error; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (pointee) { + if (auto error = ValidateVariableVulkanDescriptor(_, inst, storage_class, + *pointee)) + return error; + if (auto error = ValidateVariableVulkanLongVector(_, inst, storage_class, + *pointee)) + return error; + } + + if (auto error = ValidateVariableVulkanInterface(_, inst, storage_class, + value_type, value_id)) + return error; + + if (value_type) { + if (auto error = ValidateVariableVulkanArray(_, inst, storage_class, + *value_type, value_id)) + return error; + } + } + if (_.HasCapability(spv::Capability::Shader)) { - // Don't allow variables containing 16-bit elements without the appropriate - // capabilities. - if ((!_.HasCapability(spv::Capability::Int16) && - _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 16)) || - (!_.HasCapability(spv::Capability::Float16) && - _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeFloat, 16))) { - auto underlying_type = value_type; - while (underlying_type && - underlying_type->opcode() == spv::Op::OpTypePointer) { - storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u); - underlying_type = - _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u)); - } - bool storage_class_ok = true; - std::string sc_name = _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class)); - switch (storage_class) { - case spv::StorageClass::StorageBuffer: - case spv::StorageClass::PhysicalStorageBuffer: - if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess)) { - storage_class_ok = false; - } - break; - case spv::StorageClass::Uniform: - if (underlying_type && - !_.HasCapability( - spv::Capability::UniformAndStorageBuffer16BitAccess)) { - if (underlying_type->opcode() == spv::Op::OpTypeArray || - underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) { - underlying_type = - _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u)); - } - if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess) || - !_.HasDecoration(underlying_type->id(), - spv::Decoration::BufferBlock)) { - storage_class_ok = false; - } - } - break; - case spv::StorageClass::PushConstant: - if (!_.HasCapability(spv::Capability::StoragePushConstant16)) { - storage_class_ok = false; - } - break; - case spv::StorageClass::Input: - case spv::StorageClass::Output: - if (!_.HasCapability(spv::Capability::StorageInputOutput16)) { - storage_class_ok = false; - } - break; - case spv::StorageClass::Workgroup: - if (!_.HasCapability( - spv::Capability:: - WorkgroupMemoryExplicitLayout16BitAccessKHR)) { - storage_class_ok = false; - } - break; - default: - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Cannot allocate a variable containing a 16-bit type in " - << sc_name << " storage class"; - } - if (!storage_class_ok) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Allocating a variable containing a 16-bit element in " - << sc_name << " storage class requires an additional capability"; - } - } - // Don't allow variables containing 8-bit elements without the appropriate - // capabilities. - if (!_.HasCapability(spv::Capability::Int8) && - _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) { - auto underlying_type = value_type; - while (underlying_type && - underlying_type->opcode() == spv::Op::OpTypePointer) { - storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u); - underlying_type = - _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u)); - } - bool storage_class_ok = true; - std::string sc_name = _.grammar().lookupOperandName( - SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class)); - switch (storage_class) { - case spv::StorageClass::StorageBuffer: - case spv::StorageClass::PhysicalStorageBuffer: - if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess)) { - storage_class_ok = false; - } - break; - case spv::StorageClass::Uniform: - if (underlying_type && - !_.HasCapability( - spv::Capability::UniformAndStorageBuffer8BitAccess)) { - if (underlying_type->opcode() == spv::Op::OpTypeArray || - underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) { - underlying_type = - _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u)); - } - if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess) || - !_.HasDecoration(underlying_type->id(), - spv::Decoration::BufferBlock)) { - storage_class_ok = false; - } - } - break; - case spv::StorageClass::PushConstant: - if (!_.HasCapability(spv::Capability::StoragePushConstant8)) { - storage_class_ok = false; - } - break; - case spv::StorageClass::Workgroup: - if (!_.HasCapability( - spv::Capability:: - WorkgroupMemoryExplicitLayout8BitAccessKHR)) { - storage_class_ok = false; - } - break; - default: - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Cannot allocate a variable containing a 8-bit type in " - << sc_name << " storage class"; - } - if (!storage_class_ok) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Allocating a variable containing a 8-bit element in " - << sc_name << " storage class requires an additional capability"; - } - } + if (auto error = ValidateVariableShader(_, inst, storage_class, value_type, + value_id)) + return error; + } + + if (_.HasCapability(spv::Capability::TileShadingQCOM) && + storage_class == spv::StorageClass::TileAttachmentQCOM) { + if (auto error = ValidateVariableTileShadingQCOM(_, inst)) return error; } return SPV_SUCCESS; @@ -1023,8 +1307,81 @@ } } + // Skip checking if there is zero chance for this having a mesh shader + // entrypoint + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + pointer_type->GetOperandAs<spv::StorageClass>(1) == + spv::StorageClass::Output) { + std::string errorVUID = _.VkErrorID(7107); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + // Seems the NV Mesh extension was less strict and allowed + // writting to outputs + if (model == spv::ExecutionModel::MeshEXT) { + if (message) { + *message = errorVUID + + "The Output Storage Class in a Mesh Execution " + "Model must not be read from"; + } + return false; + } + return true; + }); + } + _.RegisterQCOMImageProcessingTextureConsumer(pointer_id, inst, nullptr); + // EXT_descriptor_heap + if (spvIsVulkanEnv(_.context()->target_env) && + _.IsDescriptorHeapBaseVariable(_.FindDef(pointer_id))) { + auto descBaseVariable = _.FindUntypedBaseVariable(_.FindDef(pointer_id)); + auto descBaseVariableId = descBaseVariable->id(); + if (!_.HasDecoration(descBaseVariableId, spv::Decoration::DescriptorSet) && + !_.HasDecoration(descBaseVariableId, spv::Decoration::Binding)) { + switch (result_type->opcode()) { + case spv::Op::OpTypeSampler: + if (!_.IsBuiltin(descBaseVariableId, spv::BuiltIn::SamplerHeapEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(11336) + << "OpTypeSampler pointer instruction has no descriptor set " + << "or binding and is not derived from a variable decorated " + "with " + "SamplerHeapEXT"; + } + break; + case spv::Op::OpTypeImage: + if (!_.IsBuiltin(descBaseVariableId, spv::BuiltIn::ResourceHeapEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(11337) + << "OpTypeImage pointer instruction has no descriptor set " + << "or binding and is not derived from a variable decorated " + "with " + "ResourceHeapEXT"; + } + break; + case spv::Op::OpTypeAccelerationStructureKHR: + uint32_t data_type; + spv::StorageClass sc; + if (_.GetPointerTypeInfo(descBaseVariable->type_id(), &data_type, + &sc) && + sc != spv::StorageClass::Private && + sc != spv::StorageClass::Function && + !_.IsBuiltin(descBaseVariableId, spv::BuiltIn::ResourceHeapEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(11339) + << "OpTypeAccelerationStructureKHR pointer instruction has " + "no " + << "descriptor set or binding and is not derived from a " + "variable decorated with ResourceHeapEXT"; + } + break; + default: + break; + } + } + } + return SPV_SUCCESS; } @@ -1228,7 +1585,7 @@ } } else { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(static_cast<spv::Op>(inst->opcode())) + << spvOpcodeString(inst->opcode()) << " with two memory access operands requires SPIR-V 1.4 or " "later"; } @@ -1446,12 +1803,21 @@ } if (!int8 && !int16 && !(source_int16_match && target_int16_match)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size must be a multiple of 4"; + << _.VkErrorID(11165) + << "Size must be a multiple of 4. This is valid if Source (" + << StorageClassToString(source_sc) << ") and Target (" + << StorageClassToString(source_sc) + << ") storage classes both support either 8-bit or 16-bit"; } if (value % 2 != 0) { if (!int8 && !(source_int8_match && target_int8_match)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Size must be a multiple of 2"; + << _.VkErrorID(11165) + << "Size must be a multiple of 2. This is valid if Source " + "(" + << StorageClassToString(source_sc) << ") and Target (" + << StorageClassToString(source_sc) + << ") storage classes both support 8-bit"; } } } @@ -1480,9 +1846,7 @@ spv_result_t ValidateAccessChain(ValidationState_t& _, const Instruction* inst) { - std::string instr_name = - "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode()))); - + const spv::Op opcode = inst->opcode(); const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode()); // The result type must be OpTypePointer for regular access chains and an @@ -1492,19 +1856,17 @@ if (!result_type || spv::Op::OpTypeUntypedPointerKHR != result_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must be OpTypeUntypedPointerKHR. Found Op" - << spvOpcodeString(static_cast<spv::Op>(result_type->opcode())) - << "."; + << spvOpcodeString(result_type->opcode()) << "."; } } else { if (!result_type || spv::Op::OpTypePointer != result_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op" - << spvOpcodeString(static_cast<spv::Op>(result_type->opcode())) - << "."; + << spvOpcodeString(result_type->opcode()) << "."; } } @@ -1517,6 +1879,60 @@ return _.diag(SPV_ERROR_INVALID_ID, inst) << "Base type must be a non-pointer type"; } + + const auto ContainsBlock = [&_](const Instruction* type_inst) { + if (type_inst->opcode() == spv::Op::OpTypeStruct) { + if (_.HasDecoration(type_inst->id(), spv::Decoration::Block) || + _.HasDecoration(type_inst->id(), spv::Decoration::BufferBlock)) { + return true; + } + } + return false; + }; + + // Block (and BufferBlock) arrays cannot be reinterpreted via untyped access + // chains. + const bool base_type_block_array = + base_type->opcode() == spv::Op::OpTypeArray && + _.ContainsType(base_type->id(), ContainsBlock, + /* traverse_all_types = */ false); + + const auto base_index = untyped_pointer ? 3 : 2; + const auto base_id = inst->GetOperandAs<uint32_t>(base_index); + auto base = _.FindDef(base_id); + // Strictly speaking this misses trivial access chains and function + // parameter chasing, but that would be a significant complication in the + // traversal. + while (base->opcode() == spv::Op::OpCopyObject) { + base = _.FindDef(base->GetOperandAs<uint32_t>(2)); + } + const Instruction* base_data_type = nullptr; + if (base->opcode() == spv::Op::OpVariable) { + const auto ptr_type = _.FindDef(base->type_id()); + base_data_type = _.FindDef(ptr_type->GetOperandAs<uint32_t>(2)); + } else if (base->opcode() == spv::Op::OpUntypedVariableKHR) { + if (base->operands().size() > 3) { + base_data_type = _.FindDef(base->GetOperandAs<uint32_t>(3)); + } + } + + if (base_data_type) { + const bool base_block_array = + base_data_type->opcode() == spv::Op::OpTypeArray && + _.ContainsType(base_data_type->id(), ContainsBlock, + /* traverse_all_types = */ false); + + if (base_type_block_array != base_block_array) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Both Base Type and Base must be Block or BufferBlock arrays " + "or neither can be"; + } else if (base_type_block_array && base_block_array && + base_type->id() != base_data_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "If Base or Base Type is a Block or BufferBlock array, the " + "other must also be the same array"; + } + } } // Base must be a pointer, pointing to the base of a composite object. @@ -1528,8 +1944,8 @@ (untyped_pointer && spv::Op::OpTypeUntypedPointerKHR == base_type->opcode()))) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The Base <id> " << _.getIdName(base_id) << " in " << instr_name - << " instruction must be a pointer."; + << "The Base <id> " << _.getIdName(base_id) << " in Op" + << spvOpcodeString(opcode) << " instruction must be a pointer."; } // The result pointer storage class and base pointer storage class must match. @@ -1539,8 +1955,8 @@ if (result_type_storage_class != base_type_storage_class) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "The result pointer storage class and base " - "pointer storage class in " - << instr_name << " do not match."; + "pointer storage class in Op" + << spvOpcodeString(opcode) << " do not match."; } // The type pointed to by OpTypePointer (word 3) must be a composite type. @@ -1564,8 +1980,9 @@ _.options()->universal_limits_.max_access_chain_indexes; if (num_indexes > num_indexes_limit) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The number of indexes in " << instr_name << " may not exceed " - << num_indexes_limit << ". Found " << num_indexes << " indexes."; + << "The number of indexes in Op" << spvOpcodeString(opcode) + << " may not exceed " << num_indexes_limit << ". Found " + << num_indexes << " indexes."; } // Indexes walk the type hierarchy to the desired depth, potentially down to // scalar granularity. The first index in Indexes will select the top-level @@ -1589,18 +2006,41 @@ auto index_type = _.FindDef(cur_word_instr->type_id()); if (!index_type || spv::Op::OpTypeInt != index_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Indexes passed to " << instr_name + << "Indexes passed to Op" << spvOpcodeString(opcode) << " must be of type integer."; } + + // Logical pointer restrictions: any constant index with a signed integer + // type must not have its sign bit set. + if (!_.options()->relax_logical_pointer && + (_.addressing_model() == spv::AddressingModel::Logical || + _.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64) && + result_type_storage_class != + static_cast<uint32_t>(spv::StorageClass::PhysicalStorageBuffer)) { + if (index_type->GetOperandAs<uint32_t>(2) == 1) { + int64_t val = 0; + if (_.EvalConstantValInt64(cur_word, &val)) { + if (val < 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index at word " << i << " may not have a negative value"; + } + } + } + } + switch (type_pointee->opcode()) { case spv::Op::OpTypeMatrix: case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: case spv::Op::OpTypeArray: - case spv::Op::OpTypeRuntimeArray: { + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeNodePayloadArrayAMDX: { // In OpTypeMatrix, OpTypeVector, spv::Op::OpTypeCooperativeMatrixNV, - // OpTypeArray, and OpTypeRuntimeArray, word 2 is the Element Type. + // OpTypeVectorIdEXT, OpTypeArray, and OpTypeRuntimeArray, word + // 2 is the Element Type. type_pointee = _.FindDef(type_pointee->word(2)); break; } @@ -1609,9 +2049,10 @@ // index: the index must be an OpConstant. int64_t cur_index; if (!_.EvalConstantValInt64(cur_word, &cur_index)) { - return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr) - << "The <id> passed to " << instr_name - << " to index into a " + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The <id> passed to Op" << spvOpcodeString(opcode) + << " to index " << _.getIdName(cur_word) + << " into a " "structure must be an OpConstant."; } @@ -1620,10 +2061,10 @@ const int64_t num_struct_members = static_cast<int64_t>(type_pointee->words().size() - 2); if (cur_index >= num_struct_members || cur_index < 0) { - return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr) - << "Index is out of bounds: " << instr_name - << " cannot find index " << cur_index - << " into the structure <id> " + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index " << _.getIdName(cur_word) << " is out of bounds: Op" + << spvOpcodeString(opcode) << " cannot find index " + << cur_index << " into the structure <id> " << _.getIdName(type_pointee->id()) << ". This structure has " << num_struct_members << " members. Largest valid index is " << num_struct_members - 1 << "."; @@ -1637,7 +2078,7 @@ default: { // Give an error. reached non-composite type while indexes still remain. return _.diag(SPV_ERROR_INVALID_ID, inst) - << instr_name + << "Op" << spvOpcodeString(opcode) << " reached non-composite type while indexes " "still remain to be traversed."; } @@ -1652,15 +2093,19 @@ // At this point, we have fully walked down from the base using the indeces. // The type being pointed to should be the same as the result type. if (type_pointee->id() != result_type_pointee->id()) { + bool same_type = result_type_pointee->opcode() == type_pointee->opcode(); return _.diag(SPV_ERROR_INVALID_ID, inst) - << instr_name << " result type (Op" - << spvOpcodeString( - static_cast<spv::Op>(result_type_pointee->opcode())) + << "Op" << spvOpcodeString(opcode) << " result type <id> " + << _.getIdName(result_type_pointee->id()) << " (Op" + << spvOpcodeString(result_type_pointee->opcode()) << ") does not match the type that results from indexing into the " "base " - "<id> (Op" - << spvOpcodeString(static_cast<spv::Op>(type_pointee->opcode())) - << ")."; + "<id> " + << _.getIdName(type_pointee->id()) << " (Op" + << spvOpcodeString(type_pointee->opcode()) << ")." + << (same_type ? " (The types must be the exact same Id, so the " + "two types referenced are slighlty different)" + : ""); } } @@ -1669,13 +2114,12 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _, const Instruction* inst) { - std::string instr_name = "Op" + std::string(spvOpcodeString(inst->opcode())); - + const spv::Op opcode = inst->opcode(); // The result type must be OpTypePointer. const auto result_type = _.FindDef(inst->type_id()); if (spv::Op::OpTypePointer != result_type->opcode()) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op" << spvOpcodeString(result_type->opcode()) << '.'; } @@ -1686,7 +2130,7 @@ storage_class != spv::StorageClass::PhysicalStorageBuffer && storage_class != spv::StorageClass::Uniform) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must point to a storage class of " "StorageBuffer, PhysicalStorageBuffer, or Uniform."; @@ -1699,7 +2143,7 @@ result_type_pointee->opcode() == spv::Op::OpTypeMatrix || result_type_pointee->opcode() == spv::Op::OpTypeStruct) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must not point to " "OpTypeArray, OpTypeMatrix, or OpTypeStruct."; @@ -1709,7 +2153,7 @@ const auto stride = _.FindDef(inst->GetOperandAs<uint32_t>(3)); if (stride->opcode() != spv::Op::OpConstant) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The Stride of " << instr_name << " <id> " + << "The Stride of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must be OpConstant. Found Op" << spvOpcodeString(stride->opcode()) << '.'; } @@ -1717,7 +2161,7 @@ const auto stride_type = _.FindDef(stride->type_id()); if (stride_type->opcode() != spv::Op::OpTypeInt) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The type of Stride of " << instr_name << " <id> " + << "The type of Stride of Op" << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op" << spvOpcodeString(stride_type->opcode()) << '.'; } @@ -1729,16 +2173,17 @@ const auto value_type = _.FindDef(value->type_id()); if (value_type->opcode() != spv::Op::OpTypeInt) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The type of " << name << " of " << instr_name << " <id> " - << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op" + << "The type of " << name << " of Op" << spvOpcodeString(opcode) + << " <id> " << _.getIdName(inst->id()) + << " must be OpTypeInt. Found Op" << spvOpcodeString(value_type->opcode()) << '.'; } const auto width = value_type->GetOperandAs<uint32_t>(1); if (width != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "The integer width of " << name << " of " << instr_name - << " <id> " << _.getIdName(inst->id()) << " must be 32. Found " - << width << '.'; + << "The integer width of " << name << " of Op" + << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id()) + << " must be 32. Found " << width << '.'; } return SPV_SUCCESS; }; @@ -1789,28 +2234,39 @@ spv_result_t ValidatePtrAccessChain(ValidationState_t& _, const Instruction* inst) { - if (_.addressing_model() == spv::AddressingModel::Logical && - inst->opcode() == spv::Op::OpPtrAccessChain) { - if (!_.features().variable_pointers) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Generating variable pointers requires capability " - << "VariablePointers or VariablePointersStorageBuffer"; - } - } - // Need to call first, will make sure Base is a valid ID if (auto error = ValidateAccessChain(_, inst)) return error; const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode()); - const auto base_id = inst->GetOperandAs<uint32_t>(2); - const auto base = _.FindDef(base_id); - const auto base_type = untyped_pointer - ? _.FindDef(inst->GetOperandAs<uint32_t>(2)) - : _.FindDef(base->type_id()); + const auto base_idx = untyped_pointer ? 3 : 2; + const auto base = _.FindDef(inst->GetOperandAs<uint32_t>(base_idx)); + const auto base_type = _.FindDef(base->type_id()); const auto base_type_storage_class = base_type->GetOperandAs<spv::StorageClass>(1); + const auto element_idx = untyped_pointer ? 4 : 3; + const auto element = _.FindDef(inst->GetOperandAs<uint32_t>(element_idx)); + const auto element_type = _.FindDef(element->type_id()); + if (!element_type || element_type->opcode() != spv::Op::OpTypeInt) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Element must be an integer"; + } + uint64_t element_val = 0; + if (_.EvalConstantValUint64(element->id(), &element_val)) { + if (element_val != 0) { + const auto interp_type = + untyped_pointer ? _.FindDef(inst->GetOperandAs<uint32_t>(2)) + : _.FindDef(base_type->GetOperandAs<uint32_t>(2)); + if (interp_type->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(interp_type->id(), spv::Decoration::Block) || + _.HasDecoration(interp_type->id(), spv::Decoration::BufferBlock))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Element must be 0 if the interpretation type is a Block- or " + "BufferBlock-decorated structure"; + } + } + } + if (_.HasCapability(spv::Capability::Shader) && (base_type_storage_class == spv::StorageClass::Uniform || base_type_storage_class == spv::StorageClass::StorageBuffer || @@ -1818,10 +2274,11 @@ base_type_storage_class == spv::StorageClass::PushConstant || (_.HasCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR) && base_type_storage_class == spv::StorageClass::Workgroup)) && - !_.HasDecoration(base_type->id(), spv::Decoration::ArrayStride)) { + (!_.HasDecoration(base_type->id(), spv::Decoration::ArrayStride) && + !_.HasDecoration(base_type->id(), spv::Decoration::ArrayStrideIdEXT))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "OpPtrAccessChain must have a Base whose type is decorated " - "with ArrayStride"; + "with ArrayStride or ArrayStrideIdEXT"; } if (spvIsVulkanEnv(_.context()->target_env)) { @@ -1857,32 +2314,39 @@ spv_result_t ValidateArrayLength(ValidationState_t& state, const Instruction* inst) { - std::string instr_name = - "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode()))); + const spv::Op opcode = inst->opcode(); - // Result type must be a 32-bit unsigned int. - auto result_type = state.FindDef(inst->type_id()); - if (result_type->opcode() != spv::Op::OpTypeInt || - result_type->GetOperandAs<uint32_t>(1) != 32 || - result_type->GetOperandAs<uint32_t>(2) != 0) { + // Result type must be a 32- or 64-bit unsigned int. + // 64-bit requires CapabilityShader64BitIndexingEXT or a pipeline/shader + // flag and is validated in VVL. + const uint32_t result_type_id = inst->type_id(); + if (!state.IsIntScalarTypeWithSignedness(result_type_id, 0)) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << state.getIdName(inst->id()) - << " must be OpTypeInt with width 32 and signedness 0."; + << " must be OpTypeInt with width 32 or 64 and signedness 0."; + } + const uint32_t result_type_width = state.GetBitWidth(inst->type_id()); + if (result_type_width != 32 && result_type_width != 64) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " + << state.getIdName(inst->id()) + << " must be OpTypeInt with width 32 or 64 and signedness 0."; } const bool untyped = inst->opcode() == spv::Op::OpUntypedArrayLengthKHR; auto pointer_ty_id = state.GetOperandTypeId(inst, (untyped ? 3 : 2)); auto pointer_ty = state.FindDef(pointer_ty_id); if (untyped) { - if (pointer_ty->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + if (!pointer_ty || + pointer_ty->opcode() != spv::Op::OpTypeUntypedPointerKHR) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "Pointer must be an untyped pointer"; + << "Pointer must be an untyped pointer object"; } } else if (pointer_ty->opcode() != spv::Op::OpTypePointer) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Structure's type in " << instr_name << " <id> " - << state.getIdName(inst->id()) + << "The Structure's type in Op" << spvOpcodeString(opcode) + << " <id> " << state.getIdName(inst->id()) << " must be a pointer to an OpTypeStruct."; } @@ -1895,8 +2359,8 @@ if (structure_type->opcode() != spv::Op::OpTypeStruct) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Structure's type in " << instr_name << " <id> " - << state.getIdName(inst->id()) + << "The Structure's type in Op" << spvOpcodeString(opcode) + << " <id> " << state.getIdName(inst->id()) << " must be a pointer to an OpTypeStruct."; } @@ -1905,8 +2369,9 @@ state.FindDef(structure_type->GetOperandAs<uint32_t>(num_of_members)); if (last_member->opcode() != spv::Op::OpTypeRuntimeArray) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Structure's last member in " << instr_name << " <id> " - << state.getIdName(inst->id()) << " must be an OpTypeRuntimeArray."; + << "The Structure's last member in Op" << spvOpcodeString(opcode) + << " <id> " << state.getIdName(inst->id()) + << " must be an OpTypeRuntimeArray."; } // The array member must the index of the last element (the run time @@ -1914,40 +2379,50 @@ const auto index = untyped ? 4 : 3; if (inst->GetOperandAs<uint32_t>(index) != num_of_members - 1) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The array member in " << instr_name << " <id> " + << "The array member in Op" << spvOpcodeString(opcode) << " <id> " << state.getIdName(inst->id()) << " must be the last member of the struct."; } + + if (spvIsVulkanEnv(state.context()->target_env)) { + const auto storage_class = pointer_ty->GetOperandAs<spv::StorageClass>(1); + if (storage_class == spv::StorageClass::Uniform && + state.HasDecoration(structure_type->id(), spv::Decoration::Block)) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << state.VkErrorID(11805) << "Op" << spvOpcodeString(opcode) + << " must not be used on the OpTypeRuntimeArray inside a Uniform " + "block"; + } + } + return SPV_SUCCESS; } -spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state, - const Instruction* inst) { - std::string instr_name = - "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode()))); - +spv_result_t ValidateCooperativeMatrixLength(ValidationState_t& state, + const Instruction* inst, + bool is_khr, + uint32_t operand_index = 2) { + const spv::Op opcode = inst->opcode(); // Result type must be a 32-bit unsigned int. - auto result_type = state.FindDef(inst->type_id()); - if (result_type->opcode() != spv::Op::OpTypeInt || - result_type->GetOperandAs<uint32_t>(1) != 32 || - result_type->GetOperandAs<uint32_t>(2) != 0) { + const uint32_t result_type_id = inst->type_id(); + if (!state.IsIntScalarTypeWithSignedness(result_type_id, 0) || + state.GetBitWidth(inst->type_id()) != 32) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The Result Type of " << instr_name << " <id> " + << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> " << state.getIdName(inst->id()) << " must be OpTypeInt with width 32 and signedness 0."; } - bool isKhr = inst->opcode() == spv::Op::OpCooperativeMatrixLengthKHR; - auto type_id = inst->GetOperandAs<uint32_t>(2); + auto type_id = inst->GetOperandAs<uint32_t>(operand_index); auto type = state.FindDef(type_id); - if (isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + if (is_khr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The type in " << instr_name << " <id> " + << "The type in Op" << spvOpcodeString(opcode) << " <id> " << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixKHR."; - } else if (!isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { + } else if (!is_khr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { return state.diag(SPV_ERROR_INVALID_ID, inst) - << "The type in " << instr_name << " <id> " + << "The type in Op" << spvOpcodeString(opcode) << " <id> " << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV."; } return SPV_SUCCESS; @@ -2150,23 +2625,96 @@ } bool stride_required = false; + bool layout_requires_constant_stride = false; uint64_t layout; if (_.EvalConstantValUint64(layout_id, &layout)) { + const bool is_arm_layout = + (layout == + (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) || + (layout == + (uint64_t)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM); + + if (is_arm_layout) { + if (!_.HasCapability(spv::Capability::CooperativeMatrixLayoutsARM)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Using the RowBlockedInterleavedARM or " + "ColumnBlockedInterleavedARM MemoryLayout requires the " + "CooperativeMatrixLayoutsARM capability be declared"; + } + } + stride_required = (layout == (uint64_t)spv::CooperativeMatrixLayout::RowMajorKHR) || - (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR); + (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR) || + is_arm_layout; + layout_requires_constant_stride = is_arm_layout; } const auto stride_index = (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 4u : 3u; if (inst->operands().size() > stride_index) { const auto stride_id = inst->GetOperandAs<uint32_t>(stride_index); - const auto stride = _.FindDef(stride_id); - if (!stride || !_.IsIntScalarType(stride->type_id())) { + const auto stride_inst = _.FindDef(stride_id); + if (!stride_inst || !_.IsIntScalarType(stride_inst->type_id())) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Stride operand <id> " << _.getIdName(stride_id) << " must be a scalar integer type."; } + // Check SPV_ARM_cooperative_matrix_layouts constraints + if (layout_requires_constant_stride && + !spvOpcodeIsConstant(stride_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout " << layout + << " requires Stride come from a constant instruction."; + } + if (layout_requires_constant_stride) { + uint64_t stride; + if (_.EvalConstantValUint64(stride_id, &stride)) { + if ((layout == + (uint64_t) + spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) || + (layout == + (uint64_t) + spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM)) { + if ((stride != 1) && (stride != 2) && (stride != 4)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout " << layout + << " requires Stride be 1, 2, or 4."; + } + } + const uint32_t elty_id = matrix_type->GetOperandAs<uint32_t>(1); + const uint32_t rows_id = matrix_type->GetOperandAs<uint32_t>(3); + const uint32_t cols_id = matrix_type->GetOperandAs<uint32_t>(4); + uint64_t rows = 0, cols = 0; + _.EvalConstantValUint64(rows_id, &rows); + _.EvalConstantValUint64(cols_id, &cols); + uint32_t sizeof_component_in_bytes = _.GetBitWidth(elty_id) / 8; + uint64_t rows_required_multiple = 4; + uint64_t cols_required_multiple = 16 / sizeof_component_in_bytes; + + if (layout == + (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) { + cols_required_multiple *= stride; + } + if (layout == + (uint64_t) + spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM) { + rows_required_multiple *= stride; + } + if ((rows != 0) && (rows % rows_required_multiple != 0)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout " << layout << " with a Stride of " << stride + << " requires that the number of rows be a multiple of " + << rows_required_multiple; + } + if ((cols != 0) && (cols % cols_required_multiple != 0)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout " << layout << " with a Stride of " << stride + << " requires that the number of columns be a multiple of " + << cols_required_multiple; + } + } + } } else if (stride_required) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "MemoryLayout " << layout << " requires a Stride."; @@ -2182,6 +2730,27 @@ return SPV_SUCCESS; } +spv_result_t ValidateBufferPointerEXT(ValidationState_t& _, + const Instruction* inst) { + const auto storage_class_ptr = _.FindDef(inst->GetOperandAs<uint32_t>(0)); + if (storage_class_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR && + storage_class_ptr->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpBufferPointerEXT's Result Type should be " + << "a pointer type."; + } else { + // Buffer operand + auto buffer = + _.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2))); + if (!_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpBufferPointerEXT's buffer must be an untyped pointer" + << " into a variable declared with the ResourceHeapEXT built-in"; + } + } + return SPV_SUCCESS; +} + // Returns the number of instruction words taken up by a tensor addressing // operands argument and its implied operands. int TensorAddressingOperandsNumWords(spv::TensorAddressingOperandsMask mask) { @@ -2398,12 +2967,443 @@ return SPV_SUCCESS; } +spv_result_t ValidateInt32Operand(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index, + const char* opcode_name, + const char* operand_name) { + const auto type_id = + _.FindDef(inst->GetOperandAs<uint32_t>(operand_index))->type_id(); + if (!_.IsIntScalarType(type_id, 32)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " " << operand_name << " type <id> " + << _.getIdName(type_id) << " is not a 32 bit integer."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateInt32Or64Operand(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index, + const char* opcode_name, + const char* operand_name) { + const auto type_id = + _.FindDef(inst->GetOperandAs<uint32_t>(operand_index))->type_id(); + if (!_.IsIntScalarType(type_id) || + !(_.GetBitWidth(type_id) == 32 || _.GetBitWidth(type_id) == 64)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " " << operand_name << " type <id> " + << _.getIdName(type_id) << " is not a 32 or 64 bit integer."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorPointer(ValidationState_t& _, + const Instruction* inst, + const char* opname, + uint32_t pointer_index) { + const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == spv::AddressingModel::Logical) && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer <id> " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + + const auto pointer_type_id = pointer->type_id(); + const auto pointer_type = _.FindDef(pointer_type_id); + if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " type for pointer <id> " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + const auto storage_class_index = 1u; + const auto storage_class = + pointer_type->GetOperandAs<spv::StorageClass>(storage_class_index); + + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " storage class for pointer type <id> " + << _.getIdName(pointer_type_id) + << " is not Workgroup or StorageBuffer."; + } + + const auto pointee_id = pointer_type->GetOperandAs<uint32_t>(2); + const auto pointee_type = _.FindDef(pointee_id); + if (!pointee_type || + (pointee_type->opcode() != spv::Op::OpTypeArray && + pointee_type->opcode() != spv::Op::OpTypeRuntimeArray)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer <id> " << _.getIdName(pointer->id()) + << "s Type must be an array type."; + } + + const auto array_elem_type_id = pointee_type->GetOperandAs<uint32_t>(1); + auto array_elem_type = _.FindDef(array_elem_type_id); + if (!array_elem_type || !(_.IsIntScalarOrVectorType(array_elem_type_id) || + _.IsFloatScalarOrVectorType(array_elem_type_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer <id> " << _.getIdName(pointer->id()) + << "s Type must be an array of scalar or vector type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorLoadStoreNV(ValidationState_t& _, + const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) { + type_id = inst->type_id(); + opname = "spv::Op::OpCooperativeVectorLoadNV"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id(); + opname = "spv::Op::OpCooperativeVectorStoreNV"; + } + + auto vector_type = _.FindDef(type_id); + + if (vector_type->opcode() != spv::Op::OpTypeVectorIdEXT) { + if (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeVectorLoadNV Result Type <id> " + << _.getIdName(type_id) << " is not a cooperative vector type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeVectorStoreNV Object type <id> " + << _.getIdName(type_id) << " is not a cooperative vector type."; + } + } + + const auto pointer_index = + (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 2u : 0u; + + const auto offset_index = + (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 3u : 1u; + + if (auto error = + ValidateCooperativeVectorPointer(_, inst, opname, pointer_index)) { + return error; + } + + if (auto error = + ValidateInt32Or64Operand(_, inst, offset_index, opname, "Offset")) { + return error; + } + + const auto memory_access_index = + (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 4u : 3u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorOuterProductNV(ValidationState_t& _, + const Instruction* inst) { + const auto pointer_index = 0u; + const auto opcode_name = + "spv::Op::OpCooperativeVectorOuterProductAccumulateNV"; + + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + pointer_index)) { + return error; + } + + auto type_id = _.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id(); + auto a_type = _.FindDef(type_id); + + if (a_type->opcode() != spv::Op::OpTypeVectorIdEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " A type <id> " << _.getIdName(type_id) + << " is not a cooperative vector type."; + } + + type_id = _.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id(); + auto b_type = _.FindDef(type_id); + + if (b_type->opcode() != spv::Op::OpTypeVectorIdEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " B type <id> " << _.getIdName(type_id) + << " is not a cooperative vector type."; + } + + const auto a_component_type_id = a_type->GetOperandAs<uint32_t>(1); + const auto b_component_type_id = b_type->GetOperandAs<uint32_t>(1); + + if (a_component_type_id != b_component_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " A and B component types " + << _.getIdName(a_component_type_id) << " and " + << _.getIdName(b_component_type_id) << " do not match."; + } + + if (auto error = + ValidateInt32Or64Operand(_, inst, 1, opcode_name, "Offset")) { + return error; + } + + if (auto error = + ValidateInt32Operand(_, inst, 4, opcode_name, "MemoryLayout")) { + return error; + } + + if (auto error = ValidateInt32Operand(_, inst, 5, opcode_name, + "MatrixInterpretation")) { + return error; + } + + if (inst->operands().size() > 6) { + if (auto error = + ValidateInt32Operand(_, inst, 6, opcode_name, "MatrixStride")) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorReduceSumNV(ValidationState_t& _, + const Instruction* inst) { + const auto opcode_name = "spv::Op::OpCooperativeVectorReduceSumAccumulateNV"; + const auto pointer_index = 0u; + + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + pointer_index)) { + return error; + } + + auto type_id = _.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id(); + auto v_type = _.FindDef(type_id); + + if (v_type->opcode() != spv::Op::OpTypeVectorIdEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " V type <id> " << _.getIdName(type_id) + << " is not a cooperative vector type."; + } + + if (auto error = + ValidateInt32Or64Operand(_, inst, 1, opcode_name, "Offset")) { + return error; + } + + return SPV_SUCCESS; +} + +bool InterpretationIsPacked(spv::ComponentType interp) { + switch (interp) { + case spv::ComponentType::SignedInt8PackedNV: + case spv::ComponentType::UnsignedInt8PackedNV: + return true; + default: + return false; + } +} + +using std::get; + +spv_result_t ValidateCooperativeVectorMatrixMulNV(ValidationState_t& _, + const Instruction* inst) { + const bool has_bias = + inst->opcode() == spv::Op::OpCooperativeVectorMatrixMulAddNV; + const auto opcode_name = has_bias + ? "spv::Op::OpCooperativeVectorMatrixMulAddNV" + : "spv::Op::OpCooperativeVectorMatrixMulNV"; + + const auto bias_offset = has_bias ? 3 : 0; + + const auto result_type_index = 0u; + const auto input_index = 2u; + const auto input_interpretation_index = 3u; + const auto matrix_index = 4u; + const auto matrix_offset_index = 5u; + const auto matrix_interpretation_index = 6u; + const auto bias_index = 7u; + const auto bias_offset_index = 8u; + const auto bias_interpretation_index = 9u; + const auto m_index = 7u + bias_offset; + const auto k_index = 8u + bias_offset; + const auto memory_layout_index = 9u + bias_offset; + const auto transpose_index = 10u + bias_offset; + + const auto result_type_id = inst->GetOperandAs<uint32_t>(result_type_index); + const auto input_id = inst->GetOperandAs<uint32_t>(input_index); + const auto input_interpretation_id = + inst->GetOperandAs<uint32_t>(input_interpretation_index); + const auto matrix_interpretation_id = + inst->GetOperandAs<uint32_t>(matrix_interpretation_index); + const auto bias_interpretation_id = + inst->GetOperandAs<uint32_t>(bias_interpretation_index); + const auto m_id = inst->GetOperandAs<uint32_t>(m_index); + const auto k_id = inst->GetOperandAs<uint32_t>(k_index); + const auto memory_layout_id = + inst->GetOperandAs<uint32_t>(memory_layout_index); + const auto transpose_id = inst->GetOperandAs<uint32_t>(transpose_index); + + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + matrix_index)) { + return error; + } + + if (inst->opcode() == spv::Op::OpCooperativeVectorMatrixMulAddNV) { + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + bias_index)) { + return error; + } + } + + const auto result_type = _.FindDef(result_type_id); + + if (result_type->opcode() != spv::Op::OpTypeVectorIdEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " result type <id> " << _.getIdName(result_type_id) + << " is not a cooperative vector type."; + } + + const auto result_component_type_id = result_type->GetOperandAs<uint32_t>(1u); + if (!_.IsIntScalarType(result_component_type_id, 32) && + !_.IsFloatScalarType(result_component_type_id, 32) && + !_.IsFloatScalarType(result_component_type_id, 16)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " result component type <id> " + << _.getIdName(result_component_type_id) + << " is not a 32 bit int or 16/32 bit float."; + } + + const auto m_eval = _.EvalInt32IfConst(m_id); + const auto rc_eval = + _.EvalInt32IfConst(result_type->GetOperandAs<uint32_t>(2u)); + if (get<1>(m_eval) && get<1>(rc_eval) && get<2>(m_eval) != get<2>(rc_eval)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " result type number of components " + << get<2>(rc_eval) << " does not match M " << get<2>(m_eval); + } + + const auto k_eval = _.EvalInt32IfConst(k_id); + + const auto input = _.FindDef(input_id); + const auto input_type = _.FindDef(input->type_id()); + const auto input_num_components_id = input_type->GetOperandAs<uint32_t>(2u); + + auto input_interp_eval = _.EvalInt32IfConst(input_interpretation_id); + if (get<1>(input_interp_eval) && + !InterpretationIsPacked(spv::ComponentType{get<2>(input_interp_eval)})) { + const auto inc_eval = _.EvalInt32IfConst(input_num_components_id); + if (get<1>(inc_eval) && get<1>(k_eval) && + get<2>(inc_eval) != get<2>(k_eval)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " input number of components " + << get<2>(inc_eval) << " does not match K " << get<2>(k_eval); + } + } + + if (!_.IsBoolScalarType(_.FindDef(transpose_id)->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Transpose <id> " << _.getIdName(transpose_id) + << " is not a scalar boolean."; + } + + const auto check_constant = [&](uint32_t id, + const char* operand_name) -> spv_result_t { + if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " " << operand_name << " <id> " + << _.getIdName(id) << " is not a constant instruction."; + } + return SPV_SUCCESS; + }; + + if (auto error = + check_constant(input_interpretation_id, "InputInterpretation")) { + return error; + } + if (auto error = + check_constant(matrix_interpretation_id, "MatrixInterpretation")) { + return error; + } + if (has_bias) { + if (auto error = + check_constant(bias_interpretation_id, "BiasInterpretation")) { + return error; + } + } + if (auto error = check_constant(m_id, "M")) { + return error; + } + if (auto error = check_constant(k_id, "K")) { + return error; + } + if (auto error = check_constant(memory_layout_id, "MemoryLayout")) { + return error; + } + if (auto error = check_constant(transpose_id, "Transpose")) { + return error; + } + + if (auto error = ValidateInt32Operand(_, inst, input_interpretation_index, + opcode_name, "InputInterpretation")) { + return error; + } + if (auto error = ValidateInt32Operand(_, inst, matrix_interpretation_index, + opcode_name, "MatrixInterpretation")) { + return error; + } + if (has_bias) { + if (auto error = ValidateInt32Operand(_, inst, bias_interpretation_index, + opcode_name, "BiasInterpretation")) { + return error; + } + } + if (auto error = ValidateInt32Operand(_, inst, m_index, opcode_name, "M")) { + return error; + } + if (auto error = ValidateInt32Operand(_, inst, k_index, opcode_name, "K")) { + return error; + } + if (auto error = ValidateInt32Operand(_, inst, memory_layout_index, + opcode_name, "MemoryLayout")) { + return error; + } + + if (auto error = ValidateInt32Or64Operand(_, inst, matrix_offset_index, + opcode_name, "MatrixOffset")) { + return error; + } + if (has_bias) { + if (auto error = ValidateInt32Or64Operand(_, inst, bias_offset_index, + opcode_name, "BiasOffset")) { + return error; + } + } + + return SPV_SUCCESS; +} + spv_result_t ValidatePtrComparison(ValidationState_t& _, const Instruction* inst) { - if (_.addressing_model() == spv::AddressingModel::Logical && + const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); + const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u)); + const auto op1_type = _.FindDef(op1->type_id()); + const auto op2_type = _.FindDef(op2->type_id()); + spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u); + if ((_.addressing_model() == spv::AddressingModel::Logical || + _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) && + sc != spv::StorageClass::PhysicalStorageBuffer && !_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Instruction cannot for logical addressing model be used without " + << "Instruction on logical pointers cannot be used without " "a variable pointers capability"; } @@ -2420,20 +3420,40 @@ } } - const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); - const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u)); - if (!op1 || !op2 || op1->type_id() != op2->type_id()) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "The types of Operand 1 and Operand 2 must match"; - } - const auto op1_type = _.FindDef(op1->type_id()); if (!op1_type || (op1_type->opcode() != spv::Op::OpTypePointer && op1_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Operand type must be a pointer"; } - spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u); + if (!op2_type || (op2_type->opcode() != spv::Op::OpTypePointer && + op2_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand type must be a pointer"; + } + + if (inst->opcode() == spv::Op::OpPtrDiff) { + if (op1->type_id() != op2->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The types of Operand 1 and Operand 2 must match"; + } + } else { + const auto either_untyped = + op1_type->opcode() == spv::Op::OpTypeUntypedPointerKHR || + op2_type->opcode() == spv::Op::OpTypeUntypedPointerKHR; + if (either_untyped) { + const auto sc1 = op1_type->GetOperandAs<spv::StorageClass>(1); + const auto sc2 = op2_type->GetOperandAs<spv::StorageClass>(1); + if (sc1 != sc2) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer storage classes must match"; + } + } else if (op1->type_id() != op2->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The types of Operand 1 and Operand 2 must match"; + } + } + if (_.addressing_model() == spv::AddressingModel::Logical) { if (sc != spv::StorageClass::Workgroup && sc != spv::StorageClass::StorageBuffer) { @@ -2461,63 +3481,73 @@ switch (inst->opcode()) { case spv::Op::OpVariable: case spv::Op::OpUntypedVariableKHR: - if (auto error = ValidateVariable(_, inst)) return error; - break; + return ValidateVariable(_, inst); + case spv::Op::OpBufferPointerEXT: + return ValidateBufferPointerEXT(_, inst); case spv::Op::OpLoad: - if (auto error = ValidateLoad(_, inst)) return error; - break; + return ValidateLoad(_, inst); case spv::Op::OpStore: - if (auto error = ValidateStore(_, inst)) return error; - break; + return ValidateStore(_, inst); case spv::Op::OpCopyMemory: case spv::Op::OpCopyMemorySized: - if (auto error = ValidateCopyMemory(_, inst)) return error; - break; + return ValidateCopyMemory(_, inst); case spv::Op::OpPtrAccessChain: case spv::Op::OpUntypedPtrAccessChainKHR: case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: - if (auto error = ValidatePtrAccessChain(_, inst)) return error; - break; + return ValidatePtrAccessChain(_, inst); case spv::Op::OpAccessChain: case spv::Op::OpInBoundsAccessChain: case spv::Op::OpInBoundsPtrAccessChain: case spv::Op::OpUntypedAccessChainKHR: case spv::Op::OpUntypedInBoundsAccessChainKHR: - if (auto error = ValidateAccessChain(_, inst)) return error; - break; + return ValidateAccessChain(_, inst); case spv::Op::OpRawAccessChainNV: - if (auto error = ValidateRawAccessChain(_, inst)) return error; - break; + return ValidateRawAccessChain(_, inst); case spv::Op::OpArrayLength: case spv::Op::OpUntypedArrayLengthKHR: - if (auto error = ValidateArrayLength(_, inst)) return error; - break; + return ValidateArrayLength(_, inst); case spv::Op::OpCooperativeMatrixLoadNV: case spv::Op::OpCooperativeMatrixStoreNV: - if (auto error = ValidateCooperativeMatrixLoadStoreNV(_, inst)) - return error; - break; + return ValidateCooperativeMatrixLoadStoreNV(_, inst); case spv::Op::OpCooperativeMatrixLengthKHR: + return ValidateCooperativeMatrixLength(_, inst, true); case spv::Op::OpCooperativeMatrixLengthNV: - if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error; - break; + return ValidateCooperativeMatrixLength(_, inst, false); case spv::Op::OpCooperativeMatrixLoadKHR: case spv::Op::OpCooperativeMatrixStoreKHR: - if (auto error = ValidateCooperativeMatrixLoadStoreKHR(_, inst)) - return error; - break; + return ValidateCooperativeMatrixLoadStoreKHR(_, inst); case spv::Op::OpCooperativeMatrixLoadTensorNV: case spv::Op::OpCooperativeMatrixStoreTensorNV: - if (auto error = ValidateCooperativeMatrixLoadStoreTensorNV(_, inst)) - return error; - break; + return ValidateCooperativeMatrixLoadStoreTensorNV(_, inst); + case spv::Op::OpCooperativeVectorLoadNV: + case spv::Op::OpCooperativeVectorStoreNV: + return ValidateCooperativeVectorLoadStoreNV(_, inst); + case spv::Op::OpCooperativeVectorOuterProductAccumulateNV: + return ValidateCooperativeVectorOuterProductNV(_, inst); + case spv::Op::OpCooperativeVectorReduceSumAccumulateNV: + return ValidateCooperativeVectorReduceSumNV(_, inst); + case spv::Op::OpCooperativeVectorMatrixMulNV: + case spv::Op::OpCooperativeVectorMatrixMulAddNV: + return ValidateCooperativeVectorMatrixMulNV(_, inst); case spv::Op::OpPtrEqual: case spv::Op::OpPtrNotEqual: case spv::Op::OpPtrDiff: - if (auto error = ValidatePtrComparison(_, inst)) return error; - break; + return ValidatePtrComparison(_, inst); case spv::Op::OpImageTexelPointer: case spv::Op::OpGenericPtrMemSemantics: + break; // no validation currently + case spv::Op::OpSpecConstantOp: { + switch (inst->GetOperandAs<spv::Op>(2u)) { + case spv::Op::OpCooperativeMatrixLengthKHR: + return ValidateCooperativeMatrixLength(_, inst, true, 3); + case spv::Op::OpCooperativeMatrixLengthNV: + return ValidateCooperativeMatrixLength(_, inst, false, 3); + // TODO - Add AccesChains + default: + break; + } + } + default: break; }
diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp index dab7b5a..1cee9f1 100644 --- a/source/val/validate_memory_semantics.cpp +++ b/source/val/validate_memory_semantics.cpp
@@ -32,6 +32,9 @@ uint32_t value = 0; std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id); + const bool is_vulkan = spvIsVulkanEnv(_.context()->target_env) || + _.memory_model() == spv::MemoryModel::VulkanKHR; + if (!is_int32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) @@ -56,6 +59,21 @@ return SPV_SUCCESS; } + if (value & uint32_t(spv::MemorySemanticsMask::UniformMemory) && + !_.HasCapability(spv::Capability::Shader)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics UniformMemory requires capability Shader"; + } + + if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) && + !_.HasCapability(spv::Capability::VulkanMemoryModel)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics OutputMemoryKHR requires capability " + << "VulkanMemoryModelKHR"; + } + const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits( value & uint32_t(spv::MemorySemanticsMask::Acquire | spv::MemorySemanticsMask::Release | @@ -64,191 +82,207 @@ if (num_memory_order_set_bits > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": Memory Semantics can have at most one of the following " - "bits " - "set: Acquire, Release, AcquireRelease or " - "SequentiallyConsistent"; + << _.VkErrorID(10865) << spvOpcodeString(opcode) + << ": Memory Semantics must have at most one non-relaxed " + "memory order bit set"; } - if (_.memory_model() == spv::MemoryModel::VulkanKHR && - value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) { + if (is_vulkan && + (value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "SequentiallyConsistent memory " - "semantics cannot be used with " - "the VulkanKHR memory model."; + << _.VkErrorID(10866) << spvOpcodeString(opcode) + << ": Memory Semantics with SequentiallyConsistent memory order " + "must not be used in the Vulkan API"; } - if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) && - !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + if ((opcode == spv::Op::OpAtomicStore || + opcode == spv::Op::OpAtomicFlagClear) && + (value & uint32_t(spv::MemorySemanticsMask::Acquire) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": Memory Semantics MakeAvailableKHR requires capability " - << "VulkanMemoryModelKHR"; + << _.VkErrorID(10867) << spvOpcodeString(opcode) + << ": MemorySemantics must not use Acquire or AcquireRelease " + "memory order with " + << spvOpcodeString(opcode); } - if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) && - !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + if (opcode == spv::Op::OpAtomicLoad && + (value & uint32_t(spv::MemorySemanticsMask::Release) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": Memory Semantics MakeVisibleKHR requires capability " - << "VulkanMemoryModelKHR"; + << _.VkErrorID(10868) << spvOpcodeString(opcode) + << ": MemorySemantics must not use Release or AcquireRelease " + "memory order with " + << spvOpcodeString(opcode); } - if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) && - !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + // In OpenCL, a relaxed fence has no effect but is not explicitly forbidden + if (is_vulkan && opcode == spv::Op::OpMemoryBarrier && + !num_memory_order_set_bits) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": Memory Semantics OutputMemoryKHR requires capability " - << "VulkanMemoryModelKHR"; + << _.VkErrorID(10869) << spvOpcodeString(opcode) + << ": MemorySemantics must not use Relaxed memory order with " + << spvOpcodeString(opcode); + } + + if (is_vulkan) { + const bool includes_storage_class = + value & uint32_t(spv::MemorySemanticsMask::UniformMemory | + spv::MemorySemanticsMask::WorkgroupMemory | + spv::MemorySemanticsMask::ImageMemory | + spv::MemorySemanticsMask::OutputMemoryKHR); + + if (num_memory_order_set_bits && !includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10870) << spvOpcodeString(opcode) + << ": Memory Semantics with a non-relaxed memory order (Acquire, " + "Release, or AcquireRelease) must have at least one " + "Vulkan-supported storage class semantics bit set " + "(UniformMemory, WorkgroupMemory, ImageMemory, or " + "OutputMemory)"; + } + + if (!num_memory_order_set_bits && includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10871) << spvOpcodeString(opcode) + << ": Memory Semantics with at least one Vulkan-supported " + "storage class semantics bit set (UniformMemory, " + "WorkgroupMemory, ImageMemory, or OutputMemory) must use " + "a non-relaxed memory order (Acquire, Release, or " + "AcquireRelease)"; + } + } + + if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR)) { + if (!_.HasCapability(spv::Capability::VulkanMemoryModel)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics MakeAvailableKHR requires capability " + << "VulkanMemoryModelKHR"; + } + if (!(value & uint32_t(spv::MemorySemanticsMask::Release | + spv::MemorySemanticsMask::AcquireRelease))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10872) << spvOpcodeString(opcode) + << ": Memory Semantics with MakeAvailable bit set must use " + "Release or AcquireRelease memory order"; + } + } + + if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR)) { + if (!_.HasCapability(spv::Capability::VulkanMemoryModel)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics MakeVisibleKHR requires capability " + << "VulkanMemoryModelKHR"; + } + if (!(value & uint32_t(spv::MemorySemanticsMask::Acquire | + spv::MemorySemanticsMask::AcquireRelease))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10873) << spvOpcodeString(opcode) + << ": Memory Semantics with MakeVisible bit set must use Acquire " + "or AcquireRelease memory order"; + } } if (value & uint32_t(spv::MemorySemanticsMask::Volatile)) { - if (!_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + if (!_.HasCapability(spv::Capability::VulkanMemoryModel)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) << ": Memory Semantics Volatile requires capability " "VulkanMemoryModelKHR"; } - if (!spvOpcodeIsAtomicOp(inst->opcode())) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Memory Semantics Volatile can only be used with atomic " - "instructions"; + << _.VkErrorID(10874) << spvOpcodeString(opcode) + << ": Memory Semantics with Volatile bit set must not be used " + "with barrier instructions"; } } - if (value & uint32_t(spv::MemorySemanticsMask::UniformMemory) && - !_.HasCapability(spv::Capability::Shader)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": Memory Semantics UniformMemory requires capability Shader"; - } - - // Checking for spv::Capability::AtomicStorage is intentionally not done here. - // See https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning - // why. - - if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR | - spv::MemorySemanticsMask::MakeVisibleKHR)) { - const bool includes_storage_class = - value & uint32_t(spv::MemorySemanticsMask::UniformMemory | - spv::MemorySemanticsMask::SubgroupMemory | - spv::MemorySemanticsMask::WorkgroupMemory | - spv::MemorySemanticsMask::CrossWorkgroupMemory | - spv::MemorySemanticsMask::AtomicCounterMemory | - spv::MemorySemanticsMask::ImageMemory | - spv::MemorySemanticsMask::OutputMemoryKHR); - - if (!includes_storage_class) { + if ((opcode == spv::Op::OpAtomicCompareExchange || + opcode == spv::Op::OpAtomicCompareExchangeWeak) && + operand_index == 5) { + if (value & uint32_t(spv::MemorySemanticsMask::Release) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": expected Memory Semantics to include a storage class"; + << _.VkErrorID(10875) << spvOpcodeString(opcode) + << " Unequal Memory Semantics must not use Release or " + "AcquireRelease memory order"; } - } - if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) && - !(value & uint32_t(spv::MemorySemanticsMask::Acquire | - spv::MemorySemanticsMask::AcquireRelease))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": MakeVisibleKHR Memory Semantics also requires either Acquire " - "or AcquireRelease Memory Semantics"; - } + bool is_equal_int32 = false; + bool is_equal_const = false; + uint32_t equal_value = 0; + std::tie(is_equal_int32, is_equal_const, equal_value) = + _.EvalInt32IfConst(inst->GetOperandAs<uint32_t>(4)); - if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) && - !(value & uint32_t(spv::MemorySemanticsMask::Release | - spv::MemorySemanticsMask::AcquireRelease))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": MakeAvailableKHR Memory Semantics also requires either " - "Release or AcquireRelease Memory Semantics"; - } + const auto equal_mask_seq_cst = + uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent); + const auto equal_mask_acquire = uint32_t( + // Allow EqualMemorySemantics Release with UnequalMemorySemantics + // Acquire, since the C standard doesn't clearly forbid it. + spv::MemorySemanticsMask::SequentiallyConsistent | + spv::MemorySemanticsMask::AcquireRelease | + spv::MemorySemanticsMask::Release | spv::MemorySemanticsMask::Acquire); - if (spvIsVulkanEnv(_.context()->target_env)) { - const bool includes_storage_class = - value & uint32_t(spv::MemorySemanticsMask::UniformMemory | - spv::MemorySemanticsMask::WorkgroupMemory | - spv::MemorySemanticsMask::ImageMemory | - spv::MemorySemanticsMask::OutputMemoryKHR); - - if (opcode == spv::Op::OpMemoryBarrier && !num_memory_order_set_bits) { + if (((value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) && + !(equal_value & equal_mask_seq_cst)) || + ((value & uint32_t(spv::MemorySemanticsMask::Acquire)) && + !(equal_value & equal_mask_acquire))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4732) << spvOpcodeString(opcode) - << ": Vulkan specification requires Memory Semantics to have " - "one " - "of the following bits set: Acquire, Release, " - "AcquireRelease " - "or SequentiallyConsistent"; - } else if (opcode != spv::Op::OpMemoryBarrier && - num_memory_order_set_bits) { - // should leave only atomics and control barriers for Vulkan env - bool memory_is_int32 = false, memory_is_const_int32 = false; - uint32_t memory_value = 0; - std::tie(memory_is_int32, memory_is_const_int32, memory_value) = - _.EvalInt32IfConst(memory_scope); - if (memory_is_int32 && - spv::Scope(memory_value) == spv::Scope::Invocation) { + << _.VkErrorID(10876) << spvOpcodeString(opcode) + << " Unequal Memory Semantics must not use a stronger memory " + "order than the corresponding Equal Memory Semantics"; + } + + if (is_vulkan) { + auto storage_class_semantics_mask = + uint32_t(spv::MemorySemanticsMask::UniformMemory | + spv::MemorySemanticsMask::WorkgroupMemory | + spv::MemorySemanticsMask::ImageMemory | + spv::MemorySemanticsMask::OutputMemoryKHR); + + if (value & ~equal_value & storage_class_semantics_mask) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4641) << spvOpcodeString(opcode) - << ": Vulkan specification requires Memory Semantics to be None " - "if used with Invocation Memory Scope"; + << _.VkErrorID(10877) << spvOpcodeString(opcode) + << " Unequal Memory Semantics must not have any " + "Vulkan-supported storage class semantics bit set " + "(UniformMemory, WorkgroupMemory, ImageMemory, or " + "OutputMemory) unless this bit is also set in the " + "corresponding Equal Memory Semantics"; + } + + if (value & ~equal_value & + uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10878) << spvOpcodeString(opcode) + << " Unequal Memory Semantics must not have MakeVisible bit set " + "unless this bit is also set in the corresponding Equal " + "Memory Semantics"; + } + + if ((equal_value & uint32_t(spv::MemorySemanticsMask::Volatile)) ^ + (value & uint32_t(spv::MemorySemanticsMask::Volatile))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10879) << spvOpcodeString(opcode) + << " Unequal Memory Semantics must have Volatile bit set if and " + "only if this bit is also set in the corresponding Equal " + "Memory Semantics"; } } - - if (opcode == spv::Op::OpMemoryBarrier && !includes_storage_class) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4733) << spvOpcodeString(opcode) - << ": expected Memory Semantics to include a Vulkan-supported " - "storage class"; - } - - if (opcode == spv::Op::OpControlBarrier && value && !includes_storage_class) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4650) << spvOpcodeString(opcode) - << ": expected Memory Semantics to include a Vulkan-supported " - "storage class if Memory Semantics is not None"; - } } - if (opcode == spv::Op::OpAtomicFlagClear && - (value & uint32_t(spv::MemorySemanticsMask::Acquire) || - value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Memory Semantics Acquire and AcquireRelease cannot be used " - "with " - << spvOpcodeString(opcode); - } - - if (opcode == spv::Op::OpAtomicCompareExchange && operand_index == 5 && - (value & uint32_t(spv::MemorySemanticsMask::Release) || - value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": Memory Semantics Release and AcquireRelease cannot be " - "used " - "for operand Unequal"; - } - - if (spvIsVulkanEnv(_.context()->target_env)) { - if (opcode == spv::Op::OpAtomicLoad && - (value & uint32_t(spv::MemorySemanticsMask::Release) || - value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) || - value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) { + if (is_vulkan && num_memory_order_set_bits) { + bool memory_is_int32 = false, memory_is_const_int32 = false; + uint32_t memory_value = 0; + std::tie(memory_is_int32, memory_is_const_int32, memory_value) = + _.EvalInt32IfConst(memory_scope); + if (memory_is_int32 && spv::Scope(memory_value) == spv::Scope::Invocation) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4731) - << "Vulkan spec disallows OpAtomicLoad with Memory Semantics " - "Release, AcquireRelease and SequentiallyConsistent"; - } - - if (opcode == spv::Op::OpAtomicStore && - (value & uint32_t(spv::MemorySemanticsMask::Acquire) || - value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) || - value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4730) - << "Vulkan spec disallows OpAtomicStore with Memory Semantics " - "Acquire, AcquireRelease and SequentiallyConsistent"; + << _.VkErrorID(4641) << spvOpcodeString(opcode) + << ": Vulkan specification requires Memory Semantics to be " + "Relaxed if used with Invocation Memory Scope"; } }
diff --git a/source/val/validate_mesh_shading.cpp b/source/val/validate_mesh_shading.cpp index e569e25..dd9bb4a 100644 --- a/source/val/validate_mesh_shading.cpp +++ b/source/val/validate_mesh_shading.cpp
@@ -14,7 +14,8 @@ // Validates ray query instructions from SPV_KHR_ray_query -#include "source/opcode.h" +#include <string> + #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" @@ -22,96 +23,210 @@ namespace spvtools { namespace val { +bool IsInterfaceVariable(ValidationState_t& _, const Instruction* inst, + spv::ExecutionModel model) { + bool foundInterface = false; + for (auto entry_point : _.entry_points()) { + const auto* models = _.GetExecutionModels(entry_point); + if (models->find(model) == models->end()) return false; + for (const auto& desc : _.entry_point_descriptions(entry_point)) { + for (auto interface : desc.interfaces) { + if (inst->id() == interface) { + foundInterface = true; + break; + } + } + } + } + return foundInterface; +} + +spv_result_t ValidateEmitMeshTasks(ValidationState_t& _, + const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::TaskEXT) { + if (message) { + *message = "OpEmitMeshTasksEXT requires TaskEXT execution model"; + } + return false; + } + return true; + }); + + const uint32_t group_count_x = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(group_count_x) || + _.GetBitWidth(group_count_x) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count X must be a 32-bit unsigned int scalar"; + } + + const uint32_t group_count_y = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(group_count_y) || + _.GetBitWidth(group_count_y) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count Y must be a 32-bit unsigned int scalar"; + } + + const uint32_t group_count_z = _.GetOperandTypeId(inst, 2); + if (!_.IsUnsignedIntScalarType(group_count_z) || + _.GetBitWidth(group_count_z) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count Z must be a 32-bit unsigned int scalar"; + } + + if (inst->operands().size() == 4) { + const auto payload = _.FindDef(inst->GetOperandAs<uint32_t>(3)); + if (payload->opcode() != spv::Op::OpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be the result of a OpVariable"; + } + if (payload->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::TaskPayloadWorkgroupEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload OpVariable must have a storage class of " + "TaskPayloadWorkgroupEXT"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateSetMeshOutputs(ValidationState_t& _, + const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::MeshEXT) { + if (message) { + *message = "OpSetMeshOutputsEXT requires MeshEXT execution model"; + } + return false; + } + return true; + }); + + const uint32_t vertex_count = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(vertex_count) || + _.GetBitWidth(vertex_count) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vertex Count must be a 32-bit unsigned int scalar"; + } + + const uint32_t primitive_count = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(primitive_count) || + _.GetBitWidth(primitive_count) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Primitive Count must be a 32-bit unsigned int scalar"; + } + + // Will only validate if constants are used (or spec constant frozen) + uint64_t vertex_count_value = 0; + if (_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(0), + &vertex_count_value)) { + _.function(inst->function()->id()) + ->RegisterLimitation( + [vertex_count_value](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const uint32_t output_vertices = + state.GetOutputVertices(entry_point->id()); + if (vertex_count_value > output_vertices) { + *message = + "OpSetMeshOutputsEXT Vertex Count (" + + std::to_string(vertex_count_value) + + ") is larger than the OutputVertices in OpExecutionMode (" + + std::to_string(output_vertices) + ")."; + return false; + } + return true; + }); + } + uint64_t primitive_count_value = 0; + if (_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(1), + &primitive_count_value)) { + _.function(inst->function()->id()) + ->RegisterLimitation( + [primitive_count_value](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const uint32_t output_primitives = + state.GetOutputPrimitivesEXT(entry_point->id()); + if (primitive_count_value > output_primitives) { + *message = "OpSetMeshOutputsEXT Primitive Count (" + + std::to_string(primitive_count_value) + + ") is larger than the OutputPrimitivesEXT in " + "OpExecutionMode (" + + std::to_string(output_primitives) + ")."; + return false; + } + return true; + }); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateMeshVariable(ValidationState_t& _, + const Instruction* inst) { + if (!_.HasCapability(spv::Capability::MeshShadingEXT)) { + return SPV_SUCCESS; + } + bool is_mesh_interface_var = + IsInterfaceVariable(_, inst, spv::ExecutionModel::MeshEXT); + bool is_frag_interface_var = + IsInterfaceVariable(_, inst, spv::ExecutionModel::Fragment); + + const spv::StorageClass storage_class = + inst->GetOperandAs<spv::StorageClass>(2); + bool storage_output = (storage_class == spv::StorageClass::Output); + bool storage_input = (storage_class == spv::StorageClass::Input); + + if (_.HasDecoration(inst->id(), spv::Decoration::PerPrimitiveEXT)) { + if (is_frag_interface_var && !storage_input) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "PerPrimitiveEXT decoration must be applied only to " + "variables in the Input Storage Class in the Fragment " + "Execution Model."; + } + + if (is_mesh_interface_var && !storage_output) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4336) + << "PerPrimitiveEXT decoration must be applied only to " + "variables in the Output Storage Class in the " + "Storage Class in the MeshEXT Execution Model."; + } + } + + // This only applies to user interface variables, not built-ins (they + // are validated with the rest of the builtin) + if (is_mesh_interface_var && storage_output && + !_.HasDecoration(inst->id(), spv::Decoration::BuiltIn)) { + const Instruction* pointer_inst = _.FindDef(inst->type_id()); + if (pointer_inst->opcode() == spv::Op::OpTypePointer) { + if (!_.IsArrayType(pointer_inst->word(3))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the MeshEXT Execution Mode, all Output Variables " + "must contain an Array."; + } + } + } + + return SPV_SUCCESS; +} + spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) { const spv::Op opcode = inst->opcode(); switch (opcode) { - case spv::Op::OpEmitMeshTasksEXT: { - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation( - [](spv::ExecutionModel model, std::string* message) { - if (model != spv::ExecutionModel::TaskEXT) { - if (message) { - *message = - "OpEmitMeshTasksEXT requires TaskEXT execution model"; - } - return false; - } - return true; - }); - - const uint32_t group_count_x = _.GetOperandTypeId(inst, 0); - if (!_.IsUnsignedIntScalarType(group_count_x) || - _.GetBitWidth(group_count_x) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Group Count X must be a 32-bit unsigned int scalar"; - } - - const uint32_t group_count_y = _.GetOperandTypeId(inst, 1); - if (!_.IsUnsignedIntScalarType(group_count_y) || - _.GetBitWidth(group_count_y) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Group Count Y must be a 32-bit unsigned int scalar"; - } - - const uint32_t group_count_z = _.GetOperandTypeId(inst, 2); - if (!_.IsUnsignedIntScalarType(group_count_z) || - _.GetBitWidth(group_count_z) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Group Count Z must be a 32-bit unsigned int scalar"; - } - - if (inst->operands().size() == 4) { - const auto payload = _.FindDef(inst->GetOperandAs<uint32_t>(3)); - if (payload->opcode() != spv::Op::OpVariable) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Payload must be the result of a OpVariable"; - } - if (payload->GetOperandAs<spv::StorageClass>(2) != - spv::StorageClass::TaskPayloadWorkgroupEXT) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Payload OpVariable must have a storage class of " - "TaskPayloadWorkgroupEXT"; - } - } - break; - } - - case spv::Op::OpSetMeshOutputsEXT: { - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation( - [](spv::ExecutionModel model, std::string* message) { - if (model != spv::ExecutionModel::MeshEXT) { - if (message) { - *message = - "OpSetMeshOutputsEXT requires MeshEXT execution model"; - } - return false; - } - return true; - }); - - const uint32_t vertex_count = _.GetOperandTypeId(inst, 0); - if (!_.IsUnsignedIntScalarType(vertex_count) || - _.GetBitWidth(vertex_count) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Vertex Count must be a 32-bit unsigned int scalar"; - } - - const uint32_t primitive_count = _.GetOperandTypeId(inst, 1); - if (!_.IsUnsignedIntScalarType(primitive_count) || - _.GetBitWidth(primitive_count) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Primitive Count must be a 32-bit unsigned int scalar"; - } - - break; - } - - case spv::Op::OpWritePackedPrimitiveIndices4x8NV: { - // No validation rules (for the moment). - break; - } - + case spv::Op::OpEmitMeshTasksEXT: + return ValidateEmitMeshTasks(_, inst); + case spv::Op::OpSetMeshOutputsEXT: + return ValidateSetMeshOutputs(_, inst); + case spv::Op::OpVariable: + return ValidateMeshVariable(_, inst); + // No validation rules (for the moment). + case spv::Op::OpWritePackedPrimitiveIndices4x8NV: default: break; }
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp index a404134..99087d6 100644 --- a/source/val/validate_misc.cpp +++ b/source/val/validate_misc.cpp
@@ -13,11 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/val/validate.h" - #include "source/opcode.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" +#include "source/val/validate.h" #include "source/val/validate_scopes.h" #include "source/val/validation_state.h" @@ -113,6 +112,22 @@ return SPV_SUCCESS; } +spv_result_t ValidateAbort(ValidationState_t& _, const Instruction* inst) { + const auto message_type = _.FindDef(inst->GetOperandAs<uint32_t>(0u)); + const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(1u)); + const auto source_type = _.FindDef(source->type_id()); + + if (source_type == message_type) return SPV_SUCCESS; + + if (!_.LogicallyMatch(source_type, message_type, false)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Type of Message operand does not logically match the type of " + "the Message Type operand"; + } + + return SPV_SUCCESS; +} + } // namespace spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { @@ -203,6 +218,11 @@ return error; } break; + case spv::Op::OpAbortKHR: + if (auto error = ValidateAbort(_, inst)) { + return error; + } + break; default: break; }
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index 8502fda..88392d2 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +18,7 @@ #include "source/opcode.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" @@ -24,6 +27,48 @@ namespace val { namespace { +// TODO - Make a common util if someone else needs it too outside this file +const char* ExecutionModelToString(spv::ExecutionModel value) { + switch (value) { + case spv::ExecutionModel::Vertex: + return "Vertex"; + case spv::ExecutionModel::TessellationControl: + return "TessellationControl"; + case spv::ExecutionModel::TessellationEvaluation: + return "TessellationEvaluation"; + case spv::ExecutionModel::Geometry: + return "Geometry"; + case spv::ExecutionModel::Fragment: + return "Fragment"; + case spv::ExecutionModel::GLCompute: + return "GLCompute"; + case spv::ExecutionModel::Kernel: + return "Kernel"; + case spv::ExecutionModel::TaskNV: + return "TaskNV"; + case spv::ExecutionModel::MeshNV: + return "MeshNV"; + case spv::ExecutionModel::RayGenerationKHR: + return "RayGenerationKHR"; + case spv::ExecutionModel::IntersectionKHR: + return "IntersectionKHR"; + case spv::ExecutionModel::AnyHitKHR: + return "AnyHitKHR"; + case spv::ExecutionModel::ClosestHitKHR: + return "ClosestHitKHR"; + case spv::ExecutionModel::MissKHR: + return "MissKHR"; + case spv::ExecutionModel::CallableKHR: + return "CallableKHR"; + case spv::ExecutionModel::TaskEXT: + return "TaskEXT"; + case spv::ExecutionModel::MeshEXT: + return "MeshEXT"; + default: + return "Unknown"; + } +} + spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { const auto entry_point_id = inst->GetOperandAs<uint32_t>(1); auto entry_point = _.FindDef(entry_point_id); @@ -56,20 +101,22 @@ } const auto* execution_modes = _.GetExecutionModes(entry_point_id); + auto has_mode = [&execution_modes](spv::ExecutionMode mode) { + return execution_modes && execution_modes->count(mode); + }; + if (_.HasCapability(spv::Capability::Shader)) { switch (execution_model) { case spv::ExecutionModel::Fragment: - if (execution_modes && - execution_modes->count(spv::ExecutionMode::OriginUpperLeft) && - execution_modes->count(spv::ExecutionMode::OriginLowerLeft)) { + if (has_mode(spv::ExecutionMode::OriginUpperLeft) && + has_mode(spv::ExecutionMode::OriginLowerLeft)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Fragment execution model entry points can only specify " "one of OriginUpperLeft or OriginLowerLeft execution " "modes."; } - if (!execution_modes || - (!execution_modes->count(spv::ExecutionMode::OriginUpperLeft) && - !execution_modes->count(spv::ExecutionMode::OriginLowerLeft))) { + if (!has_mode(spv::ExecutionMode::OriginUpperLeft) && + !has_mode(spv::ExecutionMode::OriginLowerLeft)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Fragment execution model entry points require either an " "OriginUpperLeft or OriginLowerLeft execution mode."; @@ -282,40 +329,95 @@ } } + bool has_workgroup_size = false; + bool has_local_size_id = false; + for (auto& i : _.ordered_instructions()) { + if (i.opcode() == spv::Op::OpFunction) break; + if (i.opcode() == spv::Op::OpDecorate && i.operands().size() > 2) { + if (i.GetOperandAs<spv::Decoration>(1) == spv::Decoration::BuiltIn && + i.GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) { + has_workgroup_size = true; + } + } + if (i.opcode() == spv::Op::OpExecutionModeId) { + if (i.GetOperandAs<spv::ExecutionMode>(1) == + spv::ExecutionMode::LocalSizeId) { + has_local_size_id = true; + } + } + } + if (spvIsVulkanEnv(_.context()->target_env)) { + // SPV_QCOM_tile_shading checks + if (execution_model == spv::ExecutionModel::GLCompute) { + if (_.HasCapability(spv::Capability::TileShadingQCOM)) { + if (has_mode(spv::ExecutionMode::TileShadingRateQCOM) && + (has_mode(spv::ExecutionMode::LocalSize) || + has_mode(spv::ExecutionMode::LocalSizeId))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If the TileShadingRateQCOM execution mode is used, " + << "LocalSize and LocalSizeId must not be specified."; + } + if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The NonCoherentTileAttachmentQCOM execution mode must " + "not be used in any stage other than fragment."; + } + } else { + if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If the TileShadingRateQCOM execution mode is used, the " + "TileShadingQCOM capability must be enabled."; + } + } + } else { + if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The TileShadingRateQCOM execution mode must not be used " + "in any stage other than compute."; + } + if (execution_model != spv::ExecutionModel::Fragment) { + if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The NonCoherentTileAttachmentQCOM execution mode must " + "not be used in any stage other than fragment."; + } + if (_.HasCapability(spv::Capability::TileShadingQCOM)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "The TileShadingQCOM capability must not be enabled in " + "any stage other than compute or fragment."; + } + } else { + if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) { + if (!_.HasCapability(spv::Capability::TileShadingQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "If the NonCoherentTileAttachmentReadQCOM execution " + "mode is used, the TileShadingQCOM capability must be " + "enabled."; + } + } + } + } + switch (execution_model) { case spv::ExecutionModel::GLCompute: - if (!execution_modes || - !execution_modes->count(spv::ExecutionMode::LocalSize)) { - bool ok = false; - for (auto& i : _.ordered_instructions()) { - if (i.opcode() == spv::Op::OpDecorate) { - if (i.operands().size() > 2) { - if (i.GetOperandAs<spv::Decoration>(1) == - spv::Decoration::BuiltIn && - i.GetOperandAs<spv::BuiltIn>(2) == - spv::BuiltIn::WorkgroupSize) { - ok = true; - break; - } - } - } - if (i.opcode() == spv::Op::OpExecutionModeId) { - const auto mode = i.GetOperandAs<spv::ExecutionMode>(1); - if (mode == spv::ExecutionMode::LocalSizeId) { - ok = true; - break; - } - } - } - if (!ok) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(6426) - << "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."; - } + case spv::ExecutionModel::MeshEXT: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::TaskEXT: + case spv::ExecutionModel::TaskNV: + if (!has_mode(spv::ExecutionMode::LocalSize) && !has_workgroup_size && + !has_local_size_id && + !has_mode(spv::ExecutionMode::TileShadingRateQCOM)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10685) << "In the Vulkan environment, " + << ExecutionModelToString(execution_model) + << " execution model " + "entry points require either the " + << (_.HasCapability(spv::Capability::TileShadingQCOM) + ? "TileShadingRateQCOM, " + : "") + << "LocalSize or LocalSizeId execution mode or an object " + "decorated with WorkgroupSize must be specified."; } break; default: @@ -323,6 +425,100 @@ } } + // WorkgroupSize decoration takes precedence over any LocalSize or LocalSizeId + // execution mode, so the values can be ignored + if (_.EntryPointHasLocalSizeOrId(entry_point_id) && !has_workgroup_size) { + const Instruction* local_size_inst = + _.EntryPointLocalSizeOrId(entry_point_id); + if (local_size_inst) { + const auto mode = local_size_inst->GetOperandAs<spv::ExecutionMode>(1); + const uint32_t operand_x = local_size_inst->GetOperandAs<uint32_t>(2); + const uint32_t operand_y = local_size_inst->GetOperandAs<uint32_t>(3); + const uint32_t operand_z = local_size_inst->GetOperandAs<uint32_t>(4); + if (mode == spv::ExecutionMode::LocalSize) { + const uint64_t product_size = operand_x * operand_y * operand_z; + if (product_size == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << "Local Size execution mode must not have a product of zero " + "(X " + "= " + << operand_x << ", Y = " << operand_y << ", Z = " << operand_z + << ")."; + } + if (has_mode(spv::ExecutionMode::DerivativeGroupQuadsKHR)) { + if (operand_x % 2 != 0 || operand_y % 2 != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << _.VkErrorID(10151) + << "Local Size execution mode dimensions is " + "(X = " + << operand_x << ", Y = " << operand_y + << ") but Entry Point id " << entry_point_id + << " also has an DerivativeGroupQuadsKHR execution mode, so " + "both dimensions must be a multiple of 2"; + } + } + if (has_mode(spv::ExecutionMode::DerivativeGroupLinearKHR)) { + if (product_size % 4 != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << _.VkErrorID(10152) + << "Local Size execution mode dimensions is (X = " + << operand_x << ", Y = " << operand_y + << ", Z = " << operand_z << ") but Entry Point id " + << entry_point_id + << " also has an DerivativeGroupLinearKHR execution mode, " + "so " + "the product (" + << product_size << ") must be a multiple of 4"; + } + } + } else if (mode == spv::ExecutionMode::LocalSizeId) { + // can only validate product if static and not spec constant + // (This is done for us in EvalConstantValUint64) + uint64_t x_size, y_size, z_size; + bool static_x = _.EvalConstantValUint64(operand_x, &x_size); + bool static_y = _.EvalConstantValUint64(operand_y, &y_size); + bool static_z = _.EvalConstantValUint64(operand_z, &z_size); + if (static_x && static_y && static_z) { + const uint64_t product_size = x_size * y_size * z_size; + if (product_size == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << "LocalSizeId execution mode must not have a product of " + "zero " + "(X = " + << x_size << ", Y = " << y_size << ", Z = " << z_size + << ")."; + } + if (has_mode(spv::ExecutionMode::DerivativeGroupQuadsKHR)) { + if (x_size % 2 != 0 || y_size % 2 != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << _.VkErrorID(10151) + << "LocalSizeId execution mode dimensions is " + "(X = " + << x_size << ", Y = " << y_size << ") but Entry Point id " + << entry_point_id + << " also has an DerivativeGroupQuadsKHR execution mode, " + "so " + "both dimensions must be a multiple of 2"; + } + } + if (has_mode(spv::ExecutionMode::DerivativeGroupLinearKHR)) { + if (product_size % 4 != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << _.VkErrorID(10152) + << "LocalSizeId execution mode dimensions is (X = " + << x_size << ", Y = " << y_size << ", Z = " << z_size + << ") but Entry Point id " << entry_point_id + << " also has an DerivativeGroupLinearKHR execution mode, " + "so " + "the product (" + << product_size << ") must be a multiple of 4"; + } + } + } + } + } + } + return SPV_SUCCESS; } @@ -347,6 +543,12 @@ case spv::ExecutionMode::LocalSizeId: case spv::ExecutionMode::FPFastMathDefault: case spv::ExecutionMode::MaximumRegistersIdINTEL: + case spv::ExecutionMode::IsApiEntryAMDX: + case spv::ExecutionMode::MaxNodeRecursionAMDX: + case spv::ExecutionMode::MaxNumWorkgroupsAMDX: + case spv::ExecutionMode::ShaderIndexAMDX: + case spv::ExecutionMode::SharesInputWithAMDX: + case spv::ExecutionMode::StaticNumWorkgroupsAMDX: valid_mode = true; break; default: @@ -368,6 +570,12 @@ case spv::ExecutionMode::SubgroupsPerWorkgroupId: case spv::ExecutionMode::LocalSizeHintId: case spv::ExecutionMode::LocalSizeId: + case spv::ExecutionMode::IsApiEntryAMDX: + case spv::ExecutionMode::MaxNodeRecursionAMDX: + case spv::ExecutionMode::MaxNumWorkgroupsAMDX: + case spv::ExecutionMode::ShaderIndexAMDX: + case spv::ExecutionMode::SharesInputWithAMDX: + case spv::ExecutionMode::StaticNumWorkgroupsAMDX: if (!spvOpcodeIsConstant(operand_inst->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "For OpExecutionModeId all Extra Operand ids must be " @@ -426,13 +634,20 @@ } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId || mode == spv::ExecutionMode::LocalSizeHintId || mode == spv::ExecutionMode::LocalSizeId || - mode == spv::ExecutionMode::FPFastMathDefault) { + mode == spv::ExecutionMode::FPFastMathDefault || + mode == spv::ExecutionMode::IsApiEntryAMDX || + mode == spv::ExecutionMode::MaxNodeRecursionAMDX || + mode == spv::ExecutionMode::MaxNumWorkgroupsAMDX || + mode == spv::ExecutionMode::ShaderIndexAMDX || + mode == spv::ExecutionMode::SharesInputWithAMDX || + mode == spv::ExecutionMode::StaticNumWorkgroupsAMDX) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "OpExecutionMode is only valid when the Mode operand is an " "execution mode that takes no Extra Operands, or takes Extra " "Operands that are not id operands."; } + const bool is_vulkan_env = (spvIsVulkanEnv(_.context()->target_env)); const auto* models = _.GetExecutionModels(entry_point_id); switch (mode) { case spv::ExecutionMode::Invocations: @@ -543,6 +758,15 @@ "tessellation execution model."; } } + if (is_vulkan_env) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + inst->GetOperandAs<uint32_t>(2) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7330) + << "In mesh shaders using the MeshEXT Execution Model the " + "OutputVertices Execution Mode must be greater than 0"; + } + } break; case spv::ExecutionMode::OutputLinesEXT: case spv::ExecutionMode::OutputTrianglesEXT: @@ -557,6 +781,15 @@ "execution " "model."; } + if (mode == spv::ExecutionMode::OutputPrimitivesEXT && is_vulkan_env) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + inst->GetOperandAs<uint32_t>(2) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7331) + << "In mesh shaders using the MeshEXT Execution Model the " + "OutputPrimitivesEXT Execution Mode must be greater than 0"; + } + } break; case spv::ExecutionMode::QuadDerivativesKHR: if (!std::all_of(models->begin(), models->end(), @@ -577,6 +810,7 @@ case spv::ExecutionMode::DepthGreater: case spv::ExecutionMode::DepthLess: case spv::ExecutionMode::DepthUnchanged: + case spv::ExecutionMode::StencilRefReplacingEXT: case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT: case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT: case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT: @@ -586,6 +820,7 @@ case spv::ExecutionMode::SampleInterlockUnorderedEXT: case spv::ExecutionMode::ShadingRateInterlockOrderedEXT: case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT: + case spv::ExecutionMode::PostDepthCoverage: case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD: case spv::ExecutionMode::StencilRefUnchangedFrontAMD: case spv::ExecutionMode::StencilRefGreaterFrontAMD: @@ -618,9 +853,15 @@ break; case spv::ExecutionMode::LocalSize: case spv::ExecutionMode::LocalSizeId: - if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed()) + if (mode == spv::ExecutionMode::LocalSizeId && + !_.IsLocalSizeIdAllowed()) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "LocalSizeId mode is not allowed by the current environment."; + << "LocalSizeId mode is not allowed by the current environment." + << (is_vulkan_env + ? _.MissingFeature("maintenance4 feature", + "--allow-localsizeid", false) + : ""); + } if (!std::all_of( models->begin(), models->end(), @@ -669,7 +910,7 @@ } } - if (spvIsVulkanEnv(_.context()->target_env)) { + if (is_vulkan_env) { if (mode == spv::ExecutionMode::OriginLowerLeft) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4653) @@ -682,6 +923,14 @@ << "In the Vulkan environment, the PixelCenterInteger execution " "mode must not be used."; } + if (mode == spv::ExecutionMode::TileShadingRateQCOM) { + const auto rateX = inst->GetOperandAs<int>(2); + const auto rateY = inst->GetOperandAs<int>(3); + if ((rateX & (rateX - 1)) != 0 || (rateY & (rateY - 1)) != 0) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The TileShadingRateQCOM execution mode's x and y values " + "must be powers of 2."; + } } return SPV_SUCCESS; @@ -743,6 +992,20 @@ } } +spv_result_t ValidateCapability(ValidationState_t& _, const Instruction* inst) { + auto cap = inst->GetOperandAs<spv::Capability>(0); + if (cap == spv::Capability::CooperativeMatrixKHR) { + if (_.HasCapability(spv::Capability::Shader) && + !_.HasCapability(spv::Capability::VulkanMemoryModel)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "If the Shader and CooperativeMatrixKHR capabilities are " + "declared, the VulkanMemoryModel capability must also be " + "declared"; + } + } + return SPV_SUCCESS; +} + } // namespace spv_result_t ValidateFloatControls2(ValidationState_t& _) { @@ -821,6 +1084,9 @@ case spv::Op::OpMemoryModel: if (auto error = ValidateMemoryModel(_, inst)) return error; break; + case spv::Op::OpCapability: + if (auto error = ValidateCapability(_, inst)) return error; + break; default: break; } @@ -833,12 +1099,12 @@ std::set<PerEntryKey> seen_per_entry; std::set<PerOperandKey> seen_per_operand; - const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string { - spv_operand_desc desc = nullptr; - if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE, - static_cast<uint32_t>(mode), - &desc) == SPV_SUCCESS) { - return std::string(desc->name); + const auto lookupMode = [](spv::ExecutionMode mode) -> std::string { + const spvtools::OperandDesc* desc = nullptr; + if (spvtools::LookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE, + static_cast<uint32_t>(mode), + &desc) == SPV_SUCCESS) { + return std::string(desc->name().data()); } return "Unknown"; };
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp index fd422a5..9711865 100644 --- a/source/val/validate_non_uniform.cpp +++ b/source/val/validate_non_uniform.cpp
@@ -130,7 +130,7 @@ if (!spvOpcodeIsConstant(id_op)) { std::string operand = GetOperandName(inst->opcode()); return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Before SPIR-V 1.5, " << operand + << "In SPIR-V 1.4 or earlier, " << operand << " must be a constant instruction"; } }
diff --git a/source/val/validate_pipe.cpp b/source/val/validate_pipe.cpp new file mode 100644 index 0000000..2890272 --- /dev/null +++ b/source/val/validate_pipe.cpp
@@ -0,0 +1,353 @@ +// Copyright (c) 2026 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of Pipe SPIR-V instructions. + +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" +#include "spirv/unified1/spirv.hpp11" + +namespace spvtools { +namespace val { +namespace { + +enum class ValidPipeType { + READ_ONLY, + WRITE_ONLY, + READ_OR_WRITE, // still excludes Read AND Write +}; + +spv_result_t ValidatePipeType(ValidationState_t& _, const Instruction* inst, + uint32_t operand, ValidPipeType valid_pt) { + const Instruction* pipe_type = _.FindDef(_.GetOperandTypeId(inst, operand)); + if (pipe_type->opcode() != spv::Op::OpTypePipe) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pipe must be a type of OpTypePipe."; + } + + const auto access_qualifier = + pipe_type->GetOperandAs<spv::AccessQualifier>(1); + if (valid_pt == ValidPipeType::READ_ONLY) { + if (access_qualifier != spv::AccessQualifier::ReadOnly) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pipe must have a OpTypePipe with ReadOnly access qualifier."; + } + } else if (valid_pt == ValidPipeType::WRITE_ONLY) { + if (access_qualifier != spv::AccessQualifier::WriteOnly) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pipe must have a OpTypePipe with WriteOnly access qualifier."; + } + } else if (valid_pt == ValidPipeType::READ_OR_WRITE) { + if (access_qualifier != spv::AccessQualifier::ReadOnly && + access_qualifier != spv::AccessQualifier::WriteOnly) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pipe must have a OpTypePipe with ReadOnly or WriteOnly access " + "qualifier."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidatePacketSizeAlign(ValidationState_t& _, + const Instruction* inst, + uint32_t size_operand, + uint32_t alignment_operand) { + const uint32_t packet_size_id = _.GetOperandTypeId(inst, size_operand); + if (!_.IsIntScalarType(packet_size_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Packet Size must be a 32-bit scalar integer."; + } + + const uint32_t packet_alignment_id = + _.GetOperandTypeId(inst, alignment_operand); + if (!_.IsIntScalarType(packet_alignment_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Packet Alignment must be a 32-bit scalar integer."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateReadWritePipe(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be a 32-bit int scalar."; + } + + if (inst->opcode() == spv::Op::OpReadPipe) { + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_ONLY)) + return error; + } else if (inst->opcode() == spv::Op::OpWritePipe) { + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::WRITE_ONLY)) + return error; + } + + const Instruction* pointer_type = _.FindDef(_.GetOperandTypeId(inst, 3)); + if (pointer_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer must be a type of OpTypePointer."; + } + if (pointer_type->GetOperandAs<spv::StorageClass>(1) != + spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer must be a OpTypePointer with a Generic storage class."; + } + + if (auto error = ValidatePacketSizeAlign(_, inst, 4, 5)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateReservedReadWritePipe(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be a 32-bit int scalar."; + } + + if (inst->opcode() == spv::Op::OpReservedReadPipe) { + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_ONLY)) + return error; + } else if (inst->opcode() == spv::Op::OpReservedWritePipe) { + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::WRITE_ONLY)) + return error; + } + + const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 3)); + if (reserve_id->opcode() != spv::Op::OpTypeReserveId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reserve Id type must be OpTypeReserveId."; + } + + const uint32_t index_id = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(index_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Index must be a 32-bit scalar integer."; + } + + const Instruction* pointer_type = _.FindDef(_.GetOperandTypeId(inst, 5)); + if (pointer_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer must be a type of OpTypePointer."; + } + if (pointer_type->GetOperandAs<spv::StorageClass>(1) != + spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer must be a OpTypePointer with a Generic storage class."; + } + + if (auto error = ValidatePacketSizeAlign(_, inst, 6, 7)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateReservePackets(ValidationState_t& _, + const Instruction* inst) { + const Instruction* result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypeReserveId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be OpTypeReserveId."; + } + + if (inst->opcode() == spv::Op::OpReserveReadPipePackets) { + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_ONLY)) + return error; + } else if (inst->opcode() == spv::Op::OpReserveWritePipePackets) { + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::WRITE_ONLY)) + return error; + } + + const uint32_t num_packets_id = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(num_packets_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Num Packets must be a 32-bit scalar integer."; + } + + if (auto error = ValidatePacketSizeAlign(_, inst, 4, 5)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupReservePackets(ValidationState_t& _, + const Instruction* inst) { + const Instruction* result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypeReserveId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be OpTypeReserveId."; + } + + if (inst->opcode() == spv::Op::OpGroupReserveReadPipePackets) { + if (auto error = ValidatePipeType(_, inst, 3, ValidPipeType::READ_ONLY)) + return error; + } else if (inst->opcode() == spv::Op::OpGroupReserveWritePipePackets) { + if (auto error = ValidatePipeType(_, inst, 3, ValidPipeType::WRITE_ONLY)) + return error; + } + + const uint32_t num_packets_id = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(num_packets_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Num Packets must be a 32-bit scalar integer."; + } + + if (auto error = ValidatePacketSizeAlign(_, inst, 5, 6)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateCommitPipe(ValidationState_t& _, const Instruction* inst) { + if (inst->opcode() == spv::Op::OpCommitReadPipe) { + if (auto error = ValidatePipeType(_, inst, 0, ValidPipeType::READ_ONLY)) + return error; + } else if (inst->opcode() == spv::Op::OpCommitWritePipe) { + if (auto error = ValidatePipeType(_, inst, 0, ValidPipeType::WRITE_ONLY)) + return error; + } + + const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 1)); + if (reserve_id->opcode() != spv::Op::OpTypeReserveId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reserve Id type must be OpTypeReserveId."; + } + + if (auto error = ValidatePacketSizeAlign(_, inst, 2, 3)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupCommitPipe(ValidationState_t& _, + const Instruction* inst) { + if (inst->opcode() == spv::Op::OpGroupCommitReadPipe) { + if (auto error = ValidatePipeType(_, inst, 1, ValidPipeType::READ_ONLY)) + return error; + } else if (inst->opcode() == spv::Op::OpGroupCommitWritePipe) { + if (auto error = ValidatePipeType(_, inst, 1, ValidPipeType::WRITE_ONLY)) + return error; + } + + const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 2)); + if (reserve_id->opcode() != spv::Op::OpTypeReserveId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reserve Id type must be OpTypeReserveId."; + } + + if (auto error = ValidatePacketSizeAlign(_, inst, 3, 4)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidatePipePacketsQuery(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be a 32-bit int scalar."; + } + + if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_OR_WRITE)) + return error; + + if (auto error = ValidatePacketSizeAlign(_, inst, 3, 4)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateIsValidReserveId(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be a bool scalar"; + } + + const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 2)); + if (reserve_id->opcode() != spv::Op::OpTypeReserveId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reserve Id type must be OpTypeReserveId."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCreatePipeFromPipeStorage(ValidationState_t& _, + const Instruction* inst) { + const Instruction* result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypePipe) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be OpTypePipe."; + } + + // TODO - Need to check OpTypeStorage is from OpConstantPipeStorage + return SPV_SUCCESS; +} + +spv_result_t ValidateConstantPipeStorage(ValidationState_t& _, + const Instruction* inst) { + const Instruction* result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypePipeStorage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be OpTypePipeStorage."; + } + + // TODO - Should we validate the literal values? + // https://gitlab.khronos.org/spirv/SPIR-V/-/issues/914 + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of pipe instructions. +spv_result_t PipePass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpReadPipe: + case spv::Op::OpWritePipe: + return ValidateReadWritePipe(_, inst); + case spv::Op::OpReservedReadPipe: + case spv::Op::OpReservedWritePipe: + return ValidateReservedReadWritePipe(_, inst); + case spv::Op::OpReserveReadPipePackets: + case spv::Op::OpReserveWritePipePackets: + return ValidateReservePackets(_, inst); + case spv::Op::OpGroupReserveReadPipePackets: + case spv::Op::OpGroupReserveWritePipePackets: + return ValidateGroupReservePackets(_, inst); + case spv::Op::OpCommitReadPipe: + case spv::Op::OpCommitWritePipe: + return ValidateCommitPipe(_, inst); + case spv::Op::OpGroupCommitReadPipe: + case spv::Op::OpGroupCommitWritePipe: + return ValidateGroupCommitPipe(_, inst); + case spv::Op::OpGetNumPipePackets: + case spv::Op::OpGetMaxPipePackets: + return ValidatePipePacketsQuery(_, inst); + case spv::Op::OpIsValidReserveId: + return ValidateIsValidReserveId(_, inst); + case spv::Op::OpCreatePipeFromPipeStorage: + return ValidateCreatePipeFromPipeStorage(_, inst); + case spv::Op::OpConstantPipeStorage: + return ValidateConstantPipeStorage(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_ray_query.cpp b/source/val/validate_ray_query.cpp index 9b67fc9..432cad8 100644 --- a/source/val/validate_ray_query.cpp +++ b/source/val/validate_ray_query.cpp
@@ -23,6 +23,17 @@ namespace val { namespace { +uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) { + assert(array_type->opcode() == spv::Op::OpTypeArray); + uint32_t const_int_id = array_type->GetOperandAs<uint32_t>(2U); + Instruction* array_length_inst = _.FindDef(const_int_id); + uint32_t array_length = 0; + if (array_length_inst->opcode() == spv::Op::OpConstant) { + array_length = array_length_inst->GetOperandAs<uint32_t>(2); + } + return array_length; +} + spv_result_t ValidateRayQueryPointer(ValidationState_t& _, const Instruction* inst, uint32_t ray_query_index) { @@ -55,8 +66,7 @@ inst->GetOperandAs<uint32_t>(intersection_index); const uint32_t intersection_type = _.GetTypeId(intersection_id); const spv::Op intersection_opcode = _.GetIdOpcode(intersection_id); - if (!_.IsIntScalarType(intersection_type) || - _.GetBitWidth(intersection_type) != 32 || + if (!_.IsIntScalarType(intersection_type, 32) || !spvOpcodeIsConstant(intersection_opcode)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "expected Intersection ID to be a constant 32-bit int scalar"; @@ -83,13 +93,13 @@ } const uint32_t ray_flags = _.GetOperandTypeId(inst, 2); - if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) { + if (!_.IsIntScalarType(ray_flags, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray Flags must be a 32-bit int scalar"; } const uint32_t cull_mask = _.GetOperandTypeId(inst, 3); - if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) { + if (!_.IsIntScalarType(cull_mask, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Cull Mask must be a 32-bit int scalar"; } @@ -102,7 +112,7 @@ } const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5); - if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + if (!_.IsFloatScalarType(ray_tmin, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray TMin must be a 32-bit float scalar"; } @@ -116,7 +126,7 @@ } const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7); - if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + if (!_.IsFloatScalarType(ray_tmax, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray TMax must be a 32-bit float scalar"; } @@ -133,7 +143,7 @@ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1); - if (!_.IsFloatScalarType(hit_t_id) || _.GetBitWidth(hit_t_id) != 32) { + if (!_.IsFloatScalarType(hit_t_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Hit T must be a 32-bit float scalar"; } @@ -162,8 +172,7 @@ case spv::Op::OpRayQueryGetRayTMinKHR: { if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; - if (!_.IsFloatScalarType(result_type) || - _.GetBitWidth(result_type) != 32) { + if (!_.IsFloatScalarType(result_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "expected Result Type to be 32-bit float scalar type"; } @@ -185,7 +194,7 @@ case spv::Op::OpRayQueryGetRayFlagsKHR: { if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; - if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) { + if (!_.IsIntScalarType(result_type, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "expected Result Type to be 32-bit int scalar type"; } @@ -263,6 +272,108 @@ break; } + case spv::Op::OpRayQueryGetClusterIdNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsIntScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit int scalar type"; + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionSpherePositionNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 3 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float 3-component " + "vector type"; + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + auto result_id = _.FindDef(result_type); + if ((result_id->opcode() != spv::Op::OpTypeArray) || + (GetArrayLength(_, result_id) != 2) || + !_.IsFloatVectorType(_.GetComponentType(result_type)) || + _.GetDimension(_.GetComponentType(result_type)) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 2 element array of 32-bit 3 component float point " + "vector as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatArrayType(result_type) || + (GetArrayLength(_, _.FindDef(result_type)) != 2) || + !_.IsFloatScalarType(_.GetComponentType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating point scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV: + case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit floating point " + "scalar type"; + } + break; + } + + case spv::Op::OpRayQueryIsSphereHitNV: + case spv::Op::OpRayQueryIsLSSHitNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be Boolean " + "scalar type"; + } + + break; + } + + // SPV_KHR_ray_tracing_position_fetch + case spv::Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + auto result_id = _.FindDef(result_type); + if ((result_id->opcode() != spv::Op::OpTypeArray) || + (GetArrayLength(_, result_id) != 3) || + !_.IsFloatVectorType(_.GetComponentType(result_type)) || + _.GetDimension(_.GetComponentType(result_type)) != 3 || + _.GetBitWidth(_.GetComponentType(result_type)) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 3 element array of 32-bit 3 component float point " + "vector as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } default: break; }
diff --git a/source/val/validate_ray_tracing.cpp b/source/val/validate_ray_tracing.cpp index f74e9d4..5fa14cc 100644 --- a/source/val/validate_ray_tracing.cpp +++ b/source/val/validate_ray_tracing.cpp
@@ -22,146 +22,143 @@ namespace spvtools { namespace val { -spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) { - const spv::Op opcode = inst->opcode(); +spv_result_t ValidateTraceRay(ValidationState_t& _, const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + "OpTraceRayKHR requires RayGenerationKHR, " + "ClosestHitKHR and MissKHR execution models"; + } + return false; + } + return true; + }); + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) != + spv::Op::OpTypeAccelerationStructureKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR"; + } + + const uint32_t ray_flags = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(ray_flags, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + const uint32_t cull_mask = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(cull_mask, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cull Mask must be a 32-bit int scalar"; + } + + const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(sbt_offset, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Offset must be a 32-bit int scalar"; + } + + const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(sbt_stride, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Stride must be a 32-bit int scalar"; + } + + const uint32_t miss_index = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarType(miss_index, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit int scalar"; + } + + const uint32_t ray_origin = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7); + if (!_.IsFloatScalarType(ray_tmin, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + const uint32_t ray_direction = _.GetOperandTypeId(inst, 8); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9); + if (!_.IsFloatScalarType(ray_tmax, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + + const Instruction* payload = _.FindDef(inst->GetOperandAs<uint32_t>(10)); + if (payload->opcode() != spv::Op::OpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be the result of a OpVariable"; + } else if (payload->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::RayPayloadKHR && + payload->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::IncomingRayPayloadKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must have storage class RayPayloadKHR or " + "IncomingRayPayloadKHR"; + } + + return SPV_SUCCESS; +} +spv_result_t ValidateReportIntersection(ValidationState_t& _, + const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::IntersectionKHR) { + if (message) { + *message = + "OpReportIntersectionKHR requires IntersectionKHR " + "execution model"; + } + return false; + } + return true; + }); + const uint32_t result_type = inst->type_id(); + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } - switch (opcode) { - case spv::Op::OpTraceRayKHR: { - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation( - [](spv::ExecutionModel model, std::string* message) { - if (model != spv::ExecutionModel::RayGenerationKHR && - model != spv::ExecutionModel::ClosestHitKHR && - model != spv::ExecutionModel::MissKHR) { - if (message) { - *message = - "OpTraceRayKHR requires RayGenerationKHR, " - "ClosestHitKHR and MissKHR execution models"; - } - return false; - } - return true; - }); + const uint32_t hit = _.GetOperandTypeId(inst, 2); + if (!_.IsFloatScalarType(hit, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit must be a 32-bit int scalar"; + } - if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) != - spv::Op::OpTypeAccelerationStructureKHR) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Acceleration Structure to be of type " - "OpTypeAccelerationStructureKHR"; - } + const uint32_t hit_kind = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntScalarType(hit_kind) || _.GetBitWidth(hit_kind) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Kind must be a 32-bit unsigned int scalar"; + } + return SPV_SUCCESS; +} - const uint32_t ray_flags = _.GetOperandTypeId(inst, 1); - if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Ray Flags must be a 32-bit int scalar"; - } - - const uint32_t cull_mask = _.GetOperandTypeId(inst, 2); - if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Cull Mask must be a 32-bit int scalar"; - } - - const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3); - if (!_.IsIntScalarType(sbt_offset) || _.GetBitWidth(sbt_offset) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "SBT Offset must be a 32-bit int scalar"; - } - - const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4); - if (!_.IsIntScalarType(sbt_stride) || _.GetBitWidth(sbt_stride) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "SBT Stride must be a 32-bit int scalar"; - } - - const uint32_t miss_index = _.GetOperandTypeId(inst, 5); - if (!_.IsIntScalarType(miss_index) || _.GetBitWidth(miss_index) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Miss Index must be a 32-bit int scalar"; - } - - const uint32_t ray_origin = _.GetOperandTypeId(inst, 6); - if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || - _.GetBitWidth(ray_origin) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Ray Origin must be a 32-bit float 3-component vector"; - } - - const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7); - if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Ray TMin must be a 32-bit float scalar"; - } - - const uint32_t ray_direction = _.GetOperandTypeId(inst, 8); - if (!_.IsFloatVectorType(ray_direction) || - _.GetDimension(ray_direction) != 3 || - _.GetBitWidth(ray_direction) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Ray Direction must be a 32-bit float 3-component vector"; - } - - const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9); - if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Ray TMax must be a 32-bit float scalar"; - } - - const Instruction* payload = _.FindDef(inst->GetOperandAs<uint32_t>(10)); - if (payload->opcode() != spv::Op::OpVariable) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Payload must be the result of a OpVariable"; - } else if (payload->GetOperandAs<spv::StorageClass>(2) != - spv::StorageClass::RayPayloadKHR && - payload->GetOperandAs<spv::StorageClass>(2) != - spv::StorageClass::IncomingRayPayloadKHR) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Payload must have storage class RayPayloadKHR or " - "IncomingRayPayloadKHR"; - } - break; - } - - case spv::Op::OpReportIntersectionKHR: { - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation( - [](spv::ExecutionModel model, std::string* message) { - if (model != spv::ExecutionModel::IntersectionKHR) { - if (message) { - *message = - "OpReportIntersectionKHR requires IntersectionKHR " - "execution model"; - } - return false; - } - return true; - }); - - if (!_.IsBoolScalarType(result_type)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "expected Result Type to be bool scalar type"; - } - - const uint32_t hit = _.GetOperandTypeId(inst, 2); - if (!_.IsFloatScalarType(hit) || _.GetBitWidth(hit) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Hit must be a 32-bit int scalar"; - } - - const uint32_t hit_kind = _.GetOperandTypeId(inst, 3); - if (!_.IsUnsignedIntScalarType(hit_kind) || - _.GetBitWidth(hit_kind) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Hit Kind must be a 32-bit unsigned int scalar"; - } - break; - } - - case spv::Op::OpExecuteCallableKHR: { - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, - std::string* message) { +spv_result_t ValidateExecuteCallable(ValidationState_t& _, + const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { if (model != spv::ExecutionModel::RayGenerationKHR && model != spv::ExecutionModel::ClosestHitKHR && model != spv::ExecutionModel::MissKHR && @@ -176,29 +173,37 @@ return true; }); - const uint32_t sbt_index = _.GetOperandTypeId(inst, 0); - if (!_.IsUnsignedIntScalarType(sbt_index) || - _.GetBitWidth(sbt_index) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "SBT Index must be a 32-bit unsigned int scalar"; - } + const uint32_t sbt_index = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(sbt_index) || _.GetBitWidth(sbt_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Index must be a 32-bit unsigned int scalar"; + } - const auto callable_data = _.FindDef(inst->GetOperandAs<uint32_t>(1)); - if (callable_data->opcode() != spv::Op::OpVariable) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Callable Data must be the result of a OpVariable"; - } else if (callable_data->GetOperandAs<spv::StorageClass>(2) != - spv::StorageClass::CallableDataKHR && - callable_data->GetOperandAs<spv::StorageClass>(2) != - spv::StorageClass::IncomingCallableDataKHR) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Callable Data must have storage class CallableDataKHR or " - "IncomingCallableDataKHR"; - } + const auto callable_data = _.FindDef(inst->GetOperandAs<uint32_t>(1)); + if (callable_data->opcode() != spv::Op::OpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Callable Data must be the result of a OpVariable"; + } else if (callable_data->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::CallableDataKHR && + callable_data->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::IncomingCallableDataKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Callable Data must have storage class CallableDataKHR or " + "IncomingCallableDataKHR"; + } + return SPV_SUCCESS; +} - break; - } +spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpTraceRayKHR: + return ValidateTraceRay(_, inst); + case spv::Op::OpReportIntersectionKHR: + return ValidateReportIntersection(_, inst); + case spv::Op::OpExecuteCallableKHR: + return ValidateExecuteCallable(_, inst); default: break; }
diff --git a/source/val/validate_ray_tracing_reorder.cpp b/source/val/validate_ray_tracing_reorder.cpp index cb190f9..d1c4ee5 100644 --- a/source/val/validate_ray_tracing_reorder.cpp +++ b/source/val/validate_ray_tracing_reorder.cpp
@@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Validates ray tracing instructions from SPV_NV_shader_execution_reorder +// Validates ray tracing instructions from SPV_NV_shader_invocation_reorder and +// SPV_EXT_shader_invocation_reorder #include "source/opcode.h" #include "source/val/instruction.h" @@ -26,18 +27,40 @@ static const uint32_t KRayParamInvalidId = std::numeric_limits<uint32_t>::max(); +uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) { + assert(array_type->opcode() == spv::Op::OpTypeArray); + uint32_t const_int_id = array_type->GetOperandAs<uint32_t>(2U); + Instruction* array_length_inst = _.FindDef(const_int_id); + uint32_t array_length = 0; + if (array_length_inst->opcode() == spv::Op::OpConstant) { + array_length = array_length_inst->GetOperandAs<uint32_t>(2); + } + return array_length; +} + +spv_result_t ValidateRayQueryPointer(ValidationState_t& _, + const Instruction* inst, + uint32_t ray_query_index) { + const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index); + auto variable = _.FindDef(ray_query_id); + auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0)); + if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a pointer"; + } + auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2)); + if (!type || type->opcode() != spv::Op::OpTypeRayQueryKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a pointer to OpTypeRayQueryKHR"; + } + return SPV_SUCCESS; +} + spv_result_t ValidateHitObjectPointer(ValidationState_t& _, const Instruction* inst, uint32_t hit_object_index) { const uint32_t hit_object_id = inst->GetOperandAs<uint32_t>(hit_object_index); auto variable = _.FindDef(hit_object_id); - const auto var_opcode = variable->opcode(); - if (!variable || (var_opcode != spv::Op::OpVariable && - var_opcode != spv::Op::OpFunctionParameter && - var_opcode != spv::Op::OpAccessChain)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Hit Object must be a memory object declaration"; - } auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0)); if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -51,6 +74,24 @@ return SPV_SUCCESS; } +spv_result_t ValidateHitObjectPointerEXT(ValidationState_t& _, + const Instruction* inst, + uint32_t hit_object_index) { + const uint32_t hit_object_id = inst->GetOperandAs<uint32_t>(hit_object_index); + auto variable = _.FindDef(hit_object_id); + auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0)); + if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object must be a pointer"; + } + auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2)); + if (!type || type->opcode() != spv::Op::OpTypeHitObjectEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Type must be OpTypeHitObjectEXT"; + } + return SPV_SUCCESS; +} + spv_result_t ValidateHitObjectInstructionCommonParameters( ValidationState_t& _, const Instruction* inst, uint32_t acceleration_struct_index, uint32_t instance_id_index, @@ -72,7 +113,7 @@ if (isValidId(instance_id_index)) { const uint32_t instance_id = _.GetOperandTypeId(inst, instance_id_index); - if (!_.IsIntScalarType(instance_id) || _.GetBitWidth(instance_id) != 32) { + if (!_.IsIntScalarType(instance_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Instance Id must be a 32-bit int scalar"; } @@ -80,7 +121,7 @@ if (isValidId(primtive_id_index)) { const uint32_t primitive_id = _.GetOperandTypeId(inst, primtive_id_index); - if (!_.IsIntScalarType(primitive_id) || _.GetBitWidth(primitive_id) != 32) { + if (!_.IsIntScalarType(primitive_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Primitive Id must be a 32-bit int scalar"; } @@ -88,8 +129,7 @@ if (isValidId(geometry_index)) { const uint32_t geometry_index_id = _.GetOperandTypeId(inst, geometry_index); - if (!_.IsIntScalarType(geometry_index_id) || - _.GetBitWidth(geometry_index_id) != 32) { + if (!_.IsIntScalarType(geometry_index_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Geometry Index must be a 32-bit int scalar"; } @@ -173,7 +213,7 @@ if (isValidId(ray_tmin_index)) { const uint32_t ray_tmin_id = _.GetOperandTypeId(inst, ray_tmin_index); - if (!_.IsFloatScalarType(ray_tmin_id) || _.GetBitWidth(ray_tmin_id) != 32) { + if (!_.IsFloatScalarType(ray_tmin_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray TMin must be a 32-bit float scalar"; } @@ -192,7 +232,7 @@ if (isValidId(ray_tmax_index)) { const uint32_t ray_tmax_id = _.GetOperandTypeId(inst, ray_tmax_index); - if (!_.IsFloatScalarType(ray_tmax_id) || _.GetBitWidth(ray_tmax_id) != 32) { + if (!_.IsFloatScalarType(ray_tmax_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray TMax must be a 32-bit float scalar"; } @@ -200,7 +240,7 @@ if (isValidId(ray_flags_index)) { const uint32_t ray_flags_id = _.GetOperandTypeId(inst, ray_flags_index); - if (!_.IsIntScalarType(ray_flags_id) || _.GetBitWidth(ray_flags_id) != 32) { + if (!_.IsIntScalarType(ray_flags_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray Flags must be a 32-bit int scalar"; } @@ -236,8 +276,10 @@ auto variable = _.FindDef(hit_object_attr_id); const auto var_opcode = variable->opcode(); if (!variable || var_opcode != spv::Op::OpVariable || - (variable->GetOperandAs<spv::StorageClass>(2)) != - spv::StorageClass::HitObjectAttributeNV) { + !((variable->GetOperandAs<spv::StorageClass>(2) == + spv::StorageClass::HitObjectAttributeNV) || + (variable->GetOperandAs<spv::StorageClass>(2) == + spv::StorageClass::HitObjectAttributeEXT))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Hit Object Attributes id must be a OpVariable of storage " "class HitObjectAttributeNV"; @@ -309,7 +351,7 @@ RegisterOpcodeForValidModel(_, inst); if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; - if (!_.IsIntScalarType(result_type) || !_.GetBitWidth(result_type)) + if (!_.IsIntScalarType(result_type, 32)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected 32-bit integer type scalar as Result Type: " << spvOpcodeString(opcode); @@ -322,7 +364,7 @@ RegisterOpcodeForValidModel(_, inst); if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; - if (!_.IsFloatScalarType(result_type) || _.GetBitWidth(result_type) != 32) + if (!_.IsFloatScalarType(result_type, 32)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected 32-bit floating-point type scalar as Result Type: " << spvOpcodeString(opcode); @@ -438,7 +480,7 @@ } const uint32_t ray_tmin = _.GetOperandTypeId(inst, 3); - if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + if (!_.IsFloatScalarType(ray_tmin, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray TMin must be a 32-bit float scalar"; } @@ -452,7 +494,7 @@ } const uint32_t ray_tmax = _.GetOperandTypeId(inst, 5); - if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + if (!_.IsFloatScalarType(ray_tmax, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Ray TMax must be a 32-bit float scalar"; } @@ -520,8 +562,7 @@ return error; // Current Time const uint32_t current_time_id = _.GetOperandTypeId(inst, 11); - if (!_.IsFloatScalarType(current_time_id) || - _.GetBitWidth(current_time_id) != 32) { + if (!_.IsFloatScalarType(current_time_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Current Times must be a 32-bit float scalar type"; } @@ -575,12 +616,12 @@ // Validate the optional opreands Hint and Bits const uint32_t hint_id = _.GetOperandTypeId(inst, 1); - if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) { + if (!_.IsIntScalarType(hint_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Hint must be a 32-bit int scalar"; } const uint32_t bits_id = _.GetOperandTypeId(inst, 2); - if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) { + if (!_.IsIntScalarType(bits_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "bits must be a 32-bit int scalar"; } @@ -604,16 +645,750 @@ }); const uint32_t hint_id = _.GetOperandTypeId(inst, 0); - if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) { + if (!_.IsIntScalarType(hint_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Hint must be a 32-bit int scalar"; } const uint32_t bits_id = _.GetOperandTypeId(inst, 1); - if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) { + if (!_.IsIntScalarType(bits_id, 32)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "bits must be a 32-bit int scalar"; } + break; + } + + case spv::Op::OpHitObjectGetClusterIdNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsIntScalarType(result_type, 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit integer type scalar as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetSpherePositionNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 3 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating point 2 component vector type as " + "Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetSphereRadiusNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatScalarType(result_type, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating point scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetLSSPositionsNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + auto result_id = _.FindDef(result_type); + if ((result_id->opcode() != spv::Op::OpTypeArray) || + (GetArrayLength(_, result_id) != 2) || + !_.IsFloatVectorType(_.GetComponentType(result_type)) || + _.GetDimension(_.GetComponentType(result_type)) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 2 element array of 32-bit 3 component float point " + "vector as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetLSSRadiiNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatArrayType(result_type) || + (GetArrayLength(_, _.FindDef(result_type)) != 2) || + !_.IsFloatScalarType(_.GetComponentType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 2 element array of 32-bit floating point scalar as " + "Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectIsSphereHitNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Boolean scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectIsLSSHitNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Boolean scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + default: + break; + } + return SPV_SUCCESS; +} + +spv_result_t RayReorderEXTPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + auto RegisterOpcodeForValidModel = [](ValidationState_t& vs, + const Instruction* rtinst) { + std::string opcode_name = spvOpcodeString(rtinst->opcode()); + vs.function(rtinst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR, ClosestHitKHR and " + "MissKHR execution models"; + } + return false; + } + return true; + }); + return; + }; + + switch (opcode) { + case spv::Op::OpHitObjectIsMissEXT: + case spv::Op::OpHitObjectIsHitEXT: + case spv::Op::OpHitObjectIsEmptyEXT: { + RegisterOpcodeForValidModel(_, inst); + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } + + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + break; + } + + case spv::Op::OpHitObjectGetShaderRecordBufferHandleEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + + if (!_.IsIntVectorType(result_type) || + (_.GetDimension(result_type) != 2) || + (_.GetBitWidth(result_type) != 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit integer type 2-component vector as Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetHitKindEXT: + case spv::Op::OpHitObjectGetPrimitiveIndexEXT: + case spv::Op::OpHitObjectGetGeometryIndexEXT: + case spv::Op::OpHitObjectGetInstanceIdEXT: + case spv::Op::OpHitObjectGetInstanceCustomIndexEXT: + case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexEXT: + case spv::Op::OpHitObjectGetRayFlagsEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + + if (!_.IsIntScalarType(result_type, 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit integer type scalar as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetCurrentTimeEXT: + case spv::Op::OpHitObjectGetRayTMaxEXT: + case spv::Op::OpHitObjectGetRayTMinEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + + if (!_.IsFloatScalarType(result_type, 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating-point type scalar as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetObjectToWorldEXT: + case spv::Op::OpHitObjectGetWorldToObjectEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + if (num_cols != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Count of 4" + << spvOpcodeString(opcode); + } + + if (!_.IsFloatScalarType(component_type) || + _.GetBitWidth(result_type) != 32 || num_rows != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Type of " + "3-component 32-bit float vectors: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetObjectRayOriginEXT: + case spv::Op::OpHitObjectGetObjectRayDirectionEXT: + case spv::Op::OpHitObjectGetWorldRayDirectionEXT: + case spv::Op::OpHitObjectGetWorldRayOriginEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + + if (!_.IsFloatVectorType(result_type) || + (_.GetDimension(result_type) != 3) || + (_.GetBitWidth(result_type) != 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating-point type 3-component vector as " + "Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetIntersectionTriangleVertexPositionsEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error; + + auto result_id = _.FindDef(result_type); + if ((result_id->opcode() != spv::Op::OpTypeArray) || + (GetArrayLength(_, result_id) != 3) || + !_.IsFloatVectorType(_.GetComponentType(result_type)) || + _.GetDimension(_.GetComponentType(result_type)) != 3 || + _.GetBitWidth(_.GetComponentType(result_type)) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 3 element array of 32-bit 3 component float " + "vectors as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetAttributesEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + const uint32_t hit_object_attr_id = inst->GetOperandAs<uint32_t>(1); + auto variable = _.FindDef(hit_object_attr_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + variable->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::HitObjectAttributeEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object Attributes id must be a OpVariable of storage " + "class HitObjectAttributeEXT"; + } + break; + } + + case spv::Op::OpHitObjectSetShaderBindingTableRecordIndexEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + const uint32_t sbt_index_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(sbt_index_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Index must be a 32-bit integer scalar"; + } + break; + } + + case spv::Op::OpHitObjectExecuteShaderEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + const uint32_t payload_id = inst->GetOperandAs<uint32_t>(1); + auto variable = _.FindDef(payload_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + (variable->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::RayPayloadKHR && + variable->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::IncomingRayPayloadKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be a OpVariable of storage " + "class RayPayloadKHR or IncomingRayPayloadKHR"; + } + break; + } + + case spv::Op::OpHitObjectRecordEmptyEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + break; + } + + case spv::Op::OpHitObjectRecordFromQueryEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + if (auto error = ValidateRayQueryPointer(_, inst, 1)) return error; + + if (!_.HasCapability(spv::Capability::RayQueryKHR)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": requires RayQueryKHR capability"; + + // Validate SBT Record Index (operand 2) + const uint32_t sbt_record_index_id = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(sbt_record_index_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Record Index must be a 32-bit integer scalar"; + } + + // Validate Hit Object Attributes (operand 3) + const uint32_t hit_object_attr_id = inst->GetOperandAs<uint32_t>(3); + auto attr_variable = _.FindDef(hit_object_attr_id); + const auto attr_var_opcode = attr_variable->opcode(); + if (!attr_variable || attr_var_opcode != spv::Op::OpVariable || + attr_variable->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::HitObjectAttributeEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object Attributes id must be a OpVariable of storage " + "class HitObjectAttributeEXT"; + } + break; + } + + case spv::Op::OpHitObjectRecordMissEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + // Ray Flags (operand 1) + const uint32_t ray_flags_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(ray_flags_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + // Miss Index (operand 2) + const uint32_t miss_index = _.GetOperandTypeId(inst, 2); + if (!_.IsUnsignedIntScalarType(miss_index) || + _.GetBitWidth(miss_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit unsigned int scalar"; + } + + // Ray Origin (operand 3) + const uint32_t ray_origin = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + // Ray TMin (operand 4) + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(ray_tmin, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + // Ray Direction (operand 5) + const uint32_t ray_direction = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + // Ray TMax (operand 6) + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatScalarType(ray_tmax, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + break; + } + + case spv::Op::OpHitObjectRecordMissMotionEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + // Ray Flags (operand 1) + const uint32_t ray_flags_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(ray_flags_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + // Miss Index (operand 2) + const uint32_t miss_index = _.GetOperandTypeId(inst, 2); + if (!_.IsUnsignedIntScalarType(miss_index) || + _.GetBitWidth(miss_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit unsigned int scalar"; + } + + // Ray Origin (operand 3) + const uint32_t ray_origin = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + // Ray TMin (operand 4) + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(ray_tmin, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + // Ray Direction (operand 5) + const uint32_t ray_direction = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + // Ray TMax (operand 6) + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatScalarType(ray_tmax, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + + // Current Time (operand 7) + const uint32_t current_time_id = _.GetOperandTypeId(inst, 7); + if (!_.IsFloatScalarType(current_time_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Current Time must be a 32-bit float scalar"; + } + break; + } + + case spv::Op::OpReorderThreadWithHintEXT: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + const uint32_t hint_id = _.GetOperandTypeId(inst, 0); + if (!_.IsIntScalarType(hint_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + + const uint32_t bits_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(bits_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Bits must be a 32-bit int scalar"; + } + break; + } + + case spv::Op::OpReorderThreadWithHitObjectEXT: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + if (inst->operands().size() > 1) { + if (inst->operands().size() != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint and Bits are optional together i.e " + << " Either both Hint and Bits should be provided or neither."; + } + + // Validate the optional operands Hint and Bits + const uint32_t hint_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(hint_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + const uint32_t bits_id = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(bits_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Bits must be a 32-bit int scalar"; + } + } + break; + } + + case spv::Op::OpHitObjectTraceRayEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, + KRayParamInvalidId /* Instance Id */, + KRayParamInvalidId /* Primitive Id */, + KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */, + 3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */, + 5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */, + 7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */, + 10 /* Ray TMax */, 11 /* Payload */, + KRayParamInvalidId /* Hit Object Attribute */)) + return error; + break; + } + + case spv::Op::OpHitObjectTraceRayMotionEXT: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, + KRayParamInvalidId /* Instance Id */, + KRayParamInvalidId /* Primitive Id */, + KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */, + 3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */, + 5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */, + 7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */, + 10 /* Ray TMax */, 12 /* Payload */, + KRayParamInvalidId /* Hit Object Attribute */)) + return error; + + // Current Time (operand 11) + const uint32_t current_time_id = _.GetOperandTypeId(inst, 11); + if (!_.IsFloatScalarType(current_time_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Current Time must be a 32-bit float scalar"; + } + break; + } + + case spv::Op::OpHitObjectReorderExecuteShaderEXT: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + // Validate Payload (operand 1) + const uint32_t payload_id = inst->GetOperandAs<uint32_t>(1); + auto variable = _.FindDef(payload_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + (variable->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::RayPayloadKHR && + variable->GetOperandAs<spv::StorageClass>(2) != + spv::StorageClass::IncomingRayPayloadKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be a OpVariable of storage " + "class RayPayloadKHR or IncomingRayPayloadKHR"; + } + + // Check for optional Hint and Bits (operands 2 and 3) + if (inst->operands().size() > 2) { + if (inst->operands().size() != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint and Bits are optional together i.e " + << " Either both Hint and Bits should be provided or neither."; + } + + // Validate optional Hint and Bits + const uint32_t hint_id = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(hint_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + const uint32_t bits_id = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(bits_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Bits must be a 32-bit int scalar"; + } + } + break; + } + + case spv::Op::OpHitObjectTraceReorderExecuteEXT: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + // Validate base trace ray parameters (operands 1-11) + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, + KRayParamInvalidId /* Instance Id */, + KRayParamInvalidId /* Primitive Id */, + KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */, + 3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */, + 5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */, + 7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */, + 10 /* Ray TMax */, 11 /* Payload */, + KRayParamInvalidId /* Hit Object Attribute */)) + return error; + + // Check for optional Hint and Bits (operands 12 and 13) + if (inst->operands().size() > 12) { + if (inst->operands().size() != 14) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint and Bits are optional together i.e " + << " Either both Hint and Bits should be provided or neither."; + } + + // Validate optional Hint and Bits + const uint32_t hint_id = _.GetOperandTypeId(inst, 12); + if (!_.IsIntScalarType(hint_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + const uint32_t bits_id = _.GetOperandTypeId(inst, 13); + if (!_.IsIntScalarType(bits_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Bits must be a 32-bit int scalar"; + } + } + break; + } + + case spv::Op::OpHitObjectTraceMotionReorderExecuteEXT: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error; + + // Validate base trace ray parameters (operands 1-12) + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, + KRayParamInvalidId /* Instance Id */, + KRayParamInvalidId /* Primitive Id */, + KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */, + 3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */, + 5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */, + 7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */, + 10 /* Ray TMax */, 12 /* Payload */, + KRayParamInvalidId /* Hit Object Attribute */)) + return error; + + // Current Time (operand 11) + const uint32_t current_time_id = _.GetOperandTypeId(inst, 11); + if (!_.IsFloatScalarType(current_time_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Current Time must be a 32-bit float scalar"; + } + + // Check for optional Hint and Bits (operands 13 and 14) + if (inst->operands().size() > 13) { + if (inst->operands().size() != 15) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint and Bits are optional together i.e " + << " Either both Hint and Bits should be provided or neither."; + } + + // Validate optional Hint and Bits + const uint32_t hint_id = _.GetOperandTypeId(inst, 13); + if (!_.IsIntScalarType(hint_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + const uint32_t bits_id = _.GetOperandTypeId(inst, 14); + if (!_.IsIntScalarType(bits_id, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Bits must be a 32-bit int scalar"; + } + } + break; } default:
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 6b49353..c7ef5f2 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp
@@ -94,7 +94,7 @@ // Vulkan specific rules if (spvIsVulkanEnv(_.context()->target_env)) { - // Vulkan 1.1 specific rules + // Subgroups were not added until 1.1 if (_.context()->target_env != SPV_ENV_VULKAN_1_0) { // Scope for Non Uniform Group Operations must be limited to Subgroup if ((spvOpcodeIsNonUniformGroupOperation(opcode) &&
diff --git a/source/val/validate_tensor.cpp b/source/val/validate_tensor.cpp new file mode 100644 index 0000000..0b54129 --- /dev/null +++ b/source/val/validate_tensor.cpp
@@ -0,0 +1,250 @@ +// Copyright (c) 2023-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of tensor instructions. + +#include "source/opcode.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool IsRankedTensor(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != spv::Op::OpTypeTensorARM || + inst->words().size() <= 3) { + return false; + } + return true; +} + +uint64_t GetTensorTypeRank(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != spv::Op::OpTypeTensorARM || + inst->words().size() <= 3) { + return 0; + } + uint64_t rank = 0; + if (!_.EvalConstantValUint64(inst->word(3), &rank)) { + return 0; + } + return rank; +} + +bool IsScalarTypeOrOrArrayOfScalarType(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst) { + return false; + } + return _.IsScalarType(id) || (inst->opcode() == spv::Op::OpTypeArray && + _.IsScalarType(inst->word(2))); +} + +spv_result_t ValidateTensorRead(ValidationState_t& _, const Instruction* inst) { + // Result Type must be a scalar type or array of scalar type. + if (!IsScalarTypeOrOrArrayOfScalarType(_, inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a scalar type or array of " + "scalar type."; + } + + // Tensor must be a Ranked Tensor. + auto op_tensor = inst->word(3); + auto inst_tensor = _.FindDef(op_tensor); + if (!inst_tensor || !IsRankedTensor(_, inst_tensor->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Tensor to be an OpTypeTensorARM whose Rank is " + "specified"; + } + + // The scalar type must be the same as the Element Type of Tensor. + if (_.GetComponentType(inst_tensor->type_id()) != + _.GetComponentType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be the same as the Element Type of " + "Tensor."; + } + + // Coordinates is an array whose Element Type must be an integer type and + // whose Length must be equal to the Rank of Tensor. + auto op_coord = inst->word(4); + auto inst_coord = _.FindDef(op_coord); + auto tensor_rank = GetTensorTypeRank(_, inst_tensor->type_id()); + if (!_.IsIntArrayType(inst_coord->type_id(), tensor_rank)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor."; + } + + // Validate Tensor Operands + if (inst->words().size() > 5) { + auto toperands = static_cast<spv::TensorOperandsMask>(inst->word(5)); + if ((toperands & spv::TensorOperandsMask::OutOfBoundsValueARM) != + spv::TensorOperandsMask::MaskNone) { + if (inst->words().size() < 7) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "A value must be provided after the OutOfBoundsValueARM " + "Tensor Operand."; + } + auto op_oobval = inst->word(6); + auto inst_oobval = _.FindDef(op_oobval); + if (_.GetComponentType(inst_tensor->type_id()) != + _.GetComponentType(inst_oobval->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Expected the type of the OutOfBoundsValueARM value to be " + "the same " + "as the Element Type of Tensor."; + } + } + if ((toperands & spv::TensorOperandsMask::MakeElementAvailableARM) != + spv::TensorOperandsMask::MaskNone) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MakeElementAvailableARM cannot be used with OpTensorReadARM."; + } + if (((toperands & spv::TensorOperandsMask::MakeElementVisibleARM) != + spv::TensorOperandsMask::MaskNone) && + ((toperands & spv::TensorOperandsMask::NonPrivateElementARM) == + spv::TensorOperandsMask::MaskNone)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MakeElementAvailableARM requires NonPrivateElementARM."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTensorWrite(ValidationState_t& _, + const Instruction* inst) { + // Tensor must be a Ranked Tensor. + auto op_tensor = inst->word(1); + auto inst_tensor = _.FindDef(op_tensor); + if (!IsRankedTensor(_, inst_tensor->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Tensor to be an OpTypeTensorARM whose Rank is " + "specified"; + } + + // Coordinates is an array whose Element Type must be an integer type and + // whose Length must be equal to the Rank of Tensor. + auto op_coord = inst->word(2); + auto inst_coord = _.FindDef(op_coord); + auto tensor_rank = GetTensorTypeRank(_, inst_tensor->type_id()); + if (!_.IsIntArrayType(inst_coord->type_id(), tensor_rank)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor."; + } + + // Object must be an object of scalar type or array of scalar type. + // The scalar type must be the same as the Element Type of Tensor. + auto op_object = inst->word(3); + auto inst_object = _.FindDef(op_object); + if (!IsScalarTypeOrOrArrayOfScalarType(_, inst_object->type_id()) || + (_.GetComponentType(inst_object->type_id()) != + _.GetComponentType(inst_tensor->type_id()))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Object to be a scalar type or array of scalar " + "type that is the same as the Element Type of Tensor."; + } + + // Validate Tensor Operands + if (inst->words().size() > 5) { + auto toperands = static_cast<spv::TensorOperandsMask>(inst->word(4)); + if ((toperands & spv::TensorOperandsMask::OutOfBoundsValueARM) != + spv::TensorOperandsMask::MaskNone) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OutOfBoundsValue Tensor Operand not allowed with " + "OpTensorWriteARM."; + } + if ((toperands & spv::TensorOperandsMask::MakeElementVisibleARM) != + spv::TensorOperandsMask::MaskNone) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MakeElementVisibleARM not allowed with OpTensorWriteARM."; + } + if (((toperands & spv::TensorOperandsMask::MakeElementAvailableARM) != + spv::TensorOperandsMask::MaskNone) && + ((toperands & spv::TensorOperandsMask::NonPrivateElementARM) == + spv::TensorOperandsMask::MaskNone)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MakeElementAvailableARM requires NonPrivateElementARM."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTensorQuerySize(ValidationState_t& _, + const Instruction* inst) { + // Check result type + if (!_.IsIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be an integer type scalar"; + } + + // Check Tensor operand + auto op_tensor = inst->word(3); + auto inst_tensor = _.FindDef(op_tensor); + if (!inst_tensor || !IsRankedTensor(_, inst_tensor->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Tensor to be an OpTypeTensorARM whose Rank is " + "specified"; + } + + // Check Dimension operand + auto op_dim = inst->word(4); + auto inst_dim = _.FindDef(op_dim); + if (!spvOpcodeIsConstant(inst_dim->opcode()) || + !_.IsIntScalarType(inst_dim->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dimension must come from a constant instruction of scalar " + "integer type."; + } + + auto inst_tensor_type = _.FindDef(inst_tensor->type_id()); + auto op_tensor_rank = inst_tensor_type->word(3); + uint64_t tensor_rank = 0; + uint64_t dim; + if (_.EvalConstantValUint64(op_tensor_rank, &tensor_rank) && + _.EvalConstantValUint64(op_dim, &dim) && (dim >= tensor_rank)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dimension (" << dim << ") must be less than the Rank of Tensor (" + << tensor_rank << ")."; + } + + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of tensor instructions. +spv_result_t TensorPass(ValidationState_t& _, const Instruction* inst) { + (void)_; + const spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpTensorReadARM: + return ValidateTensorRead(_, inst); + case spv::Op::OpTensorWriteARM: + return ValidateTensorWrite(_, inst); + case spv::Op::OpTensorQuerySizeARM: + return ValidateTensorQuerySize(_, inst); + default: + break; + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools
diff --git a/source/val/validate_tensor_layout.cpp b/source/val/validate_tensor_layout.cpp index 35c766b..cbd9c98 100644 --- a/source/val/validate_tensor_layout.cpp +++ b/source/val/validate_tensor_layout.cpp
@@ -129,8 +129,7 @@ for (uint32_t i = 0; i < num_values; ++i) { const auto val_id = inst->GetOperandAs<uint32_t>(i + 3); const auto val = _.FindDef(val_id); - if (!val || !_.IsIntScalarType(val->type_id()) || - _.GetBitWidth(val->type_id()) != 32) { + if (!val || !_.IsIntScalarType(val->type_id(), 32)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << spvOpcodeString(inst->opcode()) << " operand <id> " << _.getIdName(val_id) << " is not a 32-bit integer.";
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 5101a40..ce8fc75 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // Copyright (c) 2024 NVIDIA Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,6 +17,8 @@ // Ensures type declarations are unique unless allowed by the specification. +#include <optional> + #include "source/opcode.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" @@ -36,6 +40,7 @@ const auto opcode = inst->opcode(); if (opcode != spv::Op::OpTypeArray && opcode != spv::Op::OpTypeRuntimeArray && + opcode != spv::Op::OpTypeNodePayloadArrayAMDX && opcode != spv::Op::OpTypeStruct && opcode != spv::Op::OpTypePointer && opcode != spv::Op::OpTypeUntypedPointerKHR && !_.RegisterUniqueTypeDeclaration(inst)) { @@ -55,7 +60,15 @@ // integers, respectively. auto num_bits = inst->GetOperandAs<const uint32_t>(1); if (num_bits != 32) { - if (num_bits == 8) { + if (num_bits == 4) { + if (_.HasCapability(spv::Capability::Int4TypeINTEL) || + _.HasCapability(spv::Capability::ArbitraryPrecisionIntegersINTEL)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 4-bit integer type requires the Int4TypeINTEL " + "or ArbitraryPrecisionIntegersINTEL capability."; + } else if (num_bits == 8) { if (_.features().declare_int8_type) { return SPV_SUCCESS; } @@ -76,6 +89,15 @@ return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Using a 64-bit integer type requires the Int64 capability."; } else { + // Check for SPV_INTEL_arbitrary_precision_integers extension + if (_.HasCapability(spv::Capability::ArbitraryPrecisionIntegersINTEL)) { + if (num_bits == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpTypeInt has 0 bits, which is not allowed even with " + "ArbitraryPrecisionIntegersINTEL."; + } + return SPV_SUCCESS; + } return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid number of bits (" << num_bits << ") used for OpTypeInt."; @@ -104,30 +126,78 @@ } spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) { - // Validates that the number of bits specified for an Int type is valid. - // Scalar integer types can be parameterized only with 32-bits. - // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit - // integers, respectively. + // Validates: + // - the number of bits specified for a float type is valid + // - the fp encoding is valid, and only used on matching bit widths + // - required capabilities are declared auto num_bits = inst->GetOperandAs<const uint32_t>(1); + + std::optional<spv::FPEncoding> encoding; + if (inst->operands().size() > 2) { + encoding = inst->GetOperandAs<spv::FPEncoding>(2); + } + // The number of operands is already checked by the grammar structure. + // The fp encoding operand is an optional enum, and there are no further + // operands. + if (num_bits == 32) { + if (encoding.has_value()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "32-bit floating point type must not have encoding parameter."; + } return SPV_SUCCESS; } + if (num_bits == 16) { - if (_.features().declare_float16_type) { - return SPV_SUCCESS; + // An absence of FP encoding implies IEEE 754. The Float16 and Float16Buffer + // capabilities only enable IEEE 754 binary 16 + if (!encoding.has_value() && !_.features().declare_float16_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 16-bit floating point " + << "type requires the Float16 or Float16Buffer capability," + " or an extension that explicitly enables 16-bit floating " + "point."; } - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Using a 16-bit floating point " - << "type requires the Float16 or Float16Buffer capability," - " or an extension that explicitly enables 16-bit floating point."; + if (encoding.has_value() && + encoding.value() != spv::FPEncoding::BFloat16KHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Unsupported 16-bit floating point encoding (" + << static_cast<uint32_t>(encoding.value()) << ")."; + } + return SPV_SUCCESS; + } + if (num_bits == 8) { + if (!_.features().declare_float8_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 8-bit floating point " + << "type requires the Float8EXT capability."; + } + if (encoding.has_value()) { + const auto enc = encoding.value(); + if (enc != spv::FPEncoding::Float8E4M3EXT && + enc != spv::FPEncoding::Float8E5M2EXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Unsupported 8-bit floating point encoding (" + << static_cast<uint32_t>(enc) << ")."; + } + } else { + // we don't support fp8 without encoding + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "8-bit floating point type requires an encoding."; + } + return SPV_SUCCESS; } if (num_bits == 64) { - if (_.HasCapability(spv::Capability::Float64)) { - return SPV_SUCCESS; + if (!_.HasCapability(spv::Capability::Float64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 64-bit floating point " + << "type requires the Float64 capability."; } - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Using a 64-bit floating point " - << "type requires the Float64 capability."; + if (encoding.has_value()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "64-bit floating point type must not have encoding parameter."; + } + return SPV_SUCCESS; } return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat."; @@ -137,7 +207,24 @@ const auto component_index = 1; const auto component_id = inst->GetOperandAs<uint32_t>(component_index); const auto component_type = _.FindDef(component_id); - if (!component_type || !spvOpcodeIsScalarType(component_type->opcode())) { + if (component_type) { + bool isPointer = component_type->opcode() == spv::Op::OpTypePointer; + bool isScalar = spvOpcodeIsScalarType(component_type->opcode()); + + if (_.HasCapability(spv::Capability::MaskedGatherScatterINTEL) && + !isPointer && !isScalar) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid OpTypeVector Component Type<id> " + << _.getIdName(component_id) + << ": Expected a scalar or pointer type when using the " + "SPV_INTEL_masked_gather_scatter extension."; + } else if (!_.HasCapability(spv::Capability::MaskedGatherScatterINTEL) && + !isScalar) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVector Component Type <id> " << _.getIdName(component_id) + << " is not a scalar type."; + } + } else { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypeVector Component Type <id> " << _.getIdName(component_id) << " is not a scalar type."; @@ -149,18 +236,66 @@ auto num_components = inst->GetOperandAs<const uint32_t>(2); if (num_components == 2 || num_components == 3 || num_components == 4) { return SPV_SUCCESS; + } else if (num_components > 0 && + _.HasCapability(spv::Capability::LongVectorEXT)) { + return SPV_SUCCESS; } else if (num_components == 8 || num_components == 16) { if (_.HasCapability(spv::Capability::Vector16)) { return SPV_SUCCESS; } return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Having " << num_components << " components for " - << spvOpcodeString(inst->opcode()) - << " requires the Vector16 capability"; + << _.VkErrorID(12295) << "Having " << num_components + << " components for " << spvOpcodeString(inst->opcode()) + << " requires the Vector16 or LongVectorEXT capability"; } else { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Illegal number of components (" << num_components << ") for " - << spvOpcodeString(inst->opcode()); + << _.VkErrorID(12295) << "Illegal number of components (" + << num_components << ") for " << spvOpcodeString(inst->opcode()); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeVectorIdEXT(ValidationState_t& _, + const Instruction* inst) { + const auto component_index = 1; + const auto component_type_id = inst->GetOperandAs<uint32_t>(component_index); + const auto component_type = _.FindDef(component_type_id); + if (!component_type || !_.IsScalarType(component_type_id) || + (!_.HasCapability(spv::Capability::LongVectorEXT) && + spv::Op::OpTypeBool == component_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVectorIdEXT Component Type <id> " + << _.getIdName(component_type_id) + << " is not a scalar numerical type."; + } + + const auto num_components_index = 2; + const auto num_components_id = + inst->GetOperandAs<uint32_t>(num_components_index); + const auto num_components = _.FindDef(num_components_id); + if (!num_components || !spvOpcodeIsConstant(num_components->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVectorIdEXT component count <id> " + << _.getIdName(num_components_id) + << " is not a scalar constant type."; + } + + if (!_.IsIntScalarType(num_components->type_id(), 32)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVectorIdEXT component count type <id> " + << _.getIdName(num_components->type_id()) + << " is not a 32-bit integer type."; + } + + uint64_t num_components_value; + if (_.EvalConstantValUint64(num_components_id, &num_components_value)) { + if (num_components_value == 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVectorIdEXT component count <id> " + << _.getIdName(num_components_id) + << " default value must be at least 1: found 0."; + } } return SPV_SUCCESS; @@ -212,6 +347,19 @@ << " is a void type."; } + if (_.HasCapability(spv::Capability::Shader)) { + if (element_type->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(element_type->id(), spv::Decoration::Block) || + _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) { + if (_.HasDecoration(inst->id(), spv::Decoration::ArrayStride) || + _.HasDecoration(inst->id(), spv::Decoration::ArrayStrideIdEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Array containing a Block or BufferBlock must not be " + "decorated with ArrayStride or ArrayStrideIdEXT"; + } + } + } + if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == spv::Op::OpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -270,6 +418,19 @@ << " is a void type."; } + if (_.HasCapability(spv::Capability::Shader)) { + if (element_type->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(element_type->id(), spv::Decoration::Block) || + _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) { + if (_.HasDecoration(inst->id(), spv::Decoration::ArrayStride) || + _.HasDecoration(inst->id(), spv::Decoration::ArrayStrideIdEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Array containing a Block or BufferBlock must not be " + "decorated with ArrayStride or ArrayStrideIdEXT"; + } + } + } + if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == spv::Op::OpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -307,10 +468,9 @@ << "Structure <id> " << _.getIdName(member_type_id) << " contains members with BuiltIn decoration. Therefore this " << "structure may not be contained as a member of another " - << "structure " - << "type. Structure <id> " << _.getIdName(struct_id) - << " contains structure <id> " << _.getIdName(member_type_id) - << "."; + << "structure " << "type. Structure <id> " + << _.getIdName(struct_id) << " contains structure <id> " + << _.getIdName(member_type_id) << "."; } if (spvIsVulkanEnv(_.context()->target_env) && @@ -340,13 +500,20 @@ // Struct members start at word 2 of OpTypeStruct instruction. for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) { auto member = inst->word(word_i); - auto memberTypeInstr = _.FindDef(member); - if (memberTypeInstr && spv::Op::OpTypeStruct == memberTypeInstr->opcode()) { - if (_.HasDecoration(memberTypeInstr->id(), spv::Decoration::Block) || - _.HasDecoration(memberTypeInstr->id(), - spv::Decoration::BufferBlock) || - _.GetHasNestedBlockOrBufferBlockStruct(memberTypeInstr->id())) - has_nested_blockOrBufferBlock_struct = true; + if (_.ContainsType( + member, + [&_](const Instruction* type_inst) { + if (type_inst->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(type_inst->id(), spv::Decoration::Block) || + _.HasDecoration(type_inst->id(), + spv::Decoration::BufferBlock))) { + return true; + } + return false; + }, + /* traverse_all_types = */ false)) { + has_nested_blockOrBufferBlock_struct = true; + break; } } @@ -363,7 +530,9 @@ std::unordered_set<uint32_t> built_in_members; for (auto decoration : _.id_decorations(struct_id)) { if (decoration.dec_type() == spv::Decoration::BuiltIn && - decoration.struct_member_index() != Decoration::kInvalidMember) { + decoration.struct_member_index() != Decoration::kInvalidMember && + decoration.builtin() != spv::BuiltIn::ResourceHeapEXT && + decoration.builtin() != spv::BuiltIn::SamplerHeapEXT) { built_in_members.insert(decoration.struct_member_index()); } } @@ -381,25 +550,32 @@ _.RegisterStructTypeWithBuiltInMember(struct_id); } - const auto isOpaqueType = [&_](const Instruction* opaque_inst) { - auto opcode = opaque_inst->opcode(); - if (_.HasCapability(spv::Capability::BindlessTextureNV) && - (opcode == spv::Op::OpTypeImage || opcode == spv::Op::OpTypeSampler || - opcode == spv::Op::OpTypeSampledImage)) { - return false; - } else if (spvOpcodeIsBaseOpaqueType(opcode)) { - return true; - } - return false; - }; - if (spvIsVulkanEnv(_.context()->target_env) && - !_.options()->before_hlsl_legalization && - _.ContainsType(inst->id(), isOpaqueType)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4667) << "In " - << spvLogStringForEnv(_.context()->target_env) - << ", OpTypeStruct must not contain an opaque type."; + !_.options()->before_hlsl_legalization) { + // By default, without extensions, all opaque types are invalid in a struct. + // Check the exceptions allowed by the various capabilities + const auto IsInvalidOpaqueType = [&_](const Instruction* opaque_inst) { + const spv::Op opcode = opaque_inst->opcode(); + if (_.HasCapability(spv::Capability::DescriptorHeapEXT) && + _.IsDescriptorType(opcode)) { + return false; + } else if (_.HasCapability(spv::Capability::BindlessTextureNV) && + (opcode == spv::Op::OpTypeImage || + opcode == spv::Op::OpTypeSampler || + opcode == spv::Op::OpTypeSampledImage)) { + return false; + } + return spvOpcodeIsBaseOpaqueType(opcode); + }; + + if (_.ContainsType(inst->id(), IsInvalidOpaqueType)) { + const uint32_t vuid = + _.HasCapability(spv::Capability::DescriptorHeapEXT) ? 11482 : 4667; + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(vuid) << "In " + << spvLogStringForEnv(_.context()->target_env) + << ", OpTypeStruct must not contain an invalid opaque type."; + } } return SPV_SUCCESS; @@ -429,6 +605,9 @@ // a storage image. if (sampled == 2) _.RegisterPointerToStorageImage(inst->id()); } + if (type->opcode() == spv::Op::OpTypeTensorARM) { + _.RegisterPointerToTensor(inst->id()); + } } if (!_.IsValidStorageClass(storage_class)) { @@ -477,12 +656,20 @@ } // The only valid uses of OpTypeFunction are in an OpFunction, debugging, or - // decoration instruction. + // decoration instruction, or in OpTypePointer when FunctionPointersINTEL + // capability is enabled. for (auto& pair : inst->uses()) { const auto* use = pair.first; if (use->opcode() != spv::Op::OpFunction && + use->opcode() != spv::Op::OpAsmINTEL && !spvOpcodeIsDebug(use->opcode()) && !use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) { + // Check if this is OpTypePointer with FunctionPointersINTEL capability + if (use->opcode() == spv::Op::OpTypePointer && + _.HasCapability(spv::Capability::FunctionPointersINTEL)) { + // Allow OpTypePointer to use function types with this capability + continue; + } return _.diag(SPV_ERROR_INVALID_ID, use) << "Invalid use of function type result id " << _.getIdName(inst->id()) << "."; @@ -541,6 +728,24 @@ << " is not a scalar numerical type."; } + if (_.IsBfloat16ScalarType(component_type_id)) { + if (!_.HasCapability(spv::Capability::BFloat16CooperativeMatrixKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Component Type <id> " + << _.getIdName(component_type_id) + << "require BFloat16CooperativeMatrixKHR be declared."; + } + } + + if (_.IsFP8ScalarType(component_type_id)) { + if (!_.HasCapability(spv::Capability::Float8CooperativeMatrixEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Component Type <id> " + << _.getIdName(component_type_id) + << "require Float8CooperativeMatrixEXT be declared."; + } + } + const auto scope_index = 2; const auto scope_id = inst->GetOperandAs<uint32_t>(scope_index); const auto scope = _.FindDef(scope_id); @@ -583,9 +788,10 @@ } } - uint64_t scope_value; - if (_.EvalConstantValUint64(scope_id, &scope_value)) { - if (scope_value == static_cast<uint32_t>(spv::Scope::Workgroup)) { + uint64_t scope_raw_value; + if (_.EvalConstantValUint64(scope_id, &scope_raw_value)) { + spv::Scope scope_value = static_cast<spv::Scope>(scope_raw_value); + if (scope_value == spv::Scope::Workgroup) { for (auto entry_point_id : _.entry_points()) { if (!_.EntryPointHasLocalSizeOrId(entry_point_id)) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -612,6 +818,13 @@ } } } + if (scope_value != spv::Scope::Workgroup && + scope_value != spv::Scope::Subgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(12243) + << "OpTypeCooperativeMatrixKHR Scope is limited to Workgroup and " + "Subgroup"; + } } return SPV_SUCCESS; @@ -635,8 +848,23 @@ case spv::StorageClass::Uniform: case spv::StorageClass::PushConstant: break; + case spv::StorageClass::UniformConstant: + if (!_.HasCapability(spv::Capability::DescriptorHeapEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "UniformConstant storage class untyped pointers in Vulkan " + "require DescriptorHeapEXT be declared"; + } + break; + case spv::StorageClass::Image: + if (!_.HasCapability(spv::Capability::DescriptorHeapEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Image storage class untyped pointers in Vulkan " + "require DescriptorHeapEXT be declared"; + } + break; default: return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(11417) << "In Vulkan, untyped pointers can only be used in an " "explicitly laid out storage class"; } @@ -648,8 +876,7 @@ const auto dim_index = 1; const auto dim_id = inst->GetOperandAs<uint32_t>(dim_index); const auto dim = _.FindDef(dim_id); - if (!dim || !_.IsIntScalarType(dim->type_id()) || - _.GetBitWidth(dim->type_id()) != 32) { + if (!dim || !_.IsIntScalarType(dim->type_id(), 32)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << spvOpcodeString(inst->opcode()) << " Dim <id> " << _.getIdName(dim_id) << " is not a 32-bit integer."; @@ -716,8 +943,7 @@ for (size_t p_index = 3; p_index < inst->operands().size(); ++p_index) { auto p_id = inst->GetOperandAs<uint32_t>(p_index); const auto p = _.FindDef(p_id); - if (!p || !_.IsIntScalarType(p->type_id()) || - _.GetBitWidth(p->type_id()) != 32) { + if (!p || !_.IsIntScalarType(p->type_id(), 32)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << spvOpcodeString(inst->opcode()) << " Permutation <id> " << _.getIdName(p_id) << " is not a 32-bit integer."; @@ -752,6 +978,94 @@ return SPV_SUCCESS; } + +spv_result_t ValidateTypeTensorARM(ValidationState_t& _, + const Instruction* inst) { + // Element type must be a scalar type + const auto element_type_index = 1; + const auto element_type_id = inst->GetOperandAs<uint32_t>(element_type_index); + const auto element_type = _.FindDef(element_type_id); + if (!element_type || (!_.IsFloatScalarType(element_type_id) && + !_.IsIntScalarType(element_type_id) && + !_.IsBoolScalarType(element_type_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Element Type <id> " + << _.getIdName(element_type_id) << " is not a scalar type."; + } + + if (inst->operands().size() < 3) { + return SPV_SUCCESS; + } + + // Rank must be constant instruction with scalar integer type + const auto rank_index = 2; + const auto rank_id = inst->GetOperandAs<uint32_t>(rank_index); + const auto rank = _.FindDef(rank_id); + if (!rank || !spvOpcodeIsConstant(rank->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Rank <id> " << _.getIdName(rank_id) + << " is not a constant instruction."; + } + // Rank must have scalar integer type + if (!rank || !_.IsIntScalarType(rank->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Rank <id> " << _.getIdName(rank_id) + << " does not have a scalar integer type."; + } + // Rank must be greater than 0 + uint64_t rank_value = 0; + if (_.EvalConstantValUint64(rank_id, &rank_value) && rank_value == 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Rank <id> " << _.getIdName(rank_id) + << " must define a value greater than 0."; + } + + if (inst->operands().size() < 4) { + return SPV_SUCCESS; + } + + // Shape must be constant instruction + const auto shape_index = 3; + const auto shape_id = inst->GetOperandAs<uint32_t>(shape_index); + const auto shape = _.FindDef(shape_id); + if (!shape || !spvOpcodeIsConstant(shape->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Shape <id> " << _.getIdName(shape_id) + << " is not a constant instruction."; + } + + // Shape must be array of integer of length rank + if (!_.IsIntArrayType(shape->type_id(), rank_value)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Shape <id> " << _.getIdName(shape_id) + << " is not an array of integer type whose Length is equal to Rank."; + } + + // Shape constituents must be greater than 0 + for (size_t i = 2; i < shape->operands().size(); i++) { + const auto s_id = shape->GetOperandAs<uint32_t>(i); + uint64_t s_val = 0; + if (_.EvalConstantValUint64(s_id, &s_val) && s_val == 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeTensorARM Shape constituent " << i - 2 + << " is not greater than 0."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeBufferEXT(ValidationState_t& _, + const Instruction* inst) { + auto sc = inst->GetOperandAs<spv::StorageClass>(1); + if (sc != spv::StorageClass::Uniform && + sc != spv::StorageClass::StorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) + << " StorageClass could only be StorageBuffer or Uniform."; + } + return SPV_SUCCESS; +} } // namespace spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) { @@ -797,6 +1111,9 @@ case spv::Op::OpTypeCooperativeMatrixKHR: if (auto error = ValidateTypeCooperativeMatrix(_, inst)) return error; break; + case spv::Op::OpTypeVectorIdEXT: + if (auto error = ValidateTypeVectorIdEXT(_, inst)) return error; + break; case spv::Op::OpTypeUntypedPointerKHR: if (auto error = ValidateTypeUntypedPointerKHR(_, inst)) return error; break; @@ -806,6 +1123,12 @@ case spv::Op::OpTypeTensorViewNV: if (auto error = ValidateTypeTensorViewNV(_, inst)) return error; break; + case spv::Op::OpTypeTensorARM: + if (auto error = ValidateTypeTensorARM(_, inst)) return error; + break; + case spv::Op::OpTypeBufferEXT: + if (auto error = ValidateTypeBufferEXT(_, inst)) return error; + break; default: break; }
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index da9174f..a950f71 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2015-2016 The Khronos Group Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,12 +17,14 @@ #include "source/val/validation_state.h" #include <cassert> +#include <cstdint> #include <stack> #include <utility> #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "source/util/make_unique.h" #include "source/val/basic_block.h" #include "source/val/construct.h" @@ -39,14 +43,17 @@ switch (op) { case spv::Op::OpCapability: + case spv::Op::OpConditionalCapabilityINTEL: return kLayoutCapabilities; case spv::Op::OpExtension: + case spv::Op::OpConditionalExtensionINTEL: return kLayoutExtensions; case spv::Op::OpExtInstImport: return kLayoutExtInstImport; case spv::Op::OpMemoryModel: return kLayoutMemoryModel; case spv::Op::OpEntryPoint: + case spv::Op::OpConditionalEntryPointINTEL: return kLayoutEntryPoint; case spv::Op::OpExecutionMode: case spv::Op::OpExecutionModeId: @@ -63,14 +70,22 @@ return kLayoutDebug3; case spv::Op::OpDecorate: case spv::Op::OpMemberDecorate: + case spv::Op::OpMemberDecorateIdEXT: case spv::Op::OpGroupDecorate: case spv::Op::OpGroupMemberDecorate: case spv::Op::OpDecorationGroup: case spv::Op::OpDecorateId: case spv::Op::OpDecorateStringGOOGLE: case spv::Op::OpMemberDecorateStringGOOGLE: + // SPV_INTEL_memory_access_aliasing: alias scope instructions go in the + // annotations section so that OpDecorateId can reference them without + // requiring a forward reference across layout sections. + case spv::Op::OpAliasDomainDeclINTEL: + case spv::Op::OpAliasScopeDeclINTEL: + case spv::Op::OpAliasScopeListDeclINTEL: return kLayoutAnnotations; case spv::Op::OpTypeForwardPointer: + case spv::Op::OpTypeTaskSequenceINTEL: return kLayoutTypes; case spv::Op::OpVariable: case spv::Op::OpUntypedVariableKHR: @@ -81,6 +96,9 @@ // spv::Op::OpExtInst is only allowed in types section for certain // extended instruction sets. This will be checked separately. if (current_section == kLayoutTypes) return kLayoutTypes; + // SpvOpExtInst is allowed in graph definitions. + if (current_section == kLayoutGraphDefinitions) + return kLayoutGraphDefinitions; return kLayoutFunctionDefinitions; case spv::Op::OpLine: case spv::Op::OpNoLine: @@ -95,6 +113,16 @@ return kLayoutFunctionDefinitions; case spv::Op::OpSamplerImageAddressingModeNV: return kLayoutSamplerImageAddressMode; + case spv::Op::OpGraphEntryPointARM: + case spv::Op::OpGraphARM: + case spv::Op::OpGraphInputARM: + case spv::Op::OpGraphSetOutputARM: + case spv::Op::OpGraphEndARM: + return kLayoutGraphDefinitions; + case spv::Op::OpCompositeExtract: + if (current_section == kLayoutGraphDefinitions) + return kLayoutGraphDefinitions; + return kLayoutFunctionDefinitions; default: break; } @@ -170,6 +198,7 @@ pointer_size_and_alignment_(0), sampler_image_addressing_mode_(0), in_function_(false), + graph_definition_region_(kGraphDefinitionOutside), num_of_warnings_(0), max_num_of_warnings_(max_warnings) { assert(opt && "Validator options may not be Null."); @@ -358,6 +387,10 @@ module_functions_.back().current_block() != nullptr; } +GraphDefinitionRegion ValidationState_t::graph_definition_region() const { + return graph_definition_region_; +} + void ValidationState_t::RegisterCapability(spv::Capability cap) { // Avoid redundant work. Otherwise the recursion could induce work // quadrdatic in the capability dependency depth. (Ok, not much, but @@ -365,11 +398,11 @@ if (module_capabilities_.contains(cap)) return; module_capabilities_.insert(cap); - spv_operand_desc desc; - if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, - uint32_t(cap), &desc)) { - for (auto capability : - CapabilitySet(desc->numCapabilities, desc->capabilities)) { + const spvtools::OperandDesc* desc = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + uint32_t(cap), &desc)) { + for (auto capability : CapabilitySet(desc->capabilities_range.count(), + desc->capabilities().data())) { RegisterCapability(capability); } } @@ -395,6 +428,9 @@ case spv::Capability::Float16Buffer: features_.declare_float16_type = true; break; + case spv::Capability::Float8EXT: + features_.declare_float8_type = true; + break; case spv::Capability::StorageUniformBufferBlock16: case spv::Capability::StorageUniform16: case spv::Capability::StoragePushConstant16: @@ -525,6 +561,13 @@ return SPV_SUCCESS; } +void ValidationState_t::SetGraphDefinitionRegion(GraphDefinitionRegion region) { + assert((region == kGraphDefinitionOutside && + graph_definition_region_ == kGraphDefinitionOutputs) || + region >= graph_definition_region_); + graph_definition_region_ = region; +} + Instruction* ValidationState_t::AddOrderedInstruction( const spv_parsed_instruction_t* inst) { ordered_instructions_.emplace_back(inst); @@ -671,13 +714,32 @@ *message = errorVUID + "in Vulkan environment, Workgroup Storage Class is limited " - "to MeshNV, TaskNV, and GLCompute execution model"; + "to MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute " + "execution model"; } return false; } return true; }); } + + if (storage_class == spv::StorageClass::TileImageEXT) { + std::string errorVUID = VkErrorID(8720); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::Fragment) { + if (message) { + *message = errorVUID + + "in Vulkan environment, TileImageEXT Storage " + "Class is limited " + "to Fragment execution model"; + } + return false; + } + return true; + }); + } } if (storage_class == spv::StorageClass::CallableDataKHR) { @@ -824,6 +886,22 @@ } return true; }); + } else if (storage_class == spv::StorageClass::HitObjectAttributeEXT) { + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + "HitObjectAttributeEXT Storage Class is limited to " + "RayGenerationKHR, ClosestHitKHR or MissKHR execution model"; + } + return false; + } + return true; + }); } } @@ -868,9 +946,12 @@ case spv::Op::OpTypeFloat: case spv::Op::OpTypeInt: case spv::Op::OpTypeBool: + case spv::Op::OpTypePointer: + case spv::Op::OpTypeUntypedPointerKHR: return id; case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: return inst->word(2); case spv::Op::OpTypeVector: @@ -881,6 +962,10 @@ case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: + return inst->word(2); + + case spv::Op::OpTypeTensorARM: return inst->word(2); default: @@ -912,6 +997,15 @@ // Actual dimension isn't known, return 0 return 0; + case spv::Op::OpTypeVectorIdEXT: { + uint64_t value = 0; + if (EvalConstantValUint64(inst->word(3), &value)) { + return static_cast<uint32_t>(value); + } + + return 0; + } + default: break; } @@ -927,11 +1021,20 @@ const Instruction* inst = FindDef(component_type_id); assert(inst); - if (inst->opcode() == spv::Op::OpTypeFloat || - inst->opcode() == spv::Op::OpTypeInt) - return inst->word(2); - - if (inst->opcode() == spv::Op::OpTypeBool) return 1; + switch (inst->opcode()) { + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + return inst->word(2); + case spv::Op::OpTypeBool: + return 1; + case spv::Op::OpTypePointer: + case spv::Op::OpTypeUntypedPointerKHR: + assert(inst->GetOperandAs<spv::StorageClass>(1) == + spv::StorageClass::PhysicalStorageBuffer); + return 64; // all pointers to another PSB is 64-bit + default: + break; + } assert(0); return 0; @@ -942,9 +1045,139 @@ return inst && inst->opcode() == spv::Op::OpTypeVoid; } -bool ValidationState_t::IsFloatScalarType(uint32_t id) const { +bool ValidationState_t::IsScalarType(uint32_t id) const { + return IsIntScalarType(id) || IsFloatScalarType(id) || IsBoolScalarType(id); +} + +bool ValidationState_t::IsVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); - return inst && inst->opcode() == spv::Op::OpTypeFloat; + if (!inst) { + return false; + } + + return inst->opcode() == spv::Op::OpTypeVector || + inst->opcode() == spv::Op::OpTypeVectorIdEXT; +} + +bool ValidationState_t::IsArrayType(uint32_t id, uint64_t length) const { + const Instruction* inst = FindDef(id); + if (!inst || inst->opcode() != spv::Op::OpTypeArray) { + return false; + } + if (length != 0) { + const auto len_id = inst->GetOperandAs<uint32_t>(2); + const auto len = FindDef(len_id); + uint64_t len_value = 0; + if (!len || !spvOpcodeIsConstant(len->opcode()) || + (EvalConstantValUint64(len_id, &len_value) && (length != len_value))) { + return false; + } + } + return true; +} + +bool ValidationState_t::IsBfloat16ScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (inst && inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->words().size() > 3) { + if (inst->GetOperandAs<spv::FPEncoding>(2) == + spv::FPEncoding::BFloat16KHR) { + return true; + } + } + } + return false; +} + +bool ValidationState_t::IsBfloat16VectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (IsVectorType(id)) { + return IsBfloat16ScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsBfloat16CoopMatType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { + return IsBfloat16ScalarType(inst->word(2)); + } + + return false; +} + +bool ValidationState_t::IsBfloat16Type(uint32_t id) const { + return IsBfloat16ScalarType(id) || IsBfloat16VectorType(id) || + IsBfloat16CoopMatType(id); +} + +bool ValidationState_t::IsFP8ScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (inst && inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->words().size() > 3) { + auto encoding = inst->GetOperandAs<spv::FPEncoding>(2); + if ((encoding == spv::FPEncoding::Float8E4M3EXT) || + (encoding == spv::FPEncoding::Float8E5M2EXT)) { + return true; + } + } + } + return false; +} + +bool ValidationState_t::IsFP8VectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (IsVectorType(id)) { + return IsFP8ScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFP8CoopMatType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { + return IsFP8ScalarType(inst->word(2)); + } + + return false; +} + +bool ValidationState_t::IsFP8Type(uint32_t id) const { + return IsFP8ScalarType(id) || IsFP8VectorType(id) || IsFP8CoopMatType(id); +} + +bool ValidationState_t::IsFloatScalarType(uint32_t id, uint32_t width) const { + const Instruction* inst = FindDef(id); + bool is_float = inst && inst->opcode() == spv::Op::OpTypeFloat; + if (!is_float) { + return false; + } + if ((width != 0) && (width != inst->word(2))) { + return false; + } + return true; +} + +bool ValidationState_t::IsFloatArrayType(uint32_t id) const { + return IsArrayType(id) && IsFloatScalarType(GetComponentType(id)); } bool ValidationState_t::IsFloatVectorType(uint32_t id) const { @@ -953,7 +1186,7 @@ return false; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsFloatScalarType(GetComponentType(id)); } @@ -961,10 +1194,7 @@ } bool ValidationState_t::IsFloat16Vector2Or4Type(uint32_t id) const { - const Instruction* inst = FindDef(id); - assert(inst); - - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { uint32_t vectorDim = GetDimension(id); return IsFloatScalarType(GetComponentType(id)) && (vectorDim == 2 || vectorDim == 4) && @@ -984,29 +1214,34 @@ return true; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsFloatScalarType(GetComponentType(id)); } return false; } -bool ValidationState_t::IsIntScalarType(uint32_t id) const { +bool ValidationState_t::IsIntScalarType(uint32_t id, uint32_t width) const { const Instruction* inst = FindDef(id); - return inst && inst->opcode() == spv::Op::OpTypeInt; -} - -bool ValidationState_t::IsIntArrayType(uint32_t id) const { - const Instruction* inst = FindDef(id); - if (!inst) { + bool is_int = inst && inst->opcode() == spv::Op::OpTypeInt; + if (!is_int) { return false; } - - if (inst->opcode() == spv::Op::OpTypeArray) { - return IsIntScalarType(GetComponentType(id)); + if ((width != 0) && (width != inst->word(2))) { + return false; } + return true; +} - return false; +bool ValidationState_t::IsIntScalarTypeWithSignedness( + uint32_t id, uint32_t signedness) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeInt && + inst->word(3) == signedness; +} + +bool ValidationState_t::IsIntArrayType(uint32_t id, uint64_t length) const { + return IsArrayType(id, length) && IsIntScalarType(GetComponentType(id)); } bool ValidationState_t::IsIntVectorType(uint32_t id) const { @@ -1015,7 +1250,7 @@ return false; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsIntScalarType(GetComponentType(id)); } @@ -1032,7 +1267,7 @@ return true; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsIntScalarType(GetComponentType(id)); } @@ -1040,8 +1275,7 @@ } bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { - const Instruction* inst = FindDef(id); - return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 0; + return IsIntScalarTypeWithSignedness(id, 0); } bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { @@ -1050,7 +1284,7 @@ return false; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsUnsignedIntScalarType(GetComponentType(id)); } @@ -1067,7 +1301,7 @@ return inst->GetOperandAs<uint32_t>(2) == 0; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsUnsignedIntScalarType(GetComponentType(id)); } @@ -1085,7 +1319,7 @@ return false; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsSignedIntScalarType(GetComponentType(id)); } @@ -1103,7 +1337,7 @@ return false; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsBoolScalarType(GetComponentType(id)); } @@ -1120,7 +1354,7 @@ return true; } - if (inst->opcode() == spv::Op::OpTypeVector) { + if (IsVectorType(id)) { return IsBoolScalarType(GetComponentType(id)); } @@ -1185,6 +1419,7 @@ } bool ValidationState_t::IsPointerType(uint32_t id) const { + if (!id) return false; const Instruction* inst = FindDef(id); assert(inst); return inst->opcode() == spv::Op::OpTypePointer || @@ -1211,6 +1446,29 @@ return true; } +uint32_t ValidationState_t::GetLargestScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + + switch (inst->opcode()) { + case spv::Op::OpTypeStruct: { + uint32_t size = 0; + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + const uint32_t member_size = + GetLargestScalarType(inst->GetOperandAs<uint32_t>(i)); + size = std::max(size, member_size); + } + return size; + } + case spv::Op::OpTypeArray: + return GetLargestScalarType(inst->GetOperandAs<uint32_t>(1)); + case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: + return GetLargestScalarType(inst->GetOperandAs<uint32_t>(1)); + default: + return GetBitWidth(id) / 8; + } +} + bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const { const Instruction* inst = FindDef(id); return inst && inst->opcode() == spv::Op::OpTypeAccelerationStructureKHR; @@ -1289,6 +1547,116 @@ GetBitWidth(id) == 32)); } +bool ValidationState_t::IsCooperativeVectorNVType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeVectorIdEXT; +} + +bool ValidationState_t::IsFloatCooperativeVectorNVType(uint32_t id) const { + if (!IsCooperativeVectorNVType(id)) return false; + return IsFloatScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsIntCooperativeVectorNVType(uint32_t id) const { + if (!IsCooperativeVectorNVType(id)) return false; + return IsIntScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsUnsignedIntCooperativeVectorNVType( + uint32_t id) const { + if (!IsCooperativeVectorNVType(id)) return false; + return IsUnsignedIntScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsTensorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeTensorARM; +} + +// Opaque handles from [Descriptor] section (added from SPV_EXT_descriptor_heap) +bool ValidationState_t::IsDescriptorType(spv::Op opcode) const { + return opcode == spv::Op::OpTypeBufferEXT || opcode == spv::Op::OpTypeImage || + opcode == spv::Op::OpTypeTensorARM || + opcode == spv::Op::OpTypeSampler || + opcode == spv::Op::OpTypeAccelerationStructureKHR; +} + +// Opaque handles from [Descriptor] section (added from SPV_EXT_descriptor_heap) +bool ValidationState_t::IsDescriptorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && IsDescriptorType(inst->opcode()); +} + +const Instruction* ValidationState_t::FindUntypedBaseVariable( + const Instruction* inst) { + bool found_heap_base = false; + const Instruction* base_inst = inst; + while (!found_heap_base) { + switch (base_inst->opcode()) { + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: + case spv::Op::OpUntypedArrayLengthKHR: + base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(3)); + break; + case spv::Op::OpLoad: + case spv::Op::OpAtomicLoad: + if (GetIdOpcode(GetOperandTypeId(base_inst, 2)) == + spv::Op::OpTypeUntypedPointerKHR) { + base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(2)); + } + break; + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(2)); + break; + case spv::Op::OpStore: + case spv::Op::OpAtomicStore: + if (GetIdOpcode(GetOperandTypeId(base_inst, 0)) == + spv::Op::OpTypeUntypedPointerKHR) { + base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(0)); + } + break; + default: + found_heap_base = true; + break; + } + + if (found_heap_base) { + break; + } + } + + return base_inst; +} + +bool ValidationState_t::IsDescriptorHeapBaseVariable(const Instruction* inst) { + if (!HasCapability(spv::Capability::DescriptorHeapEXT)) { + return false; + } + const Instruction* base_inst = FindUntypedBaseVariable(inst); + const bool is_heap_base = + IsBuiltin(base_inst->id(), spv::BuiltIn::SamplerHeapEXT) || + IsBuiltin(base_inst->id(), spv::BuiltIn::ResourceHeapEXT); + + return FindDef(base_inst->id())->opcode() == spv::Op::OpBufferPointerEXT || + (FindDef(base_inst->id())->opcode() == spv::Op::OpUntypedVariableKHR && + is_heap_base); +} + spv_result_t ValidationState_t::CooperativeMatrixShapesMatch( const Instruction* inst, uint32_t result_type_id, uint32_t m2, bool is_conversion, bool swap_row_col) { @@ -1323,8 +1691,7 @@ if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { return diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected scopes of Matrix and Result Type to be " - << "identical"; + << "Expected scopes of Matrix and Result Type to be " << "identical"; } std::tie(m1_is_int32, m1_is_const_int32, m1_value) = @@ -1372,6 +1739,36 @@ return SPV_SUCCESS; } +spv_result_t ValidationState_t::CooperativeVectorDimensionsMatch( + const Instruction* inst, uint32_t v1, uint32_t v2) { + const auto v1_type = FindDef(v1); + const auto v2_type = FindDef(v2); + + if (v1_type->opcode() != v2_type->opcode()) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative vector types"; + } + + uint32_t v1_components_id = v1_type->GetOperandAs<uint32_t>(2); + uint32_t v2_components_id = v2_type->GetOperandAs<uint32_t>(2); + + bool v1_is_int32 = false, v1_is_const_int32 = false, v2_is_int32 = false, + v2_is_const_int32 = false; + uint32_t v1_value = 0, v2_value = 0; + + std::tie(v1_is_int32, v1_is_const_int32, v1_value) = + EvalInt32IfConst(v1_components_id); + std::tie(v2_is_int32, v2_is_const_int32, v2_value) = + EvalInt32IfConst(v2_components_id); + + if (v1_is_const_int32 && v2_is_const_int32 && v1_value != v2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of components to be identical"; + } + + return SPV_SUCCESS; +} + uint32_t ValidationState_t::GetOperandTypeId(const Instruction* inst, size_t operand_index) const { return GetTypeId(inst->GetOperandAs<uint32_t>(operand_index)); @@ -1429,13 +1826,14 @@ return true; } +// <is_int32, is_const_int32, value> std::tuple<bool, bool, uint32_t> ValidationState_t::EvalInt32IfConst( uint32_t id) const { const Instruction* const inst = FindDef(id); assert(inst); const uint32_t type = inst->type_id(); - if (type == 0 || !IsIntScalarType(type) || GetBitWidth(type) != 32) { + if (type == 0 || !IsIntScalarType(type, 32)) { return std::make_tuple(false, false, 0); } @@ -1664,6 +2062,7 @@ case spv::Op::OpTypeSampledImage: case spv::Op::OpTypeCooperativeMatrixNV: case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeVectorIdEXT: return ContainsType(inst->GetOperandAs<uint32_t>(1u), f, traverse_all_types); case spv::Op::OpTypePointer: @@ -1698,6 +2097,10 @@ const auto f = [type, width](const Instruction* inst) { if (inst->opcode() == type) { + // Bfloat16 is a special type. + if (type == spv::Op::OpTypeFloat && inst->words().size() > 3) + return false; + return inst->GetOperandAs<uint32_t>(1u) == width; } return false; @@ -1734,6 +2137,7 @@ case spv::Op::OpTypeArray: case spv::Op::OpTypeRuntimeArray: case spv::Op::OpTypeVector: + case spv::Op::OpTypeVectorIdEXT: case spv::Op::OpTypeMatrix: case spv::Op::OpTypeImage: case spv::Op::OpTypeSampledImage: @@ -1757,6 +2161,15 @@ return false; } +std::vector<uint32_t>& ValidationState_t::GetDebugSourceLineLength( + uint32_t id) { + auto it = debug_source_line_length_.find(id); + if (it == debug_source_line_length_.end()) { + return debug_source_line_length_[id]; + } + return it->second; +} + bool ValidationState_t::IsValidStorageClass( spv::StorageClass storage_class) const { if (spvIsVulkanEnv(context()->target_env)) { @@ -1780,7 +2193,10 @@ case spv::StorageClass::ShaderRecordBufferKHR: case spv::StorageClass::TaskPayloadWorkgroupEXT: case spv::StorageClass::HitObjectAttributeNV: + case spv::StorageClass::HitObjectAttributeEXT: case spv::StorageClass::TileImageEXT: + case spv::StorageClass::NodePayloadAMDX: + case spv::StorageClass::TileAttachmentQCOM: return true; default: return false; @@ -1790,6 +2206,14 @@ return true; } +std::string ValidationState_t::MissingFeature(const std::string& feature, + const std::string& cmdline, + bool hint) const { + return "\nThis is " + (hint ? std::string("may be ") : "") + + "allowed if you enable the " + feature + " (or use the " + cmdline + + " command line flag)"; +} + #define VUID_WRAP(vuid) "[" #vuid "] " // Currently no 2 VUID share the same id, so no need for |reference| @@ -1992,6 +2416,12 @@ return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282); case 4283: return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283); + case 4284: + return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04284); + case 4285: + return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04285); + case 4286: + return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04286); case 4293: return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04293); case 4294: @@ -2052,8 +2482,12 @@ return VUID_WRAP(VUID-Position-Position-04321); case 4330: return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04330); + case 4333: + return VUID_WRAP(VUID-PrimitiveId-Fragment-04333); case 4334: return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04334); + case 4336: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04336); case 4337: return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04337); case 4345: @@ -2238,8 +2672,6 @@ return VUID_WRAP(VUID-StandaloneSpirv-None-04644); case 4645: return VUID_WRAP(VUID-StandaloneSpirv-None-04645); - case 4650: - return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04650); case 4651: return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651); case 4652: @@ -2258,8 +2690,8 @@ return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658); case 4659: return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659); - case 4663: - return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663); + case 4660: + return VUID_WRAP(VUID-StandaloneSpirv-SubpassData-04660); case 4664: return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664); case 4667: @@ -2276,8 +2708,6 @@ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); case 4682: return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); - case 6426: - return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683 case 4685: return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); case 4686: @@ -2308,14 +2738,6 @@ return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710); case 4711: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711); - case 4730: - return VUID_WRAP(VUID-StandaloneSpirv-OpAtomicStore-04730); - case 4731: - return VUID_WRAP(VUID-StandaloneSpirv-OpAtomicLoad-04731); - case 4732: - return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732); - case 4733: - return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733); case 4734: return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734); case 4744: @@ -2324,8 +2746,6 @@ return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777); case 4780: return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); - case 4781: - return VUID_WRAP(VUID-StandaloneSpirv-Base-04781); case 4915: return VUID_WRAP(VUID-StandaloneSpirv-Location-04915); case 4916: @@ -2344,14 +2764,16 @@ return VUID_WRAP(VUID-StandaloneSpirv-Component-04922); case 4923: return VUID_WRAP(VUID-StandaloneSpirv-Component-04923); - case 4924: - return VUID_WRAP(VUID-StandaloneSpirv-Component-04924); + case 4965: + return VUID_WRAP(VUID-StandaloneSpirv-Image-04965); case 6201: return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201); case 6202: return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202); case 6214: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214); + case 6314: + return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314); case 6491: return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491); case 6671: @@ -2382,30 +2804,64 @@ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06924); case 6925: return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925); + case 7034: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034); + case 7035: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035); + case 7036: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036); + case 7038: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07038); + case 7039: + return VUID_WRAP(VUID-Layer-Layer-07039); + case 7040: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-07040); case 7041: return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041); + case 7042: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042); case 7043: return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043); case 7044: return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044); + case 7046: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046); case 7047: return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07047); + case 7048: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048); case 7049: return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07049); case 7050: return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07050); + case 7052: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052); case 7053: return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07053); + case 7054: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054); case 7055: return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07055); case 7056: return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07056); + case 7058: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07058); + case 7059: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-07059); + case 7060: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-07060); case 7102: return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102); - case 7320: - return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); + case 7107: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07107); case 7290: return VUID_WRAP(VUID-StandaloneSpirv-Input-07290); + case 7320: + return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); + case 7330: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07330); + case 7331: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07331); case 7650: return VUID_WRAP(VUID-StandaloneSpirv-Base-07650); case 7651: @@ -2416,18 +2872,139 @@ return VUID_WRAP(VUID-StandaloneSpirv-Component-07703); case 7951: return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951); + case 8720: + return VUID_WRAP(VUID-StandaloneSpirv-None-08720); case 8721: return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08721); case 8722: return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08722); + case 8747: + return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08747); + case 8748: + return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08748); + case 8749: + return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08749); case 8973: return VUID_WRAP(VUID-StandaloneSpirv-Pointer-08973); + case 9557: + return VUID_WRAP(VUID-StandaloneSpirv-Input-09557); case 9638: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-09638); case 9658: return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09658); case 9659: return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09659); + case 10151: + return VUID_WRAP(VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151); + case 10152: + return VUID_WRAP(VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152); + case 10213: + // This use to be a standalone, but maintenance8 will set allow_offset_texture_operand now + return VUID_WRAP(VUID-RuntimeSpirv-Offset-10213); + case 10370: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10370); + case 10583: + return VUID_WRAP(VUID-StandaloneSpirv-Component-10583); + case 10589: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10589); + case 10590: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10590); + case 10591: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10591); + case 10592: + return VUID_WRAP(VUID-Layer-Layer-10592); + case 10593: + return VUID_WRAP(VUID-Layer-Layer-10593); + case 10594: + return VUID_WRAP(VUID-Layer-Layer-10594); + case 10598: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598); + case 10599: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10599); + case 10600: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10600); + case 10601: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10601); + case 10602: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10602); + case 10603: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10603); + case 10684: + return VUID_WRAP(VUID-StandaloneSpirv-None-10684); + case 10685: + return VUID_WRAP(VUID-StandaloneSpirv-None-10685); // formally 04683/06426 + case 10823: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10823); + case 10824: + // This use to be a standalone, but maintenance9 will set allow_vulkan_32_bit_bitwise now + return VUID_WRAP(VUID-RuntimeSpirv-None-10824); + case 10865: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10865); + case 10866: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10866); + case 10867: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10867); + case 10868: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10868); + case 10869: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10869); + case 10870: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10870); + case 10871: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10871); + case 10872: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10872); + case 10873: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10873); + case 10874: + return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10874); + case 10875: + return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10875); + case 10876: + return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10876); + case 10877: + return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10877); + case 10878: + return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10878); + case 10879: + return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10879); + case 10880: + return VUID_WRAP(VUID-StandaloneSpirv-TessLevelInner-10880); + case 11165: + // Validation (via GPU-AV) will catch this if a non-constant + return VUID_WRAP(VUID-RuntimeSpirv-Size-11165); + case 11167: + return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11167); + case 11239: + return VUID_WRAP(VUID-SamplerHeapEXT-SamplerHeapEXT-11239); + case 11241: + return VUID_WRAP(VUID-ResourceHeapEXT-ResourceHeapEXT-11241); + case 11336: + return VUID_WRAP(VUID-StandaloneSpirv-Result-11336); + case 11337: + return VUID_WRAP(VUID-StandaloneSpirv-Result-11337); + case 11339: + return VUID_WRAP(VUID-StandaloneSpirv-Result-11339); + case 11346: + return VUID_WRAP(VUID-StandaloneSpirv-Result-11346); + case 11347: + return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11347); + case 11416: + return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedImageTexelPointerEXT-11416); + case 11417: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeUntypedPointerKHR-11417); + case 11482: + return VUID_WRAP(VUID-StandaloneSpirv-DescriptorHeapEXT-11482); + case 11805: + return VUID_WRAP(VUID-StandaloneSpirv-OpArrayLength-11805); + case 12243: + return VUID_WRAP(VUID-StandaloneSpirv-Scope-12243); + case 12294: + return VUID_WRAP(VUID-StandaloneSpirv-Function-12294); + case 12295: + return VUID_WRAP(VUID-StandaloneSpirv-None-12295); + case 12297: + return VUID_WRAP(VUID-StandaloneSpirv-Type-12297); default: return ""; // unknown id }
diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 44551ad..9eecb2a 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h
@@ -32,6 +32,7 @@ #include "source/name_mapper.h" #include "source/spirv_definition.h" #include "source/spirv_validator_options.h" +#include "source/table2.h" #include "source/val/decoration.h" #include "source/val/function.h" #include "source/val/instruction.h" @@ -49,6 +50,7 @@ kLayoutExtInstImport, /// < Section 2.4 #3 kLayoutMemoryModel, /// < Section 2.4 #4 kLayoutSamplerImageAddressMode, /// < Section 2.4 #5 + /// (SPV_NV_bindless_texture) kLayoutEntryPoint, /// < Section 2.4 #6 kLayoutExecutionMode, /// < Section 2.4 #7 kLayoutDebug1, /// < Section 2.4 #8 > 1 @@ -57,7 +59,18 @@ kLayoutAnnotations, /// < Section 2.4 #9 kLayoutTypes, /// < Section 2.4 #10 kLayoutFunctionDeclarations, /// < Section 2.4 #11 - kLayoutFunctionDefinitions /// < Section 2.4 #12 + kLayoutFunctionDefinitions, /// < Section 2.4 #12 + kLayoutGraphDefinitions /// < Section 2.4 #13 (SPV_ARM_graph) +}; + +/// This enum represents the regions of a graph definition. The relative +/// ordering of the values is significant. +enum GraphDefinitionRegion { + kGraphDefinitionOutside, + kGraphDefinitionBegin, + kGraphDefinitionInputs, + kGraphDefinitionBody, + kGraphDefinitionOutputs, }; /// This class manages the state of the SPIR-V validation as it is being parsed. @@ -67,6 +80,7 @@ struct Feature { bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? + bool declare_float8_type = false; // Allow OpTypeFloat with 8 bit width? bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration // and its values to be used without // requiring any capability @@ -211,6 +225,9 @@ /// instruction bool in_block() const; + /// Returns the region of a graph definition we are in. + GraphDefinitionRegion graph_definition_region() const; + struct EntryPointDescription { std::string name; std::vector<uint32_t> interfaces; @@ -245,6 +262,41 @@ const Instruction* inst) { entry_point_to_local_size_or_id_[entry_point] = inst; } + + /// Registers that the entry point maximum number of primitives + /// mesh shader will ever emit + void RegisterEntryPointOutputPrimitivesEXT(uint32_t entry_point, + const Instruction* inst) { + entry_point_to_output_primitives_[entry_point] = inst; + } + + /// Returns the maximum number of primitives mesh shader can emit + uint32_t GetOutputPrimitivesEXT(uint32_t entry_point) const { + auto entry = entry_point_to_output_primitives_.find(entry_point); + if (entry != entry_point_to_output_primitives_.end()) { + auto inst = entry->second; + return inst->GetOperandAs<uint32_t>(2); + } + return 0; + } + + /// Registers that the entry point maximum number of vertices + /// mesh shader will ever emit + void RegisterEntryPointOutputVertices(uint32_t entry_point, + const Instruction* inst) { + entry_point_to_output_vertices_[entry_point] = inst; + } + + /// Returns the maximum number of primitives mesh shader can emit + uint32_t GetOutputVertices(uint32_t entry_point) const { + auto entry = entry_point_to_output_vertices_.find(entry_point); + if (entry != entry_point_to_output_vertices_.end()) { + auto inst = entry->second; + return inst->GetOperandAs<uint32_t>(2); + } + return 0; + } + /// Returns whether the entry point declares its local size bool EntryPointHasLocalSizeOrId(uint32_t entry_point) const { return entry_point_to_local_size_or_id_.find(entry_point) != @@ -293,6 +345,16 @@ /// ComputeFunctionToEntryPointMapping. void ComputeRecursiveEntryPoints(); + /// Registers |id| as a graph entry point. + void RegisterGraphEntryPoint(const uint32_t id) { + graph_entry_points_.push_back(id); + } + + /// Returns a list of graph entry point graph ids + const std::vector<uint32_t>& graph_entry_points() const { + return graph_entry_points_; + } + /// Returns all the entry points that can call |func|. const std::vector<uint32_t>& FunctionEntryPoints(uint32_t func) const; @@ -330,6 +392,9 @@ /// Register a function end instruction spv_result_t RegisterFunctionEnd(); + /// Sets the region of a graph definition we're in. + void SetGraphDefinitionRegion(GraphDefinitionRegion region); + /// Returns true if the capability is enabled in the module. bool HasCapability(spv::Capability cap) const { return module_capabilities_.contains(cap); @@ -474,6 +539,29 @@ [dec](const Decoration& d) { return dec == d.dec_type(); }); } + /// Returns true if the given id <id> has the given built-in decoration <bt>, + /// otherwise returns false. + bool IsBuiltin(spv::Id id, spv::BuiltIn bt) { + for (auto& dec : id_decorations(id)) { + if (dec.dec_type() == spv::Decoration::BuiltIn) { + if (dec.builtin() == bt) return true; + break; + } + } + return false; + } + + bool ContainsBuiltin(spv::Id id, spv::BuiltIn bt) { + const auto isHeapType = [&](const Instruction* inst) { + if (HasCapability(spv::Capability::DescriptorHeapEXT) && + IsBuiltin(inst->id(), bt)) { + return true; + } + return false; + }; + return ContainsType(uint32_t(id), isHeapType); + } + /// Finds id's def, if it exists. If found, returns the definition otherwise /// nullptr const Instruction* FindDef(uint32_t id) const; @@ -612,16 +700,27 @@ bool GetStructMemberTypes(uint32_t struct_type_id, std::vector<uint32_t>* member_types) const; - // Returns true iff |id| is a type corresponding to the name of the function. + // Returns true if |id| is a type corresponding to the name of the function. // Only works for types not for objects. bool IsVoidType(uint32_t id) const; - bool IsFloatScalarType(uint32_t id) const; + bool IsScalarType(uint32_t id) const; + bool IsVectorType(uint32_t id) const; + bool IsBfloat16ScalarType(uint32_t id) const; + bool IsBfloat16VectorType(uint32_t id) const; + bool IsBfloat16CoopMatType(uint32_t id) const; + bool IsBfloat16Type(uint32_t id) const; + bool IsFP8ScalarType(uint32_t id) const; + bool IsFP8VectorType(uint32_t id) const; + bool IsFP8CoopMatType(uint32_t id) const; + bool IsFP8Type(uint32_t id) const; + bool IsFloatScalarType(uint32_t id, uint32_t width = 0) const; + bool IsFloatArrayType(uint32_t id) const; bool IsFloatVectorType(uint32_t id) const; bool IsFloat16Vector2Or4Type(uint32_t id) const; bool IsFloatScalarOrVectorType(uint32_t id) const; bool IsFloatMatrixType(uint32_t id) const; - bool IsIntScalarType(uint32_t id) const; - bool IsIntArrayType(uint32_t id) const; + bool IsIntScalarType(uint32_t id, uint32_t width = 0) const; + bool IsIntScalarTypeWithSignedness(uint32_t id, uint32_t signedness) const; bool IsIntVectorType(uint32_t id) const; bool IsIntScalarOrVectorType(uint32_t id) const; bool IsUnsignedIntScalarType(uint32_t id) const; @@ -644,6 +743,44 @@ bool IsIntCooperativeMatrixType(uint32_t id) const; bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const; bool IsUnsigned64BitHandle(uint32_t id) const; + bool IsCooperativeVectorNVType(uint32_t id) const; + bool IsFloatCooperativeVectorNVType(uint32_t id) const; + bool IsIntCooperativeVectorNVType(uint32_t id) const; + bool IsUnsignedIntCooperativeVectorNVType(uint32_t id) const; + bool IsTensorType(uint32_t id) const; + bool IsDescriptorType(spv::Op opcode) const; + bool IsDescriptorType(uint32_t id) const; + // When |length| is not 0, return true only if the array length is equal to + // |length| and the array length is not defined by a specialization constant. + bool IsArrayType(uint32_t id, uint64_t length = 0) const; + bool IsIntArrayType(uint32_t id, uint64_t length = 0) const; + template <unsigned int N> + bool IsIntNOrFP32OrFP16(unsigned int type_id) { + return this->ContainsType( + type_id, + [](const Instruction* inst) { + if (inst->opcode() == spv::Op::OpTypeInt) { + return inst->GetOperandAs<uint32_t>(1) == N; + } else if (inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->operands().size() > 2) { + // Not IEEE + return false; + } + auto width = inst->GetOperandAs<uint32_t>(1); + return width == 32 || width == 16; + } + return false; + }, + /* traverse_all_types = */ false); + } + + // Will walk the type to find the largest scalar value size. + // Returns value is in bytes. + // This is designed to pass in the %type from a PSB pointer + // %ptr = OpTypePointer PhysicalStorageBuffer %type + uint32_t GetLargestScalarType(uint32_t id) const; + bool IsDescriptorHeapBaseVariable(const Instruction* inst); + const Instruction* FindUntypedBaseVariable(const Instruction* inst); // Returns true if |id| is a type id that contains |type| (or integer or // floating point type) of |width| bits. @@ -684,6 +821,17 @@ bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type, spv::StorageClass* storage_class) const; + // Returns the value assocated with id via 'value' if id is an OpConstant + template <typename T> + bool GetConstantValueAs(unsigned int id, T& value) { + const auto inst = FindDef(id); + uint64_t ui64_val = 0u; + bool status = (inst && spvOpcodeIsConstant(inst->opcode()) && + EvalConstantValUint64(id, &ui64_val)); + if (status == true) value = static_cast<T>(ui64_val); + return status; + } + // Is the ID the type of a pointer to a uniform block: Block-decorated struct // in uniform storage class? The result is only valid after internal method // CheckDecorationsOfBuffers has been called. @@ -741,6 +889,16 @@ pointer_to_storage_image_.insert(type_id); } + // Is the ID the type of a pointer to a tensor? That is, the pointee + // type is a tensor type. + bool IsPointerToTensor(uint32_t type_id) const { + return pointer_to_tensor_.find(type_id) != pointer_to_tensor_.cend(); + } + // Save the ID of a pointer to a tensor. + void RegisterPointerToTensor(uint32_t type_id) { + pointer_to_tensor_.insert(type_id); + } + // Tries to evaluate a any scalar integer OpConstant as uint64. // OpConstantNull is defined as zero for scalar int (will return true) // OpSpecConstant* return false since their values cannot be relied upon @@ -763,12 +921,12 @@ // Returns the string name for |decoration|. std::string SpvDecorationString(uint32_t decoration) { - spv_operand_desc desc = nullptr; - if (grammar_.lookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration, - &desc) != SPV_SUCCESS) { + const spvtools::OperandDesc* desc = nullptr; + if (spvtools::LookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration, + &desc) != SPV_SUCCESS) { return std::string("Unknown"); } - return std::string(desc->name); + return std::string(desc->name().data()); } std::string SpvDecorationString(spv::Decoration decoration) { return SpvDecorationString(uint32_t(decoration)); @@ -783,6 +941,9 @@ uint32_t m2, bool is_conversion, bool swap_row_col = false); + spv_result_t CooperativeVectorDimensionsMatch(const Instruction* inst, + uint32_t v1, uint32_t v2); + // Returns true if |lhs| and |rhs| logically match and, if the decorations of // |rhs| are a subset of |lhs|. // @@ -810,6 +971,12 @@ // Validates the storage class for the target environment. bool IsValidStorageClass(spv::StorageClass storage_class) const; + // Helps formulate a mesesage to user that setting one of the validator + // options might make their SPIR-V actually valid The |hint| option is because + // some checks are intertwined with each other, so hard to give confirmation + std::string MissingFeature(const std::string& feature, + const std::string& cmdline, bool hint) const; + // Takes a Vulkan Valid Usage ID (VUID) as |id| and optional |reference| and // will return a non-empty string only if ID is known and targeting Vulkan. // VUIDs are found in the Vulkan-Docs repo in the form "[[VUID-ref-ref-id]]" @@ -833,6 +1000,10 @@ qcom_image_processing_consumers_.end(); } + // Get the list of line lengths for a given result ID of a DebugSource + // instruction Will create a new vector if DebugSource is not found + std::vector<uint32_t>& GetDebugSourceLineLength(uint32_t id); + private: ValidationState_t(const ValidationState_t&); @@ -905,6 +1076,9 @@ /// graph that recurses. std::set<uint32_t> recursive_entry_points_; + /// IDs that are graph entry points, ie, arguments to OpGraphEntryPointARM. + std::vector<uint32_t> graph_entry_points_; + /// Functions IDs that are target of OpFunctionCall. std::unordered_set<uint32_t> function_call_targets_; @@ -947,9 +1121,13 @@ /// bit width of sampler/image type variables. Valid values are 32 and 64 uint32_t sampler_image_addressing_mode_; - /// NOTE: See correspoding getter functions + /// NOTE: See corresponding getter functions bool in_function_; + /// Where in a graph definition we are + /// NOTE: See corresponding getter/setter functions + GraphDefinitionRegion graph_definition_region_; + /// The state of optional features. These are determined by capabilities /// declared by the module and the environment. Feature features_; @@ -971,6 +1149,14 @@ std::unordered_map<uint32_t, const Instruction*> entry_point_to_local_size_or_id_; + // Mapping entry point -> OutputPrimitivesEXT execution mode instruction + std::unordered_map<uint32_t, const Instruction*> + entry_point_to_output_primitives_; + + // Mapping entry point -> OutputVertices execution mode instruction + std::unordered_map<uint32_t, const Instruction*> + entry_point_to_output_vertices_; + /// Mapping function -> array of entry points inside this /// module which can (indirectly) call the function. std::unordered_map<uint32_t, std::vector<uint32_t>> function_to_entry_points_; @@ -992,6 +1178,13 @@ // The IDs of types of pointers to storage images. This is populated in the // TypePass. std::unordered_set<uint32_t> pointer_to_storage_image_; + // The IDs of types of pointers to tensors. This is populated in the + // TypePass. + std::unordered_set<uint32_t> pointer_to_tensor_; + + /// Maps an id of DebugSource to a vector that contains the length of each + /// line side of it. (Also will have the DebugSourceContinued source included) + std::unordered_map<uint32_t, std::vector<uint32_t>> debug_source_line_length_; /// Maps ids to friendly names. std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper_;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 119e9c9..2627f9a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt
@@ -103,24 +103,30 @@ diagnostic_test.cpp enum_string_mapping_test.cpp enum_set_test.cpp + ext_inst.arm_motion_engine_test.cpp ext_inst.cldebug100_test.cpp ext_inst.debuginfo_test.cpp ext_inst.glsl_test.cpp ext_inst.non_semantic_test.cpp ext_inst.opencl_test.cpp + ext_inst.tosa_test.cpp + ext_inst_lookup_test.cpp fix_word_test.cpp generator_magic_number_test.cpp + handle_unknown_opcodes_test.cpp hex_float_test.cpp hex_to_text_test.cpp immediate_int_test.cpp libspirv_macros_test.cpp named_id_test.cpp name_mapper_test.cpp + op_unknown_test.cpp + opcode_lookup_test.cpp opcode_make_test.cpp opcode_require_capabilities_test.cpp opcode_split_test.cpp - opcode_table_get_test.cpp operand_capabilities_test.cpp + operand_lookup_test.cpp operand_test.cpp operand_pattern_test.cpp parse_number_test.cpp
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index 1b8d72e..9351e77 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp
@@ -48,6 +48,24 @@ using ::testing::InSequence; using ::testing::Return; +using MaybeFlipWordsTest = spvtest::TextToBinaryTest; + +TEST_F(MaybeFlipWordsTest, DoNotFlip) { + std::vector<uint32_t> words{0x01234567, 0x89abcdef}; + MaybeFlipWords(false, words.begin(), words.end()); + EXPECT_EQ(words.size(), size_t(2)); + EXPECT_EQ(words[0], 0x01234567); + EXPECT_EQ(words[1], 0x89abcdef); +} + +TEST_F(MaybeFlipWordsTest, Flip) { + std::vector<uint32_t> words{0x01234567, 0x89abcdef}; + MaybeFlipWords(true, words.begin(), words.end()); + EXPECT_EQ(words.size(), size_t(2)); + EXPECT_EQ(words[0], 0x67452301); + EXPECT_EQ(words[1], 0xefcdab89); +} + // An easily-constructible and comparable object for the contents of an // spv_parsed_instruction_t. Unlike spv_parsed_instruction_t, owns the memory // of its components.
diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp index ecf4d1e..dde73e2 100644 --- a/test/binary_to_text_test.cpp +++ b/test/binary_to_text_test.cpp
@@ -428,6 +428,33 @@ "OpDecorate %1 HostAccessINTEL ReadWriteINTEL \"readwrite\"\n", }))); +// clang-format off +INSTANTIATE_TEST_SUITE_P( + MatrixMultiplyAccumulateOperands, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0), + ::testing::ValuesIn(std::vector<std::string>{ + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 None\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixASignedComponentsINTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBSignedComponentsINTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixCBFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixResultBFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedInt8INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedInt8INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedInt4INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedInt4INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixATF32INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBTF32INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixCBFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedBFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedBFloat16INTEL\n", + "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 " + "MatrixASignedComponentsINTEL|MatrixBSignedComponentsINTEL|MatrixAPackedInt8INTEL|MatrixBPackedInt8INTEL\n", + }))); +// clang-format on + using MaskSorting = TextToBinaryTest; TEST_F(MaskSorting, MasksAreSortedFromLSBToMSB) {
diff --git a/test/diff/diff_files/OpExtInst_in_variables_autogen.cpp b/test/diff/diff_files/OpExtInst_in_variables_autogen.cpp new file mode 100644 index 0000000..bc510d0 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_variables_autogen.cpp
@@ -0,0 +1,292 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../diff_test_utils.h" +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests a diff where the shader has OpExtInst in the debug, decorations and +// types/variables sections. +constexpr char kSrc[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %20 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %21 = OpExtInst %2 %20 123 + %22 = OpExtInst %2 %20 234 + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + %23 = OpExtInst %2 %20 345 + %24 = OpExtInst %2 %20 456 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %25 = OpExtInst %2 %20 567 + %26 = OpExtInst %2 %20 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %30 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %31 = OpExtInst %2 %30 123 + %32 = OpExtInst %2 %30 2340 + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + %33 = OpExtInst %2 %30 3450 + %34 = OpExtInst %2 %30 456 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %35 = OpExtInst %2 %30 567 + %36 = OpExtInst %2 %30 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, OpextinstInVariables) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 29 + ; Schema: 0 + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %20 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %21 = OpExtInst %2 %20 123 +-%22 = OpExtInst %2 %20 234 +-%23 = OpExtInst %2 %20 345 ++%27 = OpExtInst %2 %20 2340 ++%28 = OpExtInst %2 %20 3450 + %24 = OpExtInst %2 %20 456 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %25 = OpExtInst %2 %20 567 + %26 = OpExtInst %2 %20 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OpextinstInVariablesNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %20 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %21 = OpExtInst %2 %20 123 + %22 = OpExtInst %2 %20 234 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + %23 = OpExtInst %2 %20 345 + %24 = OpExtInst %2 %20 456 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %25 = OpExtInst %2 %20 567 + %26 = OpExtInst %2 %20 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %30 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %31 = OpExtInst %2 %30 123 + %32 = OpExtInst %2 %30 2340 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + %33 = OpExtInst %2 %30 3450 + %34 = OpExtInst %2 %30 456 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %35 = OpExtInst %2 %30 567 + %36 = OpExtInst %2 %30 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 29 + ; Schema: 0 + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %20 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %21 = OpExtInst %2 %20 123 +-%22 = OpExtInst %2 %20 234 +-%23 = OpExtInst %2 %20 345 ++%27 = OpExtInst %2 %20 2340 ++%28 = OpExtInst %2 %20 3450 + %24 = OpExtInst %2 %20 456 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %25 = OpExtInst %2 %20 567 + %26 = OpExtInst %2 %20 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools
diff --git a/test/diff/diff_files/OpExtInst_in_variables_dst.spvasm b/test/diff/diff_files/OpExtInst_in_variables_dst.spvasm new file mode 100644 index 0000000..59ba05b --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_variables_dst.spvasm
@@ -0,0 +1,40 @@ + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %30 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %31 = OpExtInst %2 %30 123 + %32 = OpExtInst %2 %30 2340 + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + %33 = OpExtInst %2 %30 3450 + %34 = OpExtInst %2 %30 456 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %35 = OpExtInst %2 %30 567 + %36 = OpExtInst %2 %30 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd
diff --git a/test/diff/diff_files/OpExtInst_in_variables_src.spvasm b/test/diff/diff_files/OpExtInst_in_variables_src.spvasm new file mode 100644 index 0000000..ca78909 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_variables_src.spvasm
@@ -0,0 +1,41 @@ +;; Tests a diff where the shader has OpExtInst in the debug, decorations and types/variables sections. + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %20 = OpExtInstImport "NonSemantic.MadeUp" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %21 = OpExtInst %2 %20 123 + %22 = OpExtInst %2 %20 234 + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + %23 = OpExtInst %2 %20 345 + %24 = OpExtInst %2 %20 456 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %25 = OpExtInst %2 %20 567 + %26 = OpExtInst %2 %20 678 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake index 51cb62f..b916cd7 100644 --- a/test/diff/diff_files/diff_test_files_autogen.cmake +++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -18,6 +18,7 @@ list(APPEND DIFF_TEST_FILES "diff_files/OpExtInst_in_dst_only_autogen.cpp" "diff_files/OpExtInst_in_src_only_autogen.cpp" +"diff_files/OpExtInst_in_variables_autogen.cpp" "diff_files/OpTypeForwardPointer_basic_autogen.cpp" "diff_files/OpTypeForwardPointer_intertwined_autogen.cpp" "diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp" @@ -30,6 +31,8 @@ "diff_files/different_decorations_vertex_autogen.cpp" "diff_files/different_function_parameter_count_autogen.cpp" "diff_files/extra_if_block_autogen.cpp" +"diff_files/function_group_by_full_type_autogen.cpp" +"diff_files/function_group_by_mapped_id_autogen.cpp" "diff_files/index_signedness_autogen.cpp" "diff_files/int_vs_uint_constants_autogen.cpp" "diff_files/large_functions_large_diffs_autogen.cpp" @@ -44,5 +47,6 @@ "diff_files/spec_constant_composite_autogen.cpp" "diff_files/spec_constant_op_autogen.cpp" "diff_files/spec_constant_specid_autogen.cpp" +"diff_files/string_in_ext_inst_autogen.cpp" "diff_files/unrelated_shaders_autogen.cpp" )
diff --git a/test/diff/diff_files/function_group_by_full_type_autogen.cpp b/test/diff/diff_files/function_group_by_full_type_autogen.cpp new file mode 100644 index 0000000..1843425 --- /dev/null +++ b/test/diff/diff_files/function_group_by_full_type_autogen.cpp
@@ -0,0 +1,194 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../diff_test_utils.h" +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Use functions' `OpFunctionType` to group before grouping by return type only. +constexpr char kSrc[] = R"( OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstantNull %int + + %fn_int = OpTypeFunction %bool %int + %fn_int_int = OpTypeFunction %bool %int %int + + %f = OpFunction %bool None %fn_int + %fp1 = OpFunctionParameter %int + %f_prologue = OpLabel + %fr = OpIEqual %bool %fp1 %int_0 + OpReturnValue %fr + OpFunctionEnd + + %g = OpFunction %bool None %fn_int_int + %gp1 = OpFunctionParameter %int + %gp2 = OpFunctionParameter %int + %g_prologue = OpLabel + %gr = OpIEqual %bool %gp1 %int_0 + OpReturnValue %gr + OpFunctionEnd)"; +constexpr char kDst[] = + R"(;; Use functions' `OpFunctionType` to group before grouping by return type only. + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstantNull %int + + %fn_int = OpTypeFunction %bool %int + %fn_int_int = OpTypeFunction %bool %int %int + + %g = OpFunction %bool None %fn_int_int + %gp1 = OpFunctionParameter %int + %gp2 = OpFunctionParameter %int + %g_prologue = OpLabel + %gr = OpIEqual %bool %gp1 %int_0 + OpReturnValue %gr + OpFunctionEnd + + %f = OpFunction %bool None %fn_int + %fp1 = OpFunctionParameter %int + %f_prologue = OpLabel + %fr = OpIEqual %bool %fp1 %int_0 + OpReturnValue %fr + OpFunctionEnd +)"; + +TEST(DiffTest, FunctionGroupByFullType) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 15 + ; Schema: 0 + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %1 = OpTypeBool + %2 = OpTypeInt 32 1 + %3 = OpConstantNull %2 + %4 = OpTypeFunction %1 %2 + %5 = OpTypeFunction %1 %2 %2 + %6 = OpFunction %1 None %4 + %7 = OpFunctionParameter %2 + %8 = OpLabel + %9 = OpIEqual %1 %7 %3 + OpReturnValue %9 + OpFunctionEnd + %10 = OpFunction %1 None %5 + %11 = OpFunctionParameter %2 + %12 = OpFunctionParameter %2 + %13 = OpLabel + %14 = OpIEqual %1 %11 %3 + OpReturnValue %14 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, FunctionGroupByFullTypeNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstantNull %int + + %fn_int = OpTypeFunction %bool %int + %fn_int_int = OpTypeFunction %bool %int %int + + %f = OpFunction %bool None %fn_int + %fp1 = OpFunctionParameter %int + %f_prologue = OpLabel + %fr = OpIEqual %bool %fp1 %int_0 + OpReturnValue %fr + OpFunctionEnd + + %g = OpFunction %bool None %fn_int_int + %gp1 = OpFunctionParameter %int + %gp2 = OpFunctionParameter %int + %g_prologue = OpLabel + %gr = OpIEqual %bool %gp1 %int_0 + OpReturnValue %gr + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstantNull %int + + %fn_int = OpTypeFunction %bool %int + %fn_int_int = OpTypeFunction %bool %int %int + + %g = OpFunction %bool None %fn_int_int + %gp1 = OpFunctionParameter %int + %gp2 = OpFunctionParameter %int + %g_prologue = OpLabel + %gr = OpIEqual %bool %gp1 %int_0 + OpReturnValue %gr + OpFunctionEnd + + %f = OpFunction %bool None %fn_int + %fp1 = OpFunctionParameter %int + %f_prologue = OpLabel + %fr = OpIEqual %bool %fp1 %int_0 + OpReturnValue %fr + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 15 + ; Schema: 0 + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %1 = OpTypeBool + %2 = OpTypeInt 32 1 + %3 = OpConstantNull %2 + %4 = OpTypeFunction %1 %2 + %5 = OpTypeFunction %1 %2 %2 + %6 = OpFunction %1 None %4 + %7 = OpFunctionParameter %2 + %8 = OpLabel + %9 = OpIEqual %1 %7 %3 + OpReturnValue %9 + OpFunctionEnd + %10 = OpFunction %1 None %5 + %11 = OpFunctionParameter %2 + %12 = OpFunctionParameter %2 + %13 = OpLabel + %14 = OpIEqual %1 %11 %3 + OpReturnValue %14 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools
diff --git a/test/diff/diff_files/function_group_by_full_type_dst.spvasm b/test/diff/diff_files/function_group_by_full_type_dst.spvasm new file mode 100644 index 0000000..eea5276 --- /dev/null +++ b/test/diff/diff_files/function_group_by_full_type_dst.spvasm
@@ -0,0 +1,25 @@ +;; Use functions' `OpFunctionType` to group before grouping by return type only. + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstantNull %int + + %fn_int = OpTypeFunction %bool %int + %fn_int_int = OpTypeFunction %bool %int %int + + %g = OpFunction %bool None %fn_int_int + %gp1 = OpFunctionParameter %int + %gp2 = OpFunctionParameter %int + %g_prologue = OpLabel + %gr = OpIEqual %bool %gp1 %int_0 + OpReturnValue %gr + OpFunctionEnd + + %f = OpFunction %bool None %fn_int + %fp1 = OpFunctionParameter %int + %f_prologue = OpLabel + %fr = OpIEqual %bool %fp1 %int_0 + OpReturnValue %fr + OpFunctionEnd
diff --git a/test/diff/diff_files/function_group_by_full_type_src.spvasm b/test/diff/diff_files/function_group_by_full_type_src.spvasm new file mode 100644 index 0000000..5cc7dd5 --- /dev/null +++ b/test/diff/diff_files/function_group_by_full_type_src.spvasm
@@ -0,0 +1,25 @@ +;; Use functions' `OpFunctionType` to group before grouping by return type only. + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstantNull %int + + %fn_int = OpTypeFunction %bool %int + %fn_int_int = OpTypeFunction %bool %int %int + + %f = OpFunction %bool None %fn_int + %fp1 = OpFunctionParameter %int + %f_prologue = OpLabel + %fr = OpIEqual %bool %fp1 %int_0 + OpReturnValue %fr + OpFunctionEnd + + %g = OpFunction %bool None %fn_int_int + %gp1 = OpFunctionParameter %int + %gp2 = OpFunctionParameter %int + %g_prologue = OpLabel + %gr = OpIEqual %bool %gp1 %int_0 + OpReturnValue %gr + OpFunctionEnd
diff --git a/test/diff/diff_files/function_group_by_mapped_id_autogen.cpp b/test/diff/diff_files/function_group_by_mapped_id_autogen.cpp new file mode 100644 index 0000000..66e7bd9 --- /dev/null +++ b/test/diff/diff_files/function_group_by_mapped_id_autogen.cpp
@@ -0,0 +1,181 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../diff_test_utils.h" +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Don't forget to map between source and destination ids when grouping +// functions by return type. +constexpr char kSrc[] = R"( OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %1 = OpTypeInt 32 0 + %2 = OpTypeInt 32 1 + + %fn_uint = OpTypeFunction %1 + %fn_int = OpTypeFunction %2 + + %uint_42 = OpConstant %1 42 + %int_1729 = OpConstant %2 1729 + + %f = OpFunction %1 None %fn_uint + %f_prologue = OpLabel + OpReturnValue %uint_42 + OpFunctionEnd + + %g = OpFunction %2 None %fn_int + %g_prologue = OpLabel + OpReturnValue %int_1729 + OpFunctionEnd)"; +constexpr char kDst[] = + R"(;; Don't forget to map between source and destination ids when grouping functions by return type. + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %2 = OpTypeInt 32 0 + %1 = OpTypeInt 32 1 + + %fn_uint = OpTypeFunction %2 + %fn_int = OpTypeFunction %1 + + %uint_42 = OpConstant %2 42 + %int_1729 = OpConstant %1 1729 + + %f = OpFunction %2 None %fn_uint + %f_prologue = OpLabel + OpReturnValue %uint_42 + OpFunctionEnd + + %g = OpFunction %1 None %fn_int + %g_prologue = OpLabel + OpReturnValue %int_1729 + OpFunctionEnd +)"; + +TEST(DiffTest, FunctionGroupByMappedId) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 12 + ; Schema: 0 + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %3 = OpTypeVoid + %1 = OpTypeInt 32 0 + %2 = OpTypeInt 32 1 + %4 = OpTypeFunction %1 + %5 = OpTypeFunction %2 + %6 = OpConstant %1 42 + %7 = OpConstant %2 1729 + %8 = OpFunction %1 None %4 + %9 = OpLabel + OpReturnValue %6 + OpFunctionEnd + %10 = OpFunction %2 None %5 + %11 = OpLabel + OpReturnValue %7 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, FunctionGroupByMappedIdNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %1 = OpTypeInt 32 0 + %2 = OpTypeInt 32 1 + + %fn_uint = OpTypeFunction %1 + %fn_int = OpTypeFunction %2 + + %uint_42 = OpConstant %1 42 + %int_1729 = OpConstant %2 1729 + + %f = OpFunction %1 None %fn_uint + %f_prologue = OpLabel + OpReturnValue %uint_42 + OpFunctionEnd + + %g = OpFunction %2 None %fn_int + %g_prologue = OpLabel + OpReturnValue %int_1729 + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %2 = OpTypeInt 32 0 + %1 = OpTypeInt 32 1 + + %fn_uint = OpTypeFunction %2 + %fn_int = OpTypeFunction %1 + + %uint_42 = OpConstant %2 42 + %int_1729 = OpConstant %1 1729 + + %f = OpFunction %2 None %fn_uint + %f_prologue = OpLabel + OpReturnValue %uint_42 + OpFunctionEnd + + %g = OpFunction %1 None %fn_int + %g_prologue = OpLabel + OpReturnValue %int_1729 + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 12 + ; Schema: 0 + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %3 = OpTypeVoid + %1 = OpTypeInt 32 0 + %2 = OpTypeInt 32 1 + %4 = OpTypeFunction %1 + %5 = OpTypeFunction %2 + %6 = OpConstant %1 42 + %7 = OpConstant %2 1729 + %8 = OpFunction %1 None %4 + %9 = OpLabel + OpReturnValue %6 + OpFunctionEnd + %10 = OpFunction %2 None %5 + %11 = OpLabel + OpReturnValue %7 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools
diff --git a/test/diff/diff_files/function_group_by_mapped_id_dst.spvasm b/test/diff/diff_files/function_group_by_mapped_id_dst.spvasm new file mode 100644 index 0000000..6af4241 --- /dev/null +++ b/test/diff/diff_files/function_group_by_mapped_id_dst.spvasm
@@ -0,0 +1,23 @@ +;; Don't forget to map between source and destination ids when grouping functions by return type. + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %2 = OpTypeInt 32 0 + %1 = OpTypeInt 32 1 + + %fn_uint = OpTypeFunction %2 + %fn_int = OpTypeFunction %1 + + %uint_42 = OpConstant %2 42 + %int_1729 = OpConstant %1 1729 + + %f = OpFunction %2 None %fn_uint + %f_prologue = OpLabel + OpReturnValue %uint_42 + OpFunctionEnd + + %g = OpFunction %1 None %fn_int + %g_prologue = OpLabel + OpReturnValue %int_1729 + OpFunctionEnd
diff --git a/test/diff/diff_files/function_group_by_mapped_id_src.spvasm b/test/diff/diff_files/function_group_by_mapped_id_src.spvasm new file mode 100644 index 0000000..d920381 --- /dev/null +++ b/test/diff/diff_files/function_group_by_mapped_id_src.spvasm
@@ -0,0 +1,23 @@ +;; Don't forget to map between source and destination ids when grouping functions by return type. + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %1 = OpTypeInt 32 0 + %2 = OpTypeInt 32 1 + + %fn_uint = OpTypeFunction %1 + %fn_int = OpTypeFunction %2 + + %uint_42 = OpConstant %1 42 + %int_1729 = OpConstant %2 1729 + + %f = OpFunction %1 None %fn_uint + %f_prologue = OpLabel + OpReturnValue %uint_42 + OpFunctionEnd + + %g = OpFunction %2 None %fn_int + %g_prologue = OpLabel + OpReturnValue %int_1729 + OpFunctionEnd
diff --git a/test/diff/diff_files/string_in_ext_inst_autogen.cpp b/test/diff/diff_files/string_in_ext_inst_autogen.cpp new file mode 100644 index 0000000..fc752c9 --- /dev/null +++ b/test/diff/diff_files/string_in_ext_inst_autogen.cpp
@@ -0,0 +1,205 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests a diff where the an OpString is used only as parameter of OpExtInst. +constexpr char kSrc[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %10 = OpString "unsigned == %u" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + OpName %main "main" + OpName %foo "foo" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_127 = OpConstant %uint 127 + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_uint Function + OpStore %foo %uint_127 + %11 = OpLoad %uint %foo + %13 = OpExtInst %void %12 1 %10 %11 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %10 = OpString "signed == %d" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + OpName %main "main" + OpName %foo "foo" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_127 = OpConstant %uint 127 + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_uint Function + OpStore %foo %uint_127 + %11 = OpLoad %uint %foo + %13 = OpExtInst %void %12 1 %10 %11 + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, StringInExtInst) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 14 ++; Bound: 15 + ; Schema: 0 + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 +-%10 = OpString "unsigned == %u" ++%14 = OpString "signed == %d" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + OpName %2 "main" + OpName %4 "foo" + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %7 = OpTypeInt 32 0 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 127 + %2 = OpFunction %6 None %3 + %5 = OpLabel + %4 = OpVariable %8 Function + OpStore %4 %9 + %11 = OpLoad %7 %4 +-%13 = OpExtInst %6 %12 1 %10 %11 ++%13 = OpExtInst %6 %12 1 %14 %11 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, StringInExtInstNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %10 = OpString "unsigned == %u" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_127 = OpConstant %uint 127 + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_uint Function + OpStore %foo %uint_127 + %11 = OpLoad %uint %foo + %13 = OpExtInst %void %12 1 %10 %11 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %10 = OpString "signed == %d" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_127 = OpConstant %uint 127 + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_uint Function + OpStore %foo %uint_127 + %11 = OpLoad %uint %foo + %13 = OpExtInst %void %12 1 %10 %11 + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 14 ++; Bound: 15 + ; Schema: 0 + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 +-%10 = OpString "unsigned == %u" ++%14 = OpString "signed == %d" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + %4 = OpTypeVoid + %3 = OpTypeFunction %4 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 127 + %2 = OpFunction %4 None %3 + %5 = OpLabel + %9 = OpVariable %7 Function + OpStore %9 %8 + %11 = OpLoad %6 %9 +-%13 = OpExtInst %4 %12 1 %10 %11 ++%13 = OpExtInst %4 %12 1 %14 %11 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools
diff --git a/test/diff/diff_files/string_in_ext_inst_dst.spvasm b/test/diff/diff_files/string_in_ext_inst_dst.spvasm new file mode 100644 index 0000000..3681332 --- /dev/null +++ b/test/diff/diff_files/string_in_ext_inst_dst.spvasm
@@ -0,0 +1,25 @@ + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %10 = OpString "signed == %d" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + OpName %main "main" + OpName %foo "foo" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_127 = OpConstant %uint 127 + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_uint Function + OpStore %foo %uint_127 + %11 = OpLoad %uint %foo + %13 = OpExtInst %void %12 1 %10 %11 + OpReturn + OpFunctionEnd
diff --git a/test/diff/diff_files/string_in_ext_inst_src.spvasm b/test/diff/diff_files/string_in_ext_inst_src.spvasm new file mode 100644 index 0000000..dd55bce --- /dev/null +++ b/test/diff/diff_files/string_in_ext_inst_src.spvasm
@@ -0,0 +1,26 @@ +;; Tests a diff where the an OpString is used only as parameter of OpExtInst. + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "GLSL.std.450" + %12 = OpExtInstImport "NonSemantic.DebugPrintf" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %10 = OpString "unsigned == %u" + OpSource GLSL 450 + OpSourceExtension "GL_EXT_debug_printf" + OpName %main "main" + OpName %foo "foo" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_127 = OpConstant %uint 127 + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_uint Function + OpStore %foo %uint_127 + %11 = OpLoad %uint %foo + %13 = OpExtInst %void %12 1 %10 %11 + OpReturn + OpFunctionEnd
diff --git a/test/enum_string_mapping_test.cpp b/test/enum_string_mapping_test.cpp index 01dede7..e073c4b 100644 --- a/test/enum_string_mapping_test.cpp +++ b/test/enum_string_mapping_test.cpp
@@ -21,8 +21,7 @@ #include <vector> #include "gtest/gtest.h" -#include "source/enum_string_mapping.h" -#include "source/extensions.h" +#include "source/table2.h" namespace spvtools { namespace { @@ -33,8 +32,6 @@ using ExtensionTest = ::testing::TestWithParam<std::pair<Extension, std::string>>; using UnknownExtensionTest = ::testing::TestWithParam<std::string>; -using CapabilityTest = - ::testing::TestWithParam<std::pair<spv::Capability, std::string>>; TEST_P(ExtensionTest, TestExtensionFromString) { const std::pair<Extension, std::string>& param = GetParam(); @@ -58,14 +55,6 @@ ASSERT_FALSE(GetExtensionFromString(GetParam().c_str(), &result_extension)); } -TEST_P(CapabilityTest, TestCapabilityToString) { - const std::pair<spv::Capability, std::string>& param = GetParam(); - const spv::Capability capability = param.first; - const std::string capability_str = param.second; - const std::string result_str = CapabilityToString(capability); - EXPECT_EQ(capability_str, result_str); -} - INSTANTIATE_TEST_SUITE_P( AllExtensions, ExtensionTest, ValuesIn(std::vector<std::pair<Extension, std::string>>({ @@ -97,107 +86,5 @@ /*alphabetically after all extensions*/ "Z", "SPV_ERROR_random_string_hfsdklhlktherh")); -INSTANTIATE_TEST_SUITE_P( - AllCapabilities, CapabilityTest, - ValuesIn(std::vector<std::pair<spv::Capability, std::string>>( - {{spv::Capability::Matrix, "Matrix"}, - {spv::Capability::Shader, "Shader"}, - {spv::Capability::Geometry, "Geometry"}, - {spv::Capability::Tessellation, "Tessellation"}, - {spv::Capability::Addresses, "Addresses"}, - {spv::Capability::Linkage, "Linkage"}, - {spv::Capability::Kernel, "Kernel"}, - {spv::Capability::Vector16, "Vector16"}, - {spv::Capability::Float16Buffer, "Float16Buffer"}, - {spv::Capability::Float16, "Float16"}, - {spv::Capability::Float64, "Float64"}, - {spv::Capability::Int64, "Int64"}, - {spv::Capability::Int64Atomics, "Int64Atomics"}, - {spv::Capability::ImageBasic, "ImageBasic"}, - {spv::Capability::ImageReadWrite, "ImageReadWrite"}, - {spv::Capability::ImageMipmap, "ImageMipmap"}, - {spv::Capability::Pipes, "Pipes"}, - {spv::Capability::Groups, "Groups"}, - {spv::Capability::DeviceEnqueue, "DeviceEnqueue"}, - {spv::Capability::LiteralSampler, "LiteralSampler"}, - {spv::Capability::AtomicStorage, "AtomicStorage"}, - {spv::Capability::Int16, "Int16"}, - {spv::Capability::TessellationPointSize, "TessellationPointSize"}, - {spv::Capability::GeometryPointSize, "GeometryPointSize"}, - {spv::Capability::ImageGatherExtended, "ImageGatherExtended"}, - {spv::Capability::StorageImageMultisample, "StorageImageMultisample"}, - {spv::Capability::UniformBufferArrayDynamicIndexing, - "UniformBufferArrayDynamicIndexing"}, - {spv::Capability::SampledImageArrayDynamicIndexing, - "SampledImageArrayDynamicIndexing"}, - {spv::Capability::StorageBufferArrayDynamicIndexing, - "StorageBufferArrayDynamicIndexing"}, - {spv::Capability::StorageImageArrayDynamicIndexing, - "StorageImageArrayDynamicIndexing"}, - {spv::Capability::ClipDistance, "ClipDistance"}, - {spv::Capability::CullDistance, "CullDistance"}, - {spv::Capability::ImageCubeArray, "ImageCubeArray"}, - {spv::Capability::SampleRateShading, "SampleRateShading"}, - {spv::Capability::ImageRect, "ImageRect"}, - {spv::Capability::SampledRect, "SampledRect"}, - {spv::Capability::GenericPointer, "GenericPointer"}, - {spv::Capability::Int8, "Int8"}, - {spv::Capability::InputAttachment, "InputAttachment"}, - {spv::Capability::SparseResidency, "SparseResidency"}, - {spv::Capability::MinLod, "MinLod"}, - {spv::Capability::Sampled1D, "Sampled1D"}, - {spv::Capability::Image1D, "Image1D"}, - {spv::Capability::SampledCubeArray, "SampledCubeArray"}, - {spv::Capability::SampledBuffer, "SampledBuffer"}, - {spv::Capability::ImageBuffer, "ImageBuffer"}, - {spv::Capability::ImageMSArray, "ImageMSArray"}, - {spv::Capability::StorageImageExtendedFormats, - "StorageImageExtendedFormats"}, - {spv::Capability::ImageQuery, "ImageQuery"}, - {spv::Capability::DerivativeControl, "DerivativeControl"}, - {spv::Capability::InterpolationFunction, "InterpolationFunction"}, - {spv::Capability::TransformFeedback, "TransformFeedback"}, - {spv::Capability::GeometryStreams, "GeometryStreams"}, - {spv::Capability::StorageImageReadWithoutFormat, - "StorageImageReadWithoutFormat"}, - {spv::Capability::StorageImageWriteWithoutFormat, - "StorageImageWriteWithoutFormat"}, - {spv::Capability::MultiViewport, "MultiViewport"}, - {spv::Capability::SubgroupDispatch, "SubgroupDispatch"}, - {spv::Capability::NamedBarrier, "NamedBarrier"}, - {spv::Capability::PipeStorage, "PipeStorage"}, - {spv::Capability::SubgroupBallotKHR, "SubgroupBallotKHR"}, - {spv::Capability::DrawParameters, "DrawParameters"}, - {spv::Capability::SubgroupVoteKHR, "SubgroupVoteKHR"}, - {spv::Capability::StorageBuffer16BitAccess, - "StorageBuffer16BitAccess"}, - {spv::Capability::StorageUniformBufferBlock16, - "StorageBuffer16BitAccess"}, // Preferred name - {spv::Capability::UniformAndStorageBuffer16BitAccess, - "UniformAndStorageBuffer16BitAccess"}, - {spv::Capability::StorageUniform16, - "UniformAndStorageBuffer16BitAccess"}, // Preferred name - {spv::Capability::StoragePushConstant16, "StoragePushConstant16"}, - {spv::Capability::StorageInputOutput16, "StorageInputOutput16"}, - {spv::Capability::DeviceGroup, "DeviceGroup"}, - {spv::Capability::AtomicFloat32AddEXT, "AtomicFloat32AddEXT"}, - {spv::Capability::AtomicFloat64AddEXT, "AtomicFloat64AddEXT"}, - {spv::Capability::AtomicFloat32MinMaxEXT, "AtomicFloat32MinMaxEXT"}, - {spv::Capability::AtomicFloat64MinMaxEXT, "AtomicFloat64MinMaxEXT"}, - {spv::Capability::MultiView, "MultiView"}, - {spv::Capability::Int64ImageEXT, "Int64ImageEXT"}, - {spv::Capability::SampleMaskOverrideCoverageNV, - "SampleMaskOverrideCoverageNV"}, - {spv::Capability::GeometryShaderPassthroughNV, - "GeometryShaderPassthroughNV"}, - // The next two are different names for the same token. - {spv::Capability::ShaderViewportIndexLayerNV, - "ShaderViewportIndexLayerEXT"}, - {spv::Capability::ShaderViewportIndexLayerEXT, - "ShaderViewportIndexLayerEXT"}, - {spv::Capability::ShaderViewportMaskNV, "ShaderViewportMaskNV"}, - {spv::Capability::ShaderStereoViewNV, "ShaderStereoViewNV"}, - {spv::Capability::PerViewAttributesNV, "PerViewAttributesNV"}}))); - } // namespace } // namespace spvtools
diff --git a/test/ext_inst.arm_motion_engine_test.cpp b/test/ext_inst.arm_motion_engine_test.cpp new file mode 100644 index 0000000..a4fa7be --- /dev/null +++ b/test/ext_inst.arm_motion_engine_test.cpp
@@ -0,0 +1,94 @@ +// Copyright (c) 2024-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "spirv/unified1/ArmMotionEngine.100.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using testing::Eq; +using utils::MakeVector; + +TEST_F(TextToBinaryTest, ArmMotionEngineImportTest) { + const std::string src = "%1 = OpExtInstImport \"Arm.MotionEngine.100\""; + EXPECT_THAT(CompiledInstructions(src), + Eq(MakeInstruction(spv::Op::OpExtInstImport, {1}, + MakeVector("Arm.MotionEngine.100")))); +} + +TEST_F(TextToBinaryTest, ArmMotionEngineInstructionMIN_SAD) { + const std::string src = + "%1 = OpExtInstImport \"Arm.MotionEngine.100\"\n" + "%3 = OpExtInst %2 %1 MIN_SAD %4 %5 %6 %7 %8 %9 %10 %11 %12\n"; + + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(src), + Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1}, + MakeVector("Arm.MotionEngine.100")), + MakeInstruction(spv::Op::OpExtInst, + {2, 3, 1, ArmMotionEngineMIN_SAD, 4, 5, 6, + 7, 8, 9, 10, 11, 12})}))) + << src; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src; +} + +TEST_F(TextToBinaryTest, ArmMotionEngineInstructionMIN_SAD_COST) { + const std::string src = + "%1 = OpExtInstImport \"Arm.MotionEngine.100\"\n" + "%3 = OpExtInst %2 %1 MIN_SAD_COST %4 %5 %6 %7 %8 %9 %10 %11 %12\n"; + + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(src), + Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1}, + MakeVector("Arm.MotionEngine.100")), + MakeInstruction(spv::Op::OpExtInst, + {2, 3, 1, ArmMotionEngineMIN_SAD_COST, 4, + 5, 6, 7, 8, 9, 10, 11, 12})}))) + << src; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src; +} + +TEST_F(TextToBinaryTest, ArmMotionEngineInstructionRAW_SAD) { + const std::string src = + "%1 = OpExtInstImport \"Arm.MotionEngine.100\"\n" + "%3 = OpExtInst %2 %1 RAW_SAD %4 %5 %6 %7 %8 %9 %10 %11\n"; + + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(src), + Eq(Concatenate( + {MakeInstruction(spv::Op::OpExtInstImport, {1}, + MakeVector("Arm.MotionEngine.100")), + MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, ArmMotionEngineRAW_SAD, + 4, 5, 6, 7, 8, 9, 10, 11})}))) + << src; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src; +} + +} // namespace +} // namespace spvtools
diff --git a/test/ext_inst.tosa_test.cpp b/test/ext_inst.tosa_test.cpp new file mode 100644 index 0000000..2a506bd --- /dev/null +++ b/test/ext_inst.tosa_test.cpp
@@ -0,0 +1,57 @@ +// Copyright (c) 2024-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "spirv/unified1/TOSA.001000.1.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using testing::Eq; +using utils::MakeVector; + +TEST_F(TextToBinaryTest, TOSAImportTest) { + const std::string src = "%1 = OpExtInstImport \"TOSA.001000.1\""; + EXPECT_THAT(CompiledInstructions(src), + Eq(MakeInstruction(spv::Op::OpExtInstImport, {1}, + MakeVector("TOSA.001000.1")))); +} + +TEST_F(TextToBinaryTest, TOSAInstructionSmokeTest) { + const std::string src = + "%1 = OpExtInstImport \"TOSA.001000.1\"\n" + "%3 = OpExtInst %2 %1 ARGMAX %4 %5 %6\n"; + + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(src), + Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1}, + MakeVector("TOSA.001000.1")), + MakeInstruction(spv::Op::OpExtInst, + {2, 3, 1, TOSAARGMAX, 4, 5, 6})}))) + << src; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src; +} + +} // namespace +} // namespace spvtools
diff --git a/test/ext_inst_lookup_test.cpp b/test/ext_inst_lookup_test.cpp new file mode 100644 index 0000000..a580c7c --- /dev/null +++ b/test/ext_inst_lookup_test.cpp
@@ -0,0 +1,87 @@ +// Copyright 2025 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 <array> +#include <iostream> + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "source/table2.h" +#include "test/unit_spirv.h" + +using ::testing::ContainerEq; +using ::testing::ValuesIn; + +namespace spvtools { +namespace { + +struct ExtInstLookupCase { + spv_ext_inst_type_t type; + std::string name; + uint32_t value; + bool expect_pass = true; +}; + +std::ostream& operator<<(std::ostream& os, const ExtInstLookupCase& eilc) { + os << "EILC(" << static_cast<int>(eilc.type) << ", '" << eilc.name << "', " + << eilc.value << ", expect pass? " << eilc.expect_pass << ")"; + return os; +} + +using ExtInstLookupTest = ::testing::TestWithParam<ExtInstLookupCase>; + +TEST_P(ExtInstLookupTest, ExtInstLookup_ByName) { + const ExtInstDesc* desc = nullptr; + auto status = LookupExtInst(GetParam().type, GetParam().name.data(), &desc); + if (GetParam().expect_pass) { + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(static_cast<uint32_t>(desc->value), GetParam().value); + } else { + EXPECT_NE(status, SPV_SUCCESS); + EXPECT_EQ(desc, nullptr); + } +} + +TEST_P(ExtInstLookupTest, ExtInstLookup_ByValue_Success) { + const ExtInstDesc* desc = nullptr; + if (GetParam().expect_pass) { + auto status = LookupExtInst(GetParam().type, GetParam().value, &desc); + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(desc->value, GetParam().value); + EXPECT_EQ(std::string(desc->name().data()), GetParam().name); + } +} + +INSTANTIATE_TEST_SUITE_P(Samples, ExtInstLookupTest, + ValuesIn(std::vector<ExtInstLookupCase>{ + {SPV_EXT_INST_TYPE_GLSL_STD_450, "FMix", 46}, + {SPV_EXT_INST_TYPE_OPENCL_STD, "s_mul24", 169}, + {SPV_EXT_INST_TYPE_OPENCL_STD, "mix", 99}, + })); + +TEST(ExtInstLookupSingleTest, ExtInstLookup_Value_Fails) { + // This list may need adjusting over time. + std::array<uint32_t, 3> bad_values = {{99999, 37737, 110101}}; + for (auto bad_value : bad_values) { + const ExtInstDesc* desc = nullptr; + auto status = LookupExtInst(SPV_EXT_INST_TYPE_OPENCL_STD, bad_value, &desc); + EXPECT_NE(status, SPV_SUCCESS); + ASSERT_EQ(desc, nullptr); + } +} + +} // namespace +} // namespace spvtools
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 81687ac..ac94fbf 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -569,6 +569,7 @@ const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); const auto recipient_context = BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); @@ -2002,6 +2003,7 @@ const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); const auto recipient_context = BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); @@ -2235,6 +2237,7 @@ const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); const auto recipient_context = BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp index fa19aa5..4d79d03 100644 --- a/test/fuzz/transformation_access_chain_test.cpp +++ b/test/fuzz/transformation_access_chain_test.cpp
@@ -105,6 +105,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer));
diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp index 29a936c..da82fed 100644 --- a/test/fuzz/transformation_add_copy_memory_test.cpp +++ b/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -142,6 +142,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp index 385590e..7936cad 100644 --- a/test/fuzz/transformation_add_synonym_test.cpp +++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -71,6 +71,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( @@ -208,6 +209,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( @@ -1271,6 +1273,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp index 6fde49a..e3f6d0c 100644 --- a/test/fuzz/transformation_load_test.cpp +++ b/test/fuzz/transformation_load_test.cpp
@@ -86,6 +86,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_mutate_pointer_test.cpp b/test/fuzz/transformation_mutate_pointer_test.cpp index e0cb615..92d1ab9 100644 --- a/test/fuzz/transformation_mutate_pointer_test.cpp +++ b/test/fuzz/transformation_mutate_pointer_test.cpp
@@ -79,6 +79,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_propagate_instruction_down_test.cpp b/test/fuzz/transformation_propagate_instruction_down_test.cpp index 8bedef2..f9db30d 100644 --- a/test/fuzz/transformation_propagate_instruction_down_test.cpp +++ b/test/fuzz/transformation_propagate_instruction_down_test.cpp
@@ -679,6 +679,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_propagate_instruction_up_test.cpp b/test/fuzz/transformation_propagate_instruction_up_test.cpp index 20cf4b3..ac0c14a 100644 --- a/test/fuzz/transformation_propagate_instruction_up_test.cpp +++ b/test/fuzz/transformation_propagate_instruction_up_test.cpp
@@ -690,6 +690,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( @@ -770,6 +771,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp index 0d4850d..a4641ba 100644 --- a/test/fuzz/transformation_push_id_through_variable_test.cpp +++ b/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -99,6 +99,7 @@ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); // Tests the reference shader validity. @@ -339,6 +340,7 @@ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); uint32_t value_id = 80; @@ -606,6 +608,7 @@ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); // Tests the reference shader validity. @@ -707,6 +710,7 @@ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); // Tests the reference shader validity. @@ -772,6 +776,7 @@ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); // Tests the reference shader validity.
diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp index 44901f9..d08b98c 100644 --- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp +++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -333,10 +333,12 @@ %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeStruct %6 %6 %6 + %1001 = OpTypeStruct %6 %6 %6 %8 = OpTypeInt 32 0 %9 = OpConstant %8 12 %10 = OpTypeArray %7 %9 - %11 = OpTypePointer Private %10 + %1002 = OpTypeArray %1001 %9 + %11 = OpTypePointer Private %1002 %12 = OpVariable %11 Private %15 = OpTypeStruct %10 %7 %16 = OpTypePointer Uniform %15 @@ -344,26 +346,32 @@ %18 = OpTypeInt 32 1 %19 = OpConstant %18 0 %20 = OpTypePointer Uniform %10 - %24 = OpTypePointer Private %7 + %24 = OpTypePointer Private %1001 %27 = OpTypePointer Private %6 %30 = OpConstant %18 1 - %132 = OpTypePointer Function %10 + %132 = OpTypePointer Function %1002 %135 = OpTypePointer Uniform %7 - %145 = OpTypePointer Function %7 + %145 = OpTypePointer Function %1001 %4 = OpFunction %2 None %3 %5 = OpLabel %133 = OpVariable %132 Function %21 = OpAccessChain %20 %17 %19 - OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16 + %1003 = OpLoad %10 %21 Nontemporal|Aligned 16 + %1004 = OpCopyLogical %1002 %1003 + OpStore %12 %1004 Aligned 16 OpCopyMemory %133 %12 Volatile OpCopyMemory %133 %12 OpCopyMemory %133 %12 %136 = OpAccessChain %135 %17 %30 %138 = OpAccessChain %24 %12 %19 - OpCopyMemory %138 %136 None Aligned 16 - OpCopyMemory %138 %136 Aligned 16 + %1005 = OpLoad %7 %136 Aligned 16 + %1006 = OpCopyLogical %1001 %1005 + OpStore %138 %1006 + %1007 = OpLoad %7 %136 + %1008 = OpCopyLogical %1001 %1007 + OpStore %138 %1008 Aligned 16 %146 = OpAccessChain %145 %133 %30 - %147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16 + %147 = OpLoad %1001 %146 Volatile|Nontemporal|Aligned 16 %148 = OpAccessChain %24 %12 %19 OpStore %148 %147 Nontemporal OpReturn @@ -382,14 +390,14 @@ MakeUnique<FactManager>(context.get()), validator_options); { TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0), + MakeInstructionDescriptor(21, spv::Op::OpLoad, 0), (uint32_t)spv::MemoryAccessMask::Aligned | (uint32_t)spv::MemoryAccessMask::Volatile, - 1); + 0); // Bad: cannot remove aligned ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0), - (uint32_t)spv::MemoryAccessMask::Volatile, 1) + MakeInstructionDescriptor(21, spv::Op::OpLoad, 0), + (uint32_t)spv::MemoryAccessMask::Volatile, 0) .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); @@ -399,13 +407,13 @@ { TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1), + MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0), (uint32_t)spv::MemoryAccessMask::Nontemporal | (uint32_t)spv::MemoryAccessMask::Volatile, 1); // Bad: cannot remove volatile ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1), + MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0), (uint32_t)spv::MemoryAccessMask::Nontemporal, 0) .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( @@ -417,7 +425,7 @@ { // Creates the first operand. TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 2), + MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1), (uint32_t)spv::MemoryAccessMask::Nontemporal | (uint32_t)spv::MemoryAccessMask::Volatile, 0); @@ -430,7 +438,7 @@ { // Creates both operands. TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 3), + MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 2), (uint32_t)spv::MemoryAccessMask::Nontemporal | (uint32_t)spv::MemoryAccessMask::Volatile, 1); @@ -442,13 +450,13 @@ { TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0), + MakeInstructionDescriptor(138, spv::Op::OpLoad, 0), (uint32_t)spv::MemoryAccessMask::Aligned | (uint32_t)spv::MemoryAccessMask::Nontemporal, - 1); + 0); // Bad: the first mask is None, so Aligned cannot be added to it. ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0), + MakeInstructionDescriptor(138, spv::Op::OpStore, 0), (uint32_t)spv::MemoryAccessMask::Aligned | (uint32_t)spv::MemoryAccessMask::Nontemporal, 0) @@ -461,8 +469,8 @@ { TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 1), - (uint32_t)spv::MemoryAccessMask::Volatile, 1); + MakeInstructionDescriptor(138, spv::Op::OpLoad, 1), + (uint32_t)spv::MemoryAccessMask::Volatile, 0); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), @@ -522,10 +530,12 @@ %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeStruct %6 %6 %6 + %1001 = OpTypeStruct %6 %6 %6 %8 = OpTypeInt 32 0 %9 = OpConstant %8 12 %10 = OpTypeArray %7 %9 - %11 = OpTypePointer Private %10 + %1002 = OpTypeArray %1001 %9 + %11 = OpTypePointer Private %1002 %12 = OpVariable %11 Private %15 = OpTypeStruct %10 %7 %16 = OpTypePointer Uniform %15 @@ -533,26 +543,32 @@ %18 = OpTypeInt 32 1 %19 = OpConstant %18 0 %20 = OpTypePointer Uniform %10 - %24 = OpTypePointer Private %7 + %24 = OpTypePointer Private %1001 %27 = OpTypePointer Private %6 %30 = OpConstant %18 1 - %132 = OpTypePointer Function %10 + %132 = OpTypePointer Function %1002 %135 = OpTypePointer Uniform %7 - %145 = OpTypePointer Function %7 + %145 = OpTypePointer Function %1001 %4 = OpFunction %2 None %3 %5 = OpLabel %133 = OpVariable %132 Function %21 = OpAccessChain %20 %17 %19 - OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16 + %1003 = OpLoad %10 %21 Aligned|Volatile 16 + %1004 = OpCopyLogical %1002 %1003 + OpStore %12 %1004 Aligned 16 OpCopyMemory %133 %12 Volatile Nontemporal|Volatile OpCopyMemory %133 %12 Nontemporal|Volatile OpCopyMemory %133 %12 None Nontemporal|Volatile %136 = OpAccessChain %135 %17 %30 %138 = OpAccessChain %24 %12 %19 - OpCopyMemory %138 %136 None Aligned|Nontemporal 16 - OpCopyMemory %138 %136 Aligned 16 Volatile + %1005 = OpLoad %7 %136 Aligned|Nontemporal 16 + %1006 = OpCopyLogical %1001 %1005 + OpStore %138 %1006 + %1007 = OpLoad %7 %136 Volatile + %1008 = OpCopyLogical %1001 %1007 + OpStore %138 %1008 Aligned 16 %146 = OpAccessChain %145 %133 %30 - %147 = OpLoad %7 %146 Volatile|Aligned 16 + %147 = OpLoad %1001 %146 Volatile|Aligned 16 %148 = OpAccessChain %24 %12 %19 OpStore %148 %147 None OpReturn
diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp index fe24d74..6779ee2 100644 --- a/test/fuzz/transformation_store_test.cpp +++ b/test/fuzz/transformation_store_test.cpp
@@ -95,6 +95,7 @@ const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; + validator_options.SetRelaxLogicalPointer(true); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context(
diff --git a/test/handle_unknown_opcodes_test.cpp b/test/handle_unknown_opcodes_test.cpp new file mode 100644 index 0000000..eaf00ff --- /dev/null +++ b/test/handle_unknown_opcodes_test.cpp
@@ -0,0 +1,511 @@ +// Copyright (c) 2026 NVIDIA Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES. Verifies that +// the binary parser, disassembler, and FriendlyNameMapper correctly handle +// SPIR-V binaries that contain unknown opcodes, unknown extended instruction +// numbers, or known opcodes with unknown enum operands. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/binary.h" +#include "source/disassemble.h" +#include "source/name_mapper.h" +#include "spirv-tools/libspirv.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +// Opcode 0xFFFE is not assigned in any known SPIR-V grammar version. +constexpr uint32_t kUnknownOpcode = 0xFFFEu; + +// Returns a SPIR-V module header for a module with the given ID bound. +std::vector<uint32_t> MakeHeader(uint32_t id_bound) { + return {spv::MagicNumber, 0x00010000u, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), id_bound, 0u}; +} + +// Returns the packed first word of a SPIR-V instruction. +uint32_t MakeFirstWord(uint32_t word_count, uint32_t opcode) { + return (word_count << 16) | (opcode & 0xFFFFu); +} + +class HandleUnknownOpcodesTest : public ::testing::Test { + protected: + void SetUp() override { + context_ = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + ASSERT_NE(nullptr, context_); + } + + void TearDown() override { spvContextDestroy(context_); } + + spv_context context_ = nullptr; +}; + +// Binary parser: unknown opcode without the flag -> error. +TEST_F(HandleUnknownOpcodesTest, ParseUnknownOpcodeWithoutFlagFails) { + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(1, kUnknownOpcode)}, + }); + spv_diagnostic diag = nullptr; + const spv_result_t result = + spvBinaryParseWithOptions(context_, nullptr, binary.data(), binary.size(), + nullptr, nullptr, &diag, 0u); + EXPECT_NE(SPV_SUCCESS, result); + spvDiagnosticDestroy(diag); +} + +// Binary parser: unknown opcode with the flag -> success. +TEST_F(HandleUnknownOpcodesTest, ParseUnknownOpcodeWithFlagSucceeds) { + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(3, kUnknownOpcode), 42u, 99u}, + }); + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryParseWithOptions( + context_, nullptr, binary.data(), binary.size(), nullptr, nullptr, &diag, + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES); + EXPECT_EQ(SPV_SUCCESS, result); + spvDiagnosticDestroy(diag); +} + +// Disassembler: unknown opcode without the flag -> error. +TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownOpcodeWithoutFlagFails) { + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(3, kUnknownOpcode), 42u, 99u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = + spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag); + EXPECT_NE(SPV_SUCCESS, result); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: unknown opcode with the flag -> emits OpUnknown. +TEST_F(HandleUnknownOpcodesTest, + DisassembleUnknownOpcodeWithFlagEmitsOpUnknown) { + // 3-word instruction: [opcode+wc, 42, 99] + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(3, kUnknownOpcode), 42u, 99u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryToText( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag); + ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + // kUnknownOpcode=0xFFFE=65534, word_count=3, operands=42 99 + EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 3) 42 99")); + EXPECT_THAT(output, + HasSubstr("; note: ID bound may be incorrect after reassembly")); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: known opcode with the flag -> normal output (flag is a no-op +// for known opcodes). +TEST_F(HandleUnknownOpcodesTest, DisassembleKnownOpcodeWithFlagIsNoOp) { + // OpCapability Shader: opcode=17, word_count=2, operand=1 (Shader) + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(2, 17u), 1u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryToText( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag); + ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + EXPECT_THAT(output, HasSubstr("OpCapability Shader")); + EXPECT_THAT(output, Not(HasSubstr("OpUnknown"))); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: OpExtInst with unknown instruction number in a semantic set, +// without the flag -> error. +TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownExtInstWithoutFlagFails) { + // OpExtInstImport %1 "GLSL.std.450" (6 words): + // opcode=11 word_count=6, result_id=1, "GLSL.std.450\0" (4 words) + // OpExtInst %2 %3 %1 0xFFFF (5 words): + // opcode=12 word_count=5, result_type=2, result_id=3, set_id=1, + // inst_number=0xFFFF + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(4), + {0x0006000Bu, 1u, 0x4C534C47u, 0x6474732Eu, 0x3035342Eu, 0x00000000u}, + {MakeFirstWord(5, 12u), 2u, 3u, 1u, 0xFFFFu}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = + spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag); + EXPECT_NE(SPV_SUCCESS, result); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: OpExtInst with unknown instruction number in a semantic set, +// with the flag -> emits OpUnknown with all instruction words. +TEST_F(HandleUnknownOpcodesTest, + DisassembleUnknownExtInstWithFlagEmitsOpUnknown) { + // See DisassembleUnknownExtInstWithoutFlagFails for binary layout. + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(4), + {0x0006000Bu, 1u, 0x4C534C47u, 0x6474732Eu, 0x3035342Eu, 0x00000000u}, + {MakeFirstWord(5, 12u), 2u, 3u, 1u, 0xFFFFu}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryToText( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag); + ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + // OpExtInst opcode=12, word_count=5; operands: result_type=2, result_id=3, + // set_id=1, inst_number=0xFFFF=65535. + EXPECT_THAT(output, HasSubstr("OpUnknown(12, 5) 2 3 1 65535")); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: OpExtInst with an unknown instruction number in a non-semantic +// extended instruction set disassembles normally (not as OpUnknown) both with +// and without the flag. Non-semantic sets handle unknown instruction numbers +// gracefully regardless of the flag; setting the flag must not change that. +TEST_F(HandleUnknownOpcodesTest, + NonSemanticExtInstUnknownNumberNotEmittedAsOpUnknown) { + // OpExtInstImport %1 "NonSemantic.DebugPrintf" (8 words): + // opcode=11, word_count=8, result_id=1 + // "NonSemantic.DebugPrintf\0" packed into 6 32-bit words: + // "NonS" = 0x536E6F4E, "eman" = 0x6E616D65, "tic." = 0x2E636974, + // "Debu" = 0x75626544, "gPri" = 0x69725067, "ntf\0" = 0x0066746E + // OpExtInst %2 %3 %1 0xFFFF (5 words): + // opcode=12, word_count=5, result_type=2, result_id=3, set_id=1, + // inst_number=0xFFFF (not present in the NonSemantic.DebugPrintf grammar) + const std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(4), + {0x0008000Bu, 1u, 0x536E6F4Eu, 0x6E616D65u, 0x2E636974u, 0x75626544u, + 0x69725067u, 0x0066746Eu}, + {MakeFirstWord(5, 12u), 2u, 3u, 1u, 0xFFFFu}, + }); + + // Without the flag: non-semantic sets already handle unknown instruction + // numbers gracefully; parsing and disassembly succeed. + { + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context_, binary.data(), + binary.size(), 0u, &text, &diag)) + << (diag ? diag->error : "(no diagnostic)"); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); + } + + // With the flag: the non-semantic graceful path is unchanged. The + // instruction must not be emitted as OpUnknown. + { + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + ASSERT_EQ(SPV_SUCCESS, + spvBinaryToText(context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, + &text, &diag)) + << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + EXPECT_THAT(output, Not(HasSubstr("OpUnknown"))); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); + } +} + +// FriendlyNameMapper: when a binary contains an unknown opcode, IDs defined +// after the unknown opcode get friendly names only when the flag is set. +TEST_F(HandleUnknownOpcodesTest, FriendlyNameMapperContinuesPastUnknownOpcode) { + // OpTypeVoid %1 (opcode=19, word_count=2) + // Unknown opcode 0xFFFE (word_count=1) + // OpTypeBool %2 (opcode=20, word_count=2) + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(3), + {MakeFirstWord(2, 19u), 1u}, + {MakeFirstWord(1, kUnknownOpcode)}, + {MakeFirstWord(2, 20u), 2u}, + }); + + // With the flag, parsing continues past the unknown opcode so %2 is named. + FriendlyNameMapper mapper_with_flag( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES); + EXPECT_EQ("void", mapper_with_flag.NameForId(1)); + EXPECT_EQ("bool", mapper_with_flag.NameForId(2)); + + // Without the flag, parsing stops at the unknown opcode so %2 falls back to + // its trivial numeric name. + FriendlyNameMapper mapper_without_flag(context_, binary.data(), + binary.size()); + EXPECT_EQ("void", mapper_without_flag.NameForId(1)); + EXPECT_EQ("2", mapper_without_flag.NameForId(2)); +} + +// Binary parser: unknown opcode that claims more words than remain in the +// binary -> error, even with the flag set. +TEST_F(HandleUnknownOpcodesTest, ParseTruncatedUnknownOpcodeFails) { + // Instruction claims 3 words but only 1 is present. + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(3, kUnknownOpcode)}, + }); + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryParseWithOptions( + context_, nullptr, binary.data(), binary.size(), nullptr, nullptr, &diag, + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES); + EXPECT_NE(SPV_SUCCESS, result); + spvDiagnosticDestroy(diag); +} + +// Disassembler: known opcode with an unknown flat enum operand, without the +// flag -> error. +TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownFlatEnumWithoutFlagFails) { + // OpCapability 0xFFFF: opcode=17, word_count=2, capability=0xFFFF (unknown). + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(2, 17u), 0xFFFFu}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = + spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag); + EXPECT_NE(SPV_SUCCESS, result); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: known opcode with an unknown flat enum operand, with the flag +// -> emits the whole instruction as OpUnknown. +TEST_F(HandleUnknownOpcodesTest, + DisassembleUnknownFlatEnumWithFlagEmitsOpUnknown) { + // OpCapability 0xFFFF: opcode=17, word_count=2, capability=0xFFFF (unknown). + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(1), + {MakeFirstWord(2, 17u), 0xFFFFu}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryToText( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag); + ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + // OpCapability opcode=17, word_count=2; operand=0xFFFF=65535. + EXPECT_THAT(output, HasSubstr("OpUnknown(17, 2) 65535")); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: known opcode with an unknown mask enum operand, without the +// flag -> error. +TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownMaskEnumWithoutFlagFails) { + // OpFunction %1 %2 FunctionControl(0x80000000) %3: + // opcode=54, word_count=5, result_type=1, result_id=2, + // function_control=0x80000000 (unknown bit), function_type=3. + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(4), + {MakeFirstWord(5, 54u), 1u, 2u, 0x80000000u, 3u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = + spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag); + EXPECT_NE(SPV_SUCCESS, result); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: known opcode with an unknown mask enum operand, with the flag +// -> emits the whole instruction as OpUnknown. +TEST_F(HandleUnknownOpcodesTest, + DisassembleUnknownMaskEnumWithFlagEmitsOpUnknown) { + // OpFunction %1 %2 FunctionControl(0x80000000) %3: + // opcode=54, word_count=5, result_type=1, result_id=2, + // function_control=0x80000000 (unknown bit), function_type=3. + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(4), + {MakeFirstWord(5, 54u), 1u, 2u, 0x80000000u, 3u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + const spv_result_t result = spvBinaryToText( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag); + ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + // OpFunction opcode=54, word_count=5; operands: 1, 2, 2147483648, 3. + EXPECT_THAT(output, HasSubstr("OpUnknown(54, 5) 1 2 2147483648 3")); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Round-trip: disassemble with the flag, reassemble, and verify the +// instruction words are preserved byte-for-byte. +TEST_F(HandleUnknownOpcodesTest, RoundTripUnknownOpcode) { + // 3-word instruction: [opcode+wc, 42, 99] + const std::vector<uint32_t> inst_words = {MakeFirstWord(3, kUnknownOpcode), + 42u, 99u}; + std::vector<uint32_t> original = + spvtest::Concatenate({MakeHeader(1), inst_words}); + + // Disassemble to text with the flag. + spv_text text = nullptr; + spv_diagnostic dis_diag = nullptr; + ASSERT_EQ(SPV_SUCCESS, + spvBinaryToText(context_, original.data(), original.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, + &text, &dis_diag)) + << (dis_diag ? dis_diag->error : "(no diagnostic)"); + + // Reassemble the text back to binary. + spv_binary reassembled = nullptr; + spv_diagnostic asm_diag = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context_, text->str, text->length, + &reassembled, &asm_diag)) + << (asm_diag ? asm_diag->error : "(no diagnostic)"); + + // The instruction words start after the 5-word SPIR-V module header and + // must be byte-for-byte identical to the original instruction. + ASSERT_GE(reassembled->wordCount, 5u + inst_words.size()); + for (size_t i = 0; i < inst_words.size(); i++) { + EXPECT_EQ(inst_words[i], reassembled->code[5 + i]) + << "Word mismatch at instruction word " << i; + } + + spvTextDestroy(text); + spvBinaryDestroy(reassembled); + spvDiagnosticDestroy(dis_diag); + spvDiagnosticDestroy(asm_diag); +} + +// FriendlyNameMapper: when a known instruction has an unknown enum operand and +// the flag is set, the instruction is retried as unknown. inst.result_id is +// not decoded (stays 0) so the real result ID falls back to its trivial numeric +// name. IDs defined by preceding and following instructions are still named. +TEST_F(HandleUnknownOpcodesTest, + FriendlyNameMapperUnknownEnumOperandDropsResultName) { + // OpTypeVoid %1 (opcode=19, word_count=2) + // OpCapability 0xFFFF (opcode=17, word_count=2; unknown Capability value) + // OpTypeBool %2 (opcode=20, word_count=2) + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(3), + {MakeFirstWord(2, 19u), 1u}, + {MakeFirstWord(2, 17u), 0xFFFFu}, + {MakeFirstWord(2, 20u), 2u}, + }); + + // With the flag, parsing continues past the unknown-enum instruction. + // %1 and %2 are both named; the OpCapability result (none for that opcode) + // does not affect naming. + FriendlyNameMapper mapper_with_flag( + context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES); + EXPECT_EQ("void", mapper_with_flag.NameForId(1)); + EXPECT_EQ("bool", mapper_with_flag.NameForId(2)); + + // Without the flag, parsing stops at OpCapability 0xFFFF so %2 is unnamed. + FriendlyNameMapper mapper_without_flag(context_, binary.data(), + binary.size()); + EXPECT_EQ("void", mapper_without_flag.NameForId(1)); + EXPECT_EQ("2", mapper_without_flag.NameForId(2)); +} + +// Disassembler: a binary with a valid instruction before and after an unknown +// opcode disassembles all three instructions correctly. +TEST_F(HandleUnknownOpcodesTest, DisassembleContinuesPastUnknownOpcode) { + // OpTypeVoid %1 (opcode=19, word_count=2) + // Unknown opcode 0xFFFE (word_count=1) + // OpTypeBool %2 (opcode=20, word_count=2) + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(3), + {MakeFirstWord(2, 19u), 1u}, + {MakeFirstWord(1, kUnknownOpcode)}, + {MakeFirstWord(2, 20u), 2u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + ASSERT_EQ(SPV_SUCCESS, + spvBinaryToText(context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, + &text, &diag)) + << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + EXPECT_THAT(output, HasSubstr("OpTypeVoid")); + EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 1)")); + EXPECT_THAT(output, HasSubstr("OpTypeBool")); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// Disassembler: FRIENDLY_NAMES and HANDLE_UNKNOWN_OPCODES work together. +// Known instructions use friendly names; the unknown opcode emits OpUnknown. +TEST_F(HandleUnknownOpcodesTest, DisassembleFriendlyNamesWithUnknownOpcode) { + // OpTypeVoid %1 (opcode=19, word_count=2) + // Unknown opcode 0xFFFE (word_count=1) + // OpTypeBool %2 (opcode=20, word_count=2) + std::vector<uint32_t> binary = spvtest::Concatenate({ + MakeHeader(3), + {MakeFirstWord(2, 19u), 1u}, + {MakeFirstWord(1, kUnknownOpcode)}, + {MakeFirstWord(2, 20u), 2u}, + }); + spv_text text = nullptr; + spv_diagnostic diag = nullptr; + ASSERT_EQ(SPV_SUCCESS, + spvBinaryToText(context_, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES, + &text, &diag)) + << (diag ? diag->error : "(no diagnostic)"); + const std::string output(text->str, text->length); + EXPECT_THAT(output, HasSubstr("%void")); + EXPECT_THAT(output, HasSubstr("%bool")); + EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 1)")); + spvTextDestroy(text); + spvDiagnosticDestroy(diag); +} + +// spvInstructionBinaryToText: unknown opcode with the flag emits OpUnknown. +TEST_F(HandleUnknownOpcodesTest, InstructionBinaryToTextUnknownOpcode) { + const std::vector<uint32_t> inst_words = {MakeFirstWord(3, kUnknownOpcode), + 42u, 99u}; + std::vector<uint32_t> binary = + spvtest::Concatenate({MakeHeader(1), inst_words}); + const std::string output = spvInstructionBinaryToText( + SPV_ENV_UNIVERSAL_1_0, inst_words.data(), inst_words.size(), + binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES); + EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 3) 42 99")); +} + +} // namespace +} // namespace spvtools
diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp index a44d9ec..a9722bb 100644 --- a/test/hex_float_test.cpp +++ b/test/hex_float_test.cpp
@@ -30,6 +30,7 @@ namespace utils { namespace { +using spvtools::utils::LeadingSign; using ::testing::Eq; // In this file "encode" means converting a number into a string, @@ -582,6 +583,15 @@ int16_t unbiased_half_exponent(uint16_t f) { return HexFloat<FloatProxy<Float16>>(f).getUnbiasedNormalizedExponent(); } +int16_t unbiased_bfloat16_exponent(uint16_t f) { + return HexFloat<FloatProxy<BFloat16>>(f).getUnbiasedNormalizedExponent(); +} +int8_t unbiased_E4M3_exponent(uint8_t f) { + return HexFloat<FloatProxy<Float8_E4M3>>(f).getUnbiasedNormalizedExponent(); +} +int8_t unbiased_E5M2_exponent(uint8_t f) { + return HexFloat<FloatProxy<Float8_E5M2>>(f).getUnbiasedNormalizedExponent(); +} TEST(HexFloatOperationTest, UnbiasedExponent) { // Float cases @@ -616,6 +626,42 @@ // Smallest representable number EXPECT_EQ(-24, unbiased_half_exponent(0x0001)); + + EXPECT_EQ(0, unbiased_bfloat16_exponent(0x3F80)); + EXPECT_EQ(3, unbiased_bfloat16_exponent(0x4100)); + EXPECT_EQ(-1, unbiased_bfloat16_exponent(0x3F00)); + EXPECT_EQ(-126, unbiased_bfloat16_exponent(0x0080)); + EXPECT_EQ(127, unbiased_bfloat16_exponent(0x7F00)); + EXPECT_EQ(10, unbiased_bfloat16_exponent(0x4480)); + EXPECT_EQ(128, unbiased_bfloat16_exponent(0x7F80)); + // Test case for the smallest representable denormal number. + // Effective exponent is 1 - bias - (leading_one_pos) = 1 - 127 - 7 = -133 + EXPECT_EQ(-133, unbiased_bfloat16_exponent(0x0001)); + + // E4M3 cases + // The exponent is represented in the bits 0x78 + // The offset is -7 + EXPECT_EQ(0, unbiased_E4M3_exponent(0x38)); + EXPECT_EQ(3, unbiased_E4M3_exponent(0x50)); + EXPECT_EQ(-1, unbiased_E4M3_exponent(0x30)); + EXPECT_EQ(-6, unbiased_E4M3_exponent(0x08)); + EXPECT_EQ(8, unbiased_E4M3_exponent(0x78)); + + // Smallest representable number + EXPECT_EQ(-9, unbiased_E4M3_exponent(0x01)); + + // E5M2 cases + // The exponent is represented in the bits 0x7C + // The offset is -15 + EXPECT_EQ(0, unbiased_E5M2_exponent(0x3C)); + EXPECT_EQ(3, unbiased_E5M2_exponent(0x48)); + EXPECT_EQ(-1, unbiased_E5M2_exponent(0x38)); + EXPECT_EQ(-14, unbiased_E5M2_exponent(0x04)); + EXPECT_EQ(16, unbiased_E5M2_exponent(0x7C)); + EXPECT_EQ(10, unbiased_E5M2_exponent(0x64)); + + // Smallest representable number + EXPECT_EQ(-16, unbiased_E5M2_exponent(0x01)); } // Creates a float that is the sum of 1/(2 ^ fractions[i]) for i in factions @@ -839,6 +885,166 @@ {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -131)), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity}, {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -130)), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven}, }))); + +// clang-format on + +// The same as bits_set but for a E4M3 value instead of 32-bit floating +// point. +uint8_t e4m3_bits_set(const std::vector<uint32_t>& bits) { + const uint32_t top_bit = 1u << 2u; + uint32_t val = 0; + for (uint32_t i : bits) { + val |= top_bit >> i; + } + return static_cast<uint8_t>(val); +} + +struct RoundSignificandCaseE4M3 { + float source_float; + std::pair<int8_t, bool> expected_results; + round_direction round; +}; + +using HexFloatRoundTestE4M3 = + ::testing::TestWithParam<RoundSignificandCaseE4M3>; + +TEST_P(HexFloatRoundTestE4M3, RoundDownToFPE4M3) { + using HF = HexFloat<FloatProxy<float>>; + using HFE4M3 = HexFloat<FloatProxy<Float8_E4M3>>; + + HF input_value(GetParam().source_float); + bool carry_bit = false; + EXPECT_EQ(GetParam().expected_results.first, + input_value.getRoundedNormalizedSignificand<HFE4M3>( + GetParam().round, &carry_bit)); + EXPECT_EQ(carry_bit, GetParam().expected_results.second); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(F32ToE4M3, HexFloatRoundTestE4M3, + ::testing::ValuesIn(std::vector<RoundSignificandCaseE4M3>( + { + {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToZero}, + {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToNearestEven}, + {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToPositiveInfinity}, + {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero}, + + {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNearestEven}, + + {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToZero}, + {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 1}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 1}), false), RD::kToNearestEven}, + + {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven}, + + {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero}, + {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToPositiveInfinity}, + {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNegativeInfinity}, + {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven}, + + {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven}, + + // Carries + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({0, 1, 2}), false), RD::kToZero}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({}), true), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({0, 1, 2}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({}), true), RD::kToNearestEven}, + + // Cases where original number was denorm. Note: this should have no effect + // the number is pre-normalized. + {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -128)), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero}, + {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -129)), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity}, + {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -131)), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity}, + {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -130)), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven}, + }))); + +// clang-format on +// The same as bits_set but for a E4M3 value instead of 32-bit floating +// point. +uint8_t e5m2_bits_set(const std::vector<uint32_t>& bits) { + const uint32_t top_bit = 1u << 1u; + uint32_t val = 0; + for (uint32_t i : bits) { + val |= top_bit >> i; + } + return static_cast<uint8_t>(val); +} + +struct RoundSignificandCaseE5M2 { + float source_float; + std::pair<int8_t, bool> expected_results; + round_direction round; +}; + +using HexFloatRoundTestE5M2 = + ::testing::TestWithParam<RoundSignificandCaseE5M2>; + +TEST_P(HexFloatRoundTestE5M2, RoundDownToFPE4M3) { + using HF = HexFloat<FloatProxy<float>>; + using HFE5M2 = HexFloat<FloatProxy<Float8_E5M2>>; + + HF input_value(GetParam().source_float); + bool carry_bit = false; + EXPECT_EQ(GetParam().expected_results.first, + input_value.getRoundedNormalizedSignificand<HFE5M2>( + GetParam().round, &carry_bit)); + EXPECT_EQ(carry_bit, GetParam().expected_results.second); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(F32ToE5M2, HexFloatRoundTestE5M2, + ::testing::ValuesIn(std::vector<RoundSignificandCaseE5M2>( + { + {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToZero}, + {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToNearestEven}, + {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToPositiveInfinity}, + {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1}), std::make_pair(e5m2_bits_set({0}), false), RD::kToZero}, + + {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0, 1}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNearestEven}, + + {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({}), false), RD::kToZero}, + {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({1}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({1}), false), RD::kToNearestEven}, + + {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToZero}, + {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNearestEven}, + + {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToZero}, + {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToPositiveInfinity}, + {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNegativeInfinity}, + {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNearestEven}, + + // Carries + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({0, 1}), false), RD::kToZero}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({}), true), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({0, 1}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({}), true), RD::kToNearestEven}, + + // Cases where original number was denorm. Note: this should have no effect + // the number is pre-normalized. + {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -128)), std::make_pair(e5m2_bits_set({}), false), RD::kToZero}, + {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -129)), std::make_pair(e5m2_bits_set({1}), false), RD::kToPositiveInfinity}, + {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -131)), std::make_pair(e5m2_bits_set({}), false), RD::kToNegativeInfinity}, + {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -130)), std::make_pair(e5m2_bits_set({1}), false), RD::kToNearestEven}, + }))); // clang-format on struct UpCastSignificandCase { @@ -1016,6 +1222,225 @@ // Nans are below because we cannot test for equality. }))); +using HexFloatFP32ToE4M3Tests = ::testing::TestWithParam<DownCastTest>; + +TEST_P(HexFloatFP32ToE4M3Tests, NarrowingCasts) { + using HF = HexFloat<FloatProxy<float>>; + using HFE4M3 = HexFloat<FloatProxy<Float8_E4M3>>; + HF f(GetParam().source_float); + for (auto round : GetParam().directions) { + HFE4M3 e4m3(0); + f.castTo(e4m3, round); + EXPECT_EQ(GetParam().expected_half, e4m3.value().getAsFloat().get_value()) + << get_round_text(round) << " " << std::hex + << BitwiseCast<uint32_t>(GetParam().source_float) + << " cast to: " << (uint32_t)e4m3.value().getAsFloat().get_value(); + } +} + +INSTANTIATE_TEST_SUITE_P( + F32ToE4M3, HexFloatFP32ToE4M3Tests, + ::testing::ValuesIn(std::vector<DownCastTest>({ + // Exactly representable as half. + {0.f, + 0x0, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-0.f, + 0x80, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {1.0f, + 0x38, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-1.0f, + 0xB8, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + {float_fractions({0, 1, 3}), + 0x3D, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-float_fractions({0, 1, 3}), + 0xBD, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(ldexp(float_fractions({0, 1, 3}), 3)), + 0x55, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(float_fractions({0, 1, 3}), 3)), + 0xD5, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Underflow + {static_cast<float>(ldexp(1.0f, -10)), + 0x0, + {RD::kToZero, RD::kToNegativeInfinity, RD::kToNearestEven}}, + {static_cast<float>(ldexp(1.0f, -10)), 0x1, {RD::kToPositiveInfinity}}, + {static_cast<float>(-ldexp(1.0f, -10)), + 0x80, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.0f, -9)), 0x81, {RD::kToNegativeInfinity}}, + {static_cast<float>(ldexp(1.0f, -9)), + 0x1, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Overflow + {static_cast<float>(ldexp(1.0f, 9)), + Float8_E4M3::max().get_value(), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(ldexp(1.0f, 10)), + Float8_E4M3::max().get_value(), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(ldexp(1.3f, 9)), + Float8_E4M3::max().get_value(), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.0f, 9)), + static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.0f, 10)), + static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.3f, 9)), + static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Transfer of Infinities + {std::numeric_limits<float>::infinity(), + Float8_E4M3::max().get_value(), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-std::numeric_limits<float>::infinity(), + static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()), + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Nans are below because we cannot test for equality. + }))); + +using HexFloatFP32ToE5M2Tests = ::testing::TestWithParam<DownCastTest>; + +TEST_P(HexFloatFP32ToE5M2Tests, NarrowingCasts) { + using HF = HexFloat<FloatProxy<float>>; + using HFE5M2 = HexFloat<FloatProxy<Float8_E5M2>>; + HF f(GetParam().source_float); + for (auto round : GetParam().directions) { + HFE5M2 e5m2(0); + f.castTo(e5m2, round); + EXPECT_EQ(GetParam().expected_half, e5m2.value().getAsFloat().get_value()) + << get_round_text(round) << " " << std::hex + << BitwiseCast<uint32_t>(GetParam().source_float) + << " cast to: " << (uint32_t)e5m2.value().getAsFloat().get_value(); + } +} + +const uint8_t e5m2_positive_infinity = 0x7C; +const uint8_t e5m2_negative_infinity = 0xFC; + +INSTANTIATE_TEST_SUITE_P( + F32ToE5M2, HexFloatFP32ToE5M2Tests, + ::testing::ValuesIn(std::vector<DownCastTest>({ + // Exactly representable as half. + {0.f, + 0x0, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-0.f, + 0x80, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {1.0f, + 0x3C, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-1.0f, + 0xBC, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + {float_fractions({0, 1, 2}), + 0x3F, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-float_fractions({0, 1, 2}), + 0xBF, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(ldexp(float_fractions({0, 2}), 3)), + 0x49, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(float_fractions({0, 2}), 3)), + 0xC9, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Underflow + {static_cast<float>(ldexp(1.0f, -17)), + 0x0, + {RD::kToZero, RD::kToNegativeInfinity, RD::kToNearestEven}}, + {static_cast<float>(ldexp(1.0f, -17)), 0x1, {RD::kToPositiveInfinity}}, + {static_cast<float>(-ldexp(1.0f, -17)), + 0x80, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.0f, -16)), + 0x81, + {RD::kToNegativeInfinity}}, + {static_cast<float>(ldexp(1.0f, -16)), + 0x1, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Overflow + {static_cast<float>(ldexp(1.0f, 16)), + e5m2_positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(ldexp(1.0f, 17)), + e5m2_positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(ldexp(1.3f, 16)), + e5m2_positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.0f, 16)), + e5m2_negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.0f, 17)), + e5m2_negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast<float>(-ldexp(1.3f, 16)), + e5m2_negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Transfer of Infinities + {std::numeric_limits<float>::infinity(), + e5m2_positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-std::numeric_limits<float>::infinity(), + e5m2_negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Nans are below because we cannot test for equality. + }))); + struct UpCastCase { uint16_t source_half; float expected_float; @@ -1069,6 +1494,9 @@ TEST(HexFloatOperationTests, NanTests) { using HF = HexFloat<FloatProxy<float>>; using HF16 = HexFloat<FloatProxy<Float16>>; + using BF16 = HexFloat<FloatProxy<BFloat16>>; + using FE4M3 = HexFloat<FloatProxy<Float8_E4M3>>; + using FE5M2 = HexFloat<FloatProxy<Float8_E5M2>>; round_direction rounding[] = {round_direction::kToZero, round_direction::kToNearestEven, round_direction::kToPositiveInfinity, @@ -1078,6 +1506,9 @@ for (round_direction round : rounding) { HF16 f16(0); HF f(0.f); + BF16 bf16(0); + FE4M3 fe4m3(0); + FE5M2 fe5m2(0); HF(std::numeric_limits<float>::quiet_NaN()).castTo(f16, round); EXPECT_TRUE(f16.value().isNan()); HF(std::numeric_limits<float>::signaling_NaN()).castTo(f16, round); @@ -1093,6 +1524,45 @@ EXPECT_TRUE(f.value().isNan()); HF16(0xFF00).castTo(f, round); EXPECT_TRUE(f.value().isNan()); + + BF16(0x7F81).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + BF16(0x7F91).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + BF16(0xFF81).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + BF16(0x7F90).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + BF16(0xFFE0).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + + HF(std::numeric_limits<float>::quiet_NaN()).castTo(fe4m3, round); + EXPECT_TRUE(fe4m3.value().isNan()); + HF(std::numeric_limits<float>::signaling_NaN()).castTo(fe4m3, round); + EXPECT_TRUE(fe4m3.value().isNan()); + + FE4M3(0x7F).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + FE4M3(0xFF).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + + HF(std::numeric_limits<float>::quiet_NaN()).castTo(fe5m2, round); + EXPECT_TRUE(fe5m2.value().isNan()); + HF(std::numeric_limits<float>::signaling_NaN()).castTo(fe5m2, round); + EXPECT_TRUE(fe5m2.value().isNan()); + + FE5M2(0x7D).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + FE5M2(0x7E).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + FE5M2(0x7F).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + FE5M2(0xFD).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + FE5M2(0xFE).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + FE5M2(0xFF).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); } } @@ -1100,73 +1570,96 @@ template <typename T> struct FloatParseCase { std::string literal; - bool negate_value; + LeadingSign leading_sign; bool expect_success; HexFloat<FloatProxy<T>> expected_value; }; +const char* str(const LeadingSign& ls) { + switch (ls) { + case LeadingSign::None: + return "(none)"; + case LeadingSign::Minus: + return "-"; + case LeadingSign::Plus: + return "+"; + } + return ""; // should not happen +} + using ParseNormalFloatTest = ::testing::TestWithParam<FloatParseCase<float>>; TEST_P(ParseNormalFloatTest, Samples) { std::stringstream input(GetParam().literal); HexFloat<FloatProxy<float>> parsed_value(0.0f); - ParseNormalFloat(input, GetParam().negate_value, parsed_value); + ParseNormalFloat(input, GetParam().leading_sign, parsed_value); EXPECT_NE(GetParam().expect_success, input.fail()) << " literal: " << GetParam().literal - << " negate: " << GetParam().negate_value; + << " leading_sign: " << str(GetParam().leading_sign); if (GetParam().expect_success) { EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) << " literal: " << GetParam().literal - << " negate: " << GetParam().negate_value; + << " leading_sign: " << str(GetParam().leading_sign); } } // Returns a FloatParseCase with expected failure. template <typename T> -FloatParseCase<T> BadFloatParseCase(std::string literal, bool negate_value, +FloatParseCase<T> BadFloatParseCase(std::string literal, + LeadingSign leading_sign, T expected_value) { HexFloat<FloatProxy<T>> proxy_expected_value(expected_value); - return FloatParseCase<T>{literal, negate_value, false, proxy_expected_value}; + return FloatParseCase<T>{literal, leading_sign, false, proxy_expected_value}; } // Returns a FloatParseCase that should successfully parse to a given value. template <typename T> -FloatParseCase<T> GoodFloatParseCase(std::string literal, bool negate_value, +FloatParseCase<T> GoodFloatParseCase(std::string literal, + LeadingSign leading_sign, T expected_value) { HexFloat<FloatProxy<T>> proxy_expected_value(expected_value); - return FloatParseCase<T>{literal, negate_value, true, proxy_expected_value}; + return FloatParseCase<T>{literal, leading_sign, true, proxy_expected_value}; } INSTANTIATE_TEST_SUITE_P( FloatParse, ParseNormalFloatTest, ::testing::ValuesIn(std::vector<FloatParseCase<float>>{ // Failing cases due to trivially incorrect syntax. - BadFloatParseCase("abc", false, 0.0f), - BadFloatParseCase("abc", true, 0.0f), + BadFloatParseCase("abc", LeadingSign::None, 0.0f), + BadFloatParseCase("abc", LeadingSign::Minus, 0.0f), + BadFloatParseCase("abc", LeadingSign::Plus, 0.0f), // Valid cases. - GoodFloatParseCase("0", false, 0.0f), - GoodFloatParseCase("0.0", false, 0.0f), - GoodFloatParseCase("-0.0", false, -0.0f), - GoodFloatParseCase("2.0", false, 2.0f), - GoodFloatParseCase("-2.0", false, -2.0f), - GoodFloatParseCase("+2.0", false, 2.0f), - // Cases with negate_value being true. - GoodFloatParseCase("0.0", true, -0.0f), - GoodFloatParseCase("2.0", true, -2.0f), + GoodFloatParseCase("0", LeadingSign::None, 0.0f), + GoodFloatParseCase("0.0", LeadingSign::None, 0.0f), + GoodFloatParseCase("-0.0", LeadingSign::None, -0.0f), + GoodFloatParseCase("2.0", LeadingSign::None, 2.0f), + GoodFloatParseCase("-2.0", LeadingSign::None, -2.0f), + GoodFloatParseCase("+2.0", LeadingSign::None, 2.0f), + // Cases with leading sign + GoodFloatParseCase("0.0", LeadingSign::Minus, -0.0f), + GoodFloatParseCase("2.0", LeadingSign::Minus, -2.0f), + GoodFloatParseCase("0", LeadingSign::Plus, 0.0f), + GoodFloatParseCase("0.0", LeadingSign::Plus, 0.0f), + GoodFloatParseCase("2.0", LeadingSign::Plus, 2.0f), - // When negate_value is true, we should not accept a + // When a leading sign is present, we should not accept a // leading minus or plus. - BadFloatParseCase("-0.0", true, 0.0f), - BadFloatParseCase("-2.0", true, 0.0f), - BadFloatParseCase("+0.0", true, 0.0f), - BadFloatParseCase("+2.0", true, 0.0f), + BadFloatParseCase("-0.0", LeadingSign::Minus, 0.0f), + BadFloatParseCase("-2.0", LeadingSign::Minus, 0.0f), + BadFloatParseCase("+0.0", LeadingSign::Minus, 0.0f), + BadFloatParseCase("+2.0", LeadingSign::Minus, 0.0f), + BadFloatParseCase("-0.0", LeadingSign::Plus, 0.0f), + BadFloatParseCase("-2.0", LeadingSign::Plus, 0.0f), + BadFloatParseCase("+0.0", LeadingSign::Plus, 0.0f), + BadFloatParseCase("+2.0", LeadingSign::Plus, 0.0f), // Overflow is an error for 32-bit float parsing. - BadFloatParseCase("1e40", false, FLT_MAX), - BadFloatParseCase("1e40", true, -FLT_MAX), - BadFloatParseCase("-1e40", false, -FLT_MAX), - // We can't have -1e40 and negate_value == true since + BadFloatParseCase("1e40", LeadingSign::None, FLT_MAX), + BadFloatParseCase("1e40", LeadingSign::Plus, FLT_MAX), + BadFloatParseCase("1e40", LeadingSign::Minus, -FLT_MAX), + BadFloatParseCase("-1e40", LeadingSign::None, -FLT_MAX), + // We can't have -1e40 and leading sign == Minus since // that represents an original case of "--1e40" which // is invalid. })); @@ -1177,14 +1670,14 @@ TEST_P(ParseNormalFloat16Test, Samples) { std::stringstream input(GetParam().literal); HexFloat<FloatProxy<Float16>> parsed_value(0); - ParseNormalFloat(input, GetParam().negate_value, parsed_value); + ParseNormalFloat(input, GetParam().leading_sign, parsed_value); EXPECT_NE(GetParam().expect_success, input.fail()) << " literal: " << GetParam().literal - << " negate: " << GetParam().negate_value; + << " leading_sign: " << str(GetParam().leading_sign); if (GetParam().expect_success) { EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) << " literal: " << GetParam().literal - << " negate: " << GetParam().negate_value; + << " leading_sign: " << str(GetParam().leading_sign); } } @@ -1192,26 +1685,158 @@ Float16Parse, ParseNormalFloat16Test, ::testing::ValuesIn(std::vector<FloatParseCase<Float16>>{ // Failing cases due to trivially incorrect syntax. - BadFloatParseCase<Float16>("abc", false, uint16_t{0}), - BadFloatParseCase<Float16>("abc", true, uint16_t{0}), + BadFloatParseCase<Float16>("abc", LeadingSign::None, uint16_t{0}), + BadFloatParseCase<Float16>("abc", LeadingSign::Minus, uint16_t{0}), + BadFloatParseCase<Float16>("abc", LeadingSign::Plus, uint16_t{0}), // Valid cases. - GoodFloatParseCase<Float16>("0", false, uint16_t{0}), - GoodFloatParseCase<Float16>("0.0", false, uint16_t{0}), - GoodFloatParseCase<Float16>("-0.0", false, uint16_t{0x8000}), - GoodFloatParseCase<Float16>("2.0", false, uint16_t{0x4000}), - GoodFloatParseCase<Float16>("-2.0", false, uint16_t{0xc000}), - GoodFloatParseCase<Float16>("+2.0", false, uint16_t{0x4000}), - // Cases with negate_value being true. - GoodFloatParseCase<Float16>("0.0", true, uint16_t{0x8000}), - GoodFloatParseCase<Float16>("2.0", true, uint16_t{0xc000}), + GoodFloatParseCase<Float16>("0", LeadingSign::None, uint16_t{0}), + GoodFloatParseCase<Float16>("0.0", LeadingSign::None, uint16_t{0}), + GoodFloatParseCase<Float16>("-0.0", LeadingSign::None, + uint16_t{0x8000}), + GoodFloatParseCase<Float16>("2.0", LeadingSign::None, uint16_t{0x4000}), + GoodFloatParseCase<Float16>("-2.0", LeadingSign::None, + uint16_t{0xc000}), + GoodFloatParseCase<Float16>("+2.0", LeadingSign::None, + uint16_t{0x4000}), + // Cases with leading sign + GoodFloatParseCase<Float16>("0", LeadingSign::Plus, uint16_t{0}), + GoodFloatParseCase<Float16>("0.0", LeadingSign::Plus, uint16_t{0}), + GoodFloatParseCase<Float16>("2.0", LeadingSign::Plus, uint16_t{0x4000}), + GoodFloatParseCase<Float16>("0.0", LeadingSign::Minus, + uint16_t{0x8000}), + GoodFloatParseCase<Float16>("2.0", LeadingSign::Minus, + uint16_t{0xc000}), - // When negate_value is true, we should not accept a leading minus or + // When a leading sign is present, we should not accept a leading minus + // or // plus. - BadFloatParseCase<Float16>("-0.0", true, uint16_t{0}), - BadFloatParseCase<Float16>("-2.0", true, uint16_t{0}), - BadFloatParseCase<Float16>("+0.0", true, uint16_t{0}), - BadFloatParseCase<Float16>("+2.0", true, uint16_t{0}), + BadFloatParseCase<Float16>("-0.0", LeadingSign::Minus, uint16_t{0}), + BadFloatParseCase<Float16>("-2.0", LeadingSign::Minus, uint16_t{0}), + BadFloatParseCase<Float16>("+0.0", LeadingSign::Minus, uint16_t{0}), + BadFloatParseCase<Float16>("+2.0", LeadingSign::Minus, uint16_t{0}), + BadFloatParseCase<Float16>("-0.0", LeadingSign::Plus, uint16_t{0}), + BadFloatParseCase<Float16>("-2.0", LeadingSign::Plus, uint16_t{0}), + BadFloatParseCase<Float16>("+0.0", LeadingSign::Plus, uint16_t{0}), + BadFloatParseCase<Float16>("+2.0", LeadingSign::Plus, uint16_t{0}), + })); + +using ParseNormalFloatE4M3Test = + ::testing::TestWithParam<FloatParseCase<Float8_E4M3>>; + +TEST_P(ParseNormalFloatE4M3Test, Samples) { + std::stringstream input(GetParam().literal); + HexFloat<FloatProxy<Float8_E4M3>> parsed_value(0); + ParseNormalFloat(input, GetParam().leading_sign, parsed_value); + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().literal + << " leading_sign: " << str(GetParam().leading_sign); + if (GetParam().expect_success) { + EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) + << " literal: " << GetParam().literal + << " leading_sign: " << str(GetParam().leading_sign); + } +} + +INSTANTIATE_TEST_SUITE_P( + FloatE4M3Parse, ParseNormalFloatE4M3Test, + ::testing::ValuesIn(std::vector<FloatParseCase<Float8_E4M3>>{ + // Failing cases due to trivially incorrect syntax. + BadFloatParseCase<Float8_E4M3>("abc", LeadingSign::None, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("abc", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("abc", LeadingSign::Plus, uint8_t{0}), + + // Valid cases. + GoodFloatParseCase<Float8_E4M3>("0", LeadingSign::None, uint8_t{0}), + GoodFloatParseCase<Float8_E4M3>("0.0", LeadingSign::None, uint8_t{0}), + GoodFloatParseCase<Float8_E4M3>("-0.0", LeadingSign::None, + uint8_t{0x80}), + GoodFloatParseCase<Float8_E4M3>("2.0", LeadingSign::None, + uint8_t{0x40}), + GoodFloatParseCase<Float8_E4M3>("-2.0", LeadingSign::None, + uint8_t{0xc0}), + GoodFloatParseCase<Float8_E4M3>("+2.0", LeadingSign::None, + uint8_t{0x40}), + // Cases with leading sign. + GoodFloatParseCase<Float8_E4M3>("0", LeadingSign::Plus, uint8_t{0}), + GoodFloatParseCase<Float8_E4M3>("0.0", LeadingSign::Plus, uint8_t{0}), + GoodFloatParseCase<Float8_E4M3>("2.0", LeadingSign::Plus, + uint8_t{0x40}), + GoodFloatParseCase<Float8_E4M3>("0.0", LeadingSign::Minus, + uint8_t{0x80}), + GoodFloatParseCase<Float8_E4M3>("2.0", LeadingSign::Minus, + uint8_t{0xc0}), + + // When a leading sign is present, we should not accept a leading minus + // or + // plus. + BadFloatParseCase<Float8_E4M3>("-0.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("-2.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("+0.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("+2.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("-0.0", LeadingSign::Plus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("-2.0", LeadingSign::Plus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("+0.0", LeadingSign::Plus, uint8_t{0}), + BadFloatParseCase<Float8_E4M3>("+2.0", LeadingSign::Plus, uint8_t{0}), + })); + +using ParseNormalFloatE5M2Test = + ::testing::TestWithParam<FloatParseCase<Float8_E5M2>>; + +TEST_P(ParseNormalFloatE5M2Test, Samples) { + std::stringstream input(GetParam().literal); + HexFloat<FloatProxy<Float8_E5M2>> parsed_value(0); + ParseNormalFloat(input, GetParam().leading_sign, parsed_value); + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().literal + << " leading_sign: " << str(GetParam().leading_sign); + if (GetParam().expect_success) { + EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) + << " literal: " << GetParam().literal + << " leading_sign: " << str(GetParam().leading_sign); + } +} + +INSTANTIATE_TEST_SUITE_P( + FloatE5M2Parse, ParseNormalFloatE5M2Test, + ::testing::ValuesIn(std::vector<FloatParseCase<Float8_E5M2>>{ + // Failing cases due to trivially incorrect syntax. + BadFloatParseCase<Float8_E5M2>("abc", LeadingSign::None, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("abc", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("abc", LeadingSign::Plus, uint8_t{0}), + + // Valid cases. + GoodFloatParseCase<Float8_E5M2>("0", LeadingSign::None, uint8_t{0}), + GoodFloatParseCase<Float8_E5M2>("0.0", LeadingSign::None, uint8_t{0}), + GoodFloatParseCase<Float8_E5M2>("-0.0", LeadingSign::None, + uint8_t{0x80}), + GoodFloatParseCase<Float8_E5M2>("2.0", LeadingSign::None, + uint8_t{0x40}), + GoodFloatParseCase<Float8_E5M2>("-2.0", LeadingSign::None, + uint8_t{0xc0}), + GoodFloatParseCase<Float8_E5M2>("+2.0", LeadingSign::None, + uint8_t{0x40}), + // Cases with a leading sign + GoodFloatParseCase<Float8_E5M2>("0", LeadingSign::Plus, uint8_t{0}), + GoodFloatParseCase<Float8_E5M2>("0.0", LeadingSign::Plus, uint8_t{0}), + GoodFloatParseCase<Float8_E5M2>("2.0", LeadingSign::Plus, + uint8_t{0x40}), + GoodFloatParseCase<Float8_E5M2>("0.0", LeadingSign::Minus, + uint8_t{0x80}), + GoodFloatParseCase<Float8_E5M2>("2.0", LeadingSign::Minus, + uint8_t{0xc0}), + + // When a leading sign is present, we should not accept a leading minus + // or + // plus. + BadFloatParseCase<Float8_E5M2>("-0.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("-2.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("+0.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("+2.0", LeadingSign::Minus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("-0.0", LeadingSign::Plus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("-2.0", LeadingSign::Plus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("+0.0", LeadingSign::Plus, uint8_t{0}), + BadFloatParseCase<Float8_E5M2>("+2.0", LeadingSign::Plus, uint8_t{0}), })); // A test case for detecting infinities. @@ -1307,6 +1932,68 @@ {"-1e400", false, uint16_t{0xfbff}}, }))); +using FloatProxyParseOverflowFloatE4M3Test = + ::testing::TestWithParam<OverflowParseCase<uint8_t>>; + +TEST_P(FloatProxyParseOverflowFloatE4M3Test, Sample) { + std::istringstream input(GetParam().input); + HexFloat<FloatProxy<Float8_E4M3>> value(0); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().input; + if (GetParam().expect_success) { + EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value)) + << " literal: " << GetParam().input; + } +} + +INSTANTIATE_TEST_SUITE_P( + FloatE4M3Overflow, FloatProxyParseOverflowFloatE4M3Test, + ::testing::ValuesIn(std::vector<OverflowParseCase<uint8_t>>({ + {"0", true, uint8_t{0}}, + {"0.0", true, uint8_t{0}}, + {"1.0", true, uint8_t{0x38}}, + // Overflow for E4M3 float is an error, and returns max or + // lowest value. + {"1e38", false, uint8_t{0x7e}}, + {"1e40", false, uint8_t{0x7e}}, + {"1e400", false, uint8_t{0x7e}}, + {"-1e38", false, uint8_t{0xfe}}, + {"-1e40", false, uint8_t{0xfe}}, + {"-1e400", false, uint8_t{0xfe}}, + }))); + +using FloatProxyParseOverflowFloatE5M2Test = + ::testing::TestWithParam<OverflowParseCase<uint8_t>>; + +TEST_P(FloatProxyParseOverflowFloatE5M2Test, Sample) { + std::istringstream input(GetParam().input); + HexFloat<FloatProxy<Float8_E5M2>> value(0); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().input; + if (GetParam().expect_success) { + EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value)) + << " literal: " << GetParam().input; + } +} + +INSTANTIATE_TEST_SUITE_P( + FloatE5M2Overflow, FloatProxyParseOverflowFloatE5M2Test, + ::testing::ValuesIn(std::vector<OverflowParseCase<uint8_t>>({ + {"0", true, uint8_t{0}}, + {"0.0", true, uint8_t{0}}, + {"1.0", true, uint8_t{0x3c}}, + // Overflow for E5M2 float is an error, and returns max or + // lowest value. + {"1e38", false, uint8_t{0x7b}}, + {"1e40", false, uint8_t{0x7b}}, + {"1e400", false, uint8_t{0x7b}}, + {"-1e38", false, uint8_t{0xfb}}, + {"-1e40", false, uint8_t{0xfb}}, + {"-1e400", false, uint8_t{0xfb}}, + }))); + TEST(FloatProxy, Max) { EXPECT_THAT(FloatProxy<Float16>::max().getAsFloat().get_value(), Eq(uint16_t{0x7bff})); @@ -1351,6 +2038,10 @@ using Float32StreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>; using Float16StreamParseTest = ::testing::TestWithParam<StreamParseCase<Float16>>; +using FloatE4M3StreamParseTest = + ::testing::TestWithParam<StreamParseCase<Float8_E4M3>>; +using FloatE5M2StreamParseTest = + ::testing::TestWithParam<StreamParseCase<Float8_E5M2>>; TEST_P(Float32StreamParseTest, Samples) { std::stringstream input(GetParam().literal); @@ -1518,6 +2209,18 @@ })); INSTANTIATE_TEST_SUITE_P( + Underflow, Float32StreamParseTest, + ::testing::ValuesIn(std::vector<StreamParseCase<float>>{ + // Underflow + {"0x1.p-149", true, "", ldexpf(1, -149)}, + {"0x1.p-150", true, "", 0.0f}, + {"+0x1.p-149", true, "", ldexpf(1, -149)}, + {"+0x1.p-150", true, "", 0.0f}, + {"-0x1.p-149", true, "", -ldexpf(1, -149)}, + {"-0x1.p-150", true, "", -0.0f}, + })); + +INSTANTIATE_TEST_SUITE_P( HexFloat16ExcessSignificantDigits, Float16StreamParseTest, ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{ // Zero @@ -1620,6 +2323,197 @@ {"0x8.5a40000p0", true, "", makeF16(0, 3, 0x02d)}, {"0x8.5a7ffffp0", true, "", makeF16(0, 3, 0x02d)}})); +INSTANTIATE_TEST_SUITE_P( + Underflow, Float16StreamParseTest, + ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{ + // Underflow + {"0x1.p-24", true, "", Float16(uint16_t(1))}, + {"0x1.p-25", true, "", Float16(uint16_t(0))}, + {"+0x1.p-24", true, "", Float16(uint16_t(1))}, + {"+0x1.p-25", true, "", Float16(uint16_t(0))}, + {"-0x1.p-24", true, "", Float16(uint16_t(0x8001))}, + {"-0x1.p-25", true, "", Float16(uint16_t(0x8000))}, + })); + +// Returns a E4M3 constructed from its sign bit, unbiased exponent, and +// mantissa. +Float8_E4M3 makeE4M3(int sign_bit, int unbiased_exp, int mantissa) { + EXPECT_LE(0, sign_bit); + EXPECT_LE(sign_bit, 1); + // Exponent is 4 bits, with bias of 7. + EXPECT_LE(-7, unbiased_exp); // -7 means zero or subnormal + EXPECT_LE(unbiased_exp, 8); + EXPECT_LE(0, mantissa); + EXPECT_LE(mantissa, 0x7); + const unsigned biased_exp = 7 + unbiased_exp; + const uint32_t as_bits = sign_bit << 7 | (biased_exp << 3) | mantissa; + EXPECT_LE(as_bits, 0xffu); + return Float8_E4M3(static_cast<uint8_t>(as_bits)); +} + +TEST_P(FloatE4M3StreamParseTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat<FloatProxy<Float8_E4M3>> parsed_value(makeE4M3(0, 0, 0)); + // 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; + const auto got = parsed_value.value(); + const auto expected = GetParam().expected_value.value(); + EXPECT_EQ(got.data(), expected.data()) + << "got: " << got << " expected: " << expected; + } else { + EXPECT_TRUE(input.fail()); + } +} + +INSTANTIATE_TEST_SUITE_P( + HexFloatE4M3IncreasingExponentsAndMantissa, FloatE4M3StreamParseTest, + ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E4M3>>{ + // Zero + {"0x0p0", true, "", makeE4M3(0, -7, 0x0)}, + {"0x0p5000000000000", true, "", makeE4M3(0, -7, 0x0)}, + {"-0x0p5000000000000", true, "", makeE4M3(1, -7, 0x0)}, + // Leading 1 + {"0x1p0", true, "", makeE4M3(0, 0, 0x0)}, + {"0x1p1", true, "", makeE4M3(0, 1, 0x0)}, + {"0x1p8", true, "", makeE4M3(0, 8, 0x0)}, + {"0x1p-1", true, "", makeE4M3(0, -1, 0x0)}, + {"0x1p-6", true, "", makeE4M3(0, -6, 0x0)}, + // Leading 2 + {"0x2p0", true, "", makeE4M3(0, 1, 0x0)}, + {"0x2p1", true, "", makeE4M3(0, 2, 0x0)}, + {"0x2p7", true, "", makeE4M3(0, 8, 0x0)}, + {"0x2p-1", true, "", makeE4M3(0, 0, 0x0)}, + {"0x2p-7", true, "", makeE4M3(0, -6, 0x0)}, + // Leading 8 + {"0x8p0", true, "", makeE4M3(0, 3, 0x0)}, + {"0x8p1", true, "", makeE4M3(0, 4, 0x0)}, + {"0x8p5", true, "", makeE4M3(0, 8, 0x0)}, + {"0x8p-3", true, "", makeE4M3(0, 0, 0x0)}, + {"0x8p-9", true, "", makeE4M3(0, -6, 0x0)}, + // Leading 10 + {"0x10.0p0", true, "", makeE4M3(0, 4, 0x0)}, + {"0x10.0p1", true, "", makeE4M3(0, 5, 0x0)}, + {"0x10.0p4", true, "", makeE4M3(0, 8, 0x0)}, + {"0x10.0p-5", true, "", makeE4M3(0, -1, 0x0)}, + {"0x10.0p-10", true, "", makeE4M3(0, -6, 0x0)}, + // Samples that drop out bits *and* truncate significant bits + // that can't be represented. + // Progressively increase the leading digit. + {"0x1.5a40000p0", true, "", makeE4M3(0, 0, 0x2)}, + {"0x1.5a7ffffp0", true, "", makeE4M3(0, 0, 0x2)}, + {"0x2.5a40000p0", true, "", makeE4M3(0, 1, 0x1)}, + {"0x2.5a7ffffp0", true, "", makeE4M3(0, 1, 0x1)}, + {"0x4.5a40000p0", true, "", makeE4M3(0, 2, 0x0)}, + {"0x4.5a7ffffp0", true, "", makeE4M3(0, 2, 0x0)}, + {"0x8.5a40000p0", true, "", makeE4M3(0, 3, 0x0)}, + {"0x8.5a7ffffp0", true, "", makeE4M3(0, 3, 0x0)}})); + +INSTANTIATE_TEST_SUITE_P( + Underflow, FloatE4M3StreamParseTest, + ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E4M3>>{ + // Underflow + {"0x1.p-9", true, "", Float8_E4M3(uint8_t(1))}, + {"0x1.p-10", true, "", Float8_E4M3(uint8_t(0))}, + {"+0x1.p-9", true, "", Float8_E4M3(uint8_t(1))}, + {"+0x1.p-10", true, "", Float8_E4M3(uint8_t(0))}, + {"-0x1.p-9", true, "", Float8_E4M3(uint8_t(0x81))}, + {"-0x1.p-10", true, "", Float8_E4M3(uint8_t(0x80))}, + })); + +// Returns a E5M2 constructed from its sign bit, unbiased exponent, and +// mantissa. +Float8_E5M2 makeE5M2(int sign_bit, int unbiased_exp, int mantissa) { + EXPECT_LE(0, sign_bit); + EXPECT_LE(sign_bit, 1); + // Exponent is 5 bits, with bias of 15. + EXPECT_LE(-15, unbiased_exp); // -15 means zero or subnormal + EXPECT_LE(unbiased_exp, 16); + EXPECT_LE(0, mantissa); + EXPECT_LE(mantissa, 0x3); + const unsigned biased_exp = 15 + unbiased_exp; + const uint32_t as_bits = sign_bit << 7 | (biased_exp << 2) | mantissa; + EXPECT_LE(as_bits, 0xffu); + return Float8_E5M2(static_cast<uint8_t>(as_bits)); +} + +TEST_P(FloatE5M2StreamParseTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat<FloatProxy<Float8_E5M2>> parsed_value(makeE5M2(0, 0, 0)); + // 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; + const auto got = parsed_value.value(); + const auto expected = GetParam().expected_value.value(); + EXPECT_EQ(got.data(), expected.data()) + << "got: " << got << " expected: " << expected; + } else { + EXPECT_TRUE(input.fail()); + } +} + +INSTANTIATE_TEST_SUITE_P( + HexFloatE5M2IncreasingExponentsAndMantissa, FloatE5M2StreamParseTest, + ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E5M2>>{ + // Zero + {"0x0p0", true, "", makeE5M2(0, -15, 0x0)}, + {"0x0p5000000000000", true, "", makeE5M2(0, -15, 0x0)}, + {"-0x0p5000000000000", true, "", makeE5M2(1, -15, 0x0)}, + // Leading 1 + {"0x1p0", true, "", makeE5M2(0, 0, 0x0)}, + {"0x1p1", true, "", makeE5M2(0, 1, 0x0)}, + {"0x1p16", true, "", makeE5M2(0, 16, 0x0)}, + {"0x1p-1", true, "", makeE5M2(0, -1, 0x0)}, + {"0x1p-14", true, "", makeE5M2(0, -14, 0x0)}, + // Leading 2 + {"0x2p0", true, "", makeE5M2(0, 1, 0x0)}, + {"0x2p1", true, "", makeE5M2(0, 2, 0x0)}, + {"0x2p15", true, "", makeE5M2(0, 16, 0x0)}, + {"0x2p-1", true, "", makeE5M2(0, 0, 0x0)}, + {"0x2p-15", true, "", makeE5M2(0, -14, 0x0)}, + // Leading 8 + {"0x8p0", true, "", makeE5M2(0, 3, 0x0)}, + {"0x8p1", true, "", makeE5M2(0, 4, 0x0)}, + {"0x8p13", true, "", makeE5M2(0, 16, 0x0)}, + {"0x8p-3", true, "", makeE5M2(0, 0, 0x0)}, + {"0x8p-17", true, "", makeE5M2(0, -14, 0x0)}, + // Leading 10 + {"0x10.0p0", true, "", makeE5M2(0, 4, 0x0)}, + {"0x10.0p1", true, "", makeE5M2(0, 5, 0x0)}, + {"0x10.0p12", true, "", makeE5M2(0, 16, 0x0)}, + {"0x10.0p-5", true, "", makeE5M2(0, -1, 0x0)}, + {"0x10.0p-18", true, "", makeE5M2(0, -14, 0x0)}, + // Samples that drop out bits *and* truncate significant bits + // that can't be represented. + // Progressively increase the leading digit. + {"0x1.aa40000p0", true, "", makeE5M2(0, 0, 0x2)}, + {"0x1.aa7ffffp0", true, "", makeE5M2(0, 0, 0x2)}, + {"0x2.aa40000p0", true, "", makeE5M2(0, 1, 0x1)}, + {"0x2.aa7ffffp0", true, "", makeE5M2(0, 1, 0x1)}, + {"0x4.aa40000p0", true, "", makeE5M2(0, 2, 0x0)}, + {"0x4.aa7ffffp0", true, "", makeE5M2(0, 2, 0x0)}, + {"0x8.aa40000p0", true, "", makeE5M2(0, 3, 0x0)}, + {"0x8.aa7ffffp0", true, "", makeE5M2(0, 3, 0x0)}, + })); + +INSTANTIATE_TEST_SUITE_P( + Underflow, FloatE5M2StreamParseTest, + ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E5M2>>{ + // Underflow + {"0x1.p-16", true, "", Float8_E5M2(uint8_t(1))}, + {"0x1.p-17", true, "", Float8_E5M2(uint8_t(0))}, + {"+0x1.p-16", true, "", Float8_E5M2(uint8_t(1))}, + {"+0x1.p-17", true, "", Float8_E5M2(uint8_t(0))}, + {"-0x1.p-16", true, "", Float8_E5M2(uint8_t(0x81))}, + {"-0x1.p-17", true, "", Float8_E5M2(uint8_t(0x80))}, + })); + } // namespace } // namespace utils } // namespace spvtools
diff --git a/test/link/CMakeLists.txt b/test/link/CMakeLists.txt index ee41b91..4076530 100644 --- a/test/link/CMakeLists.txt +++ b/test/link/CMakeLists.txt
@@ -24,5 +24,6 @@ partial_linkage_test.cpp unique_ids_test.cpp type_match_test.cpp + function_variants.cpp LIBS SPIRV-Tools-opt SPIRV-Tools-link )
diff --git a/test/link/function_variants.cpp b/test/link/function_variants.cpp new file mode 100644 index 0000000..1f08414 --- /dev/null +++ b/test/link/function_variants.cpp
@@ -0,0 +1,581 @@ +// Copyright 2025 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 <string> + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using FunctionVariants = spvtest::LinkerTest; + +TEST_F(FunctionVariants, Dot4) { + constexpr const char* const dot4_fp32 = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int8 + OpCapability Int64 + OpCapability Linkage + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %2 "dot4" %3 + OpExecutionMode %2 ContractionOff + OpSource OpenCL_C 102000 + OpName %2 "dot4" + OpName %3 "__spirv_BuiltInGlobalInvocationId" + OpName %4 "entry" + %5 = OpTypeFloat 32 + %6 = OpTypePointer CrossWorkgroup %5 + %7 = OpTypeInt 8 0 + %8 = OpTypePointer CrossWorkgroup %7 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 %6 %6 %8 + %11 = OpTypeInt 64 0 + %12 = OpTypeInt 32 0 + %13 = OpTypeVector %11 3 + %14 = OpTypePointer Input %13 + %15 = OpTypeVector %5 4 + %16 = OpConstant %11 30 + %17 = OpConstant %11 32 + %18 = OpConstant %12 2 + %3 = OpVariable %14 Input + %2 = OpFunction %9 None %10 + %19 = OpFunctionParameter %6 + %20 = OpFunctionParameter %6 + %21 = OpFunctionParameter %8 + %4 = OpLabel + %22 = OpLoad %13 %3 Aligned 1 + %23 = OpCompositeExtract %11 %22 0 + %24 = OpUConvert %12 %23 + %25 = OpShiftLeftLogical %12 %24 %18 + %26 = OpSConvert %11 %25 + %27 = OpExtInst %15 %1 vloadn %26 %19 4 + %28 = OpExtInst %15 %1 vloadn %26 %20 4 + %29 = OpDot %5 %27 %28 + %30 = OpShiftLeftLogical %11 %23 %17 + %31 = OpShiftRightArithmetic %11 %30 %16 + %32 = OpInBoundsPtrAccessChain %8 %21 %31 + %33 = OpBitcast %6 %32 + OpStore %33 %29 Aligned 4 + OpReturn + OpFunctionEnd + )"; + + constexpr const char* const dot4_fp16 = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int8 + OpCapability Int64 + OpCapability Float16 + OpCapability Linkage + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %2 "dot4" %3 + OpExecutionMode %2 ContractionOff + OpSource OpenCL_C 102000 + OpName %2 "dot4" + OpName %3 "__spirv_BuiltInGlobalInvocationId" + OpName %4 "entry" + %5 = OpTypeFloat 16 + %6 = OpTypePointer CrossWorkgroup %5 + %7 = OpTypeInt 8 0 + %8 = OpTypePointer CrossWorkgroup %7 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 %6 %6 %8 + %11 = OpTypeInt 64 0 + %12 = OpTypeInt 32 0 + %13 = OpTypeVector %11 3 + %14 = OpTypePointer Input %13 + %15 = OpTypeVector %5 4 + %16 = OpConstant %11 31 + %17 = OpConstant %11 32 + %18 = OpConstant %12 2 + %3 = OpVariable %14 Input + %2 = OpFunction %9 None %10 + %19 = OpFunctionParameter %6 + %20 = OpFunctionParameter %6 + %21 = OpFunctionParameter %8 + %4 = OpLabel + %22 = OpLoad %13 %3 Aligned 1 + %23 = OpCompositeExtract %11 %22 0 + %24 = OpUConvert %12 %23 + %25 = OpShiftLeftLogical %12 %24 %18 + %26 = OpSConvert %11 %25 + %27 = OpExtInst %15 %1 vloadn %26 %19 4 + %28 = OpExtInst %15 %1 vloadn %26 %20 4 + %29 = OpDot %5 %27 %28 + %30 = OpShiftLeftLogical %11 %23 %17 + %31 = OpShiftRightArithmetic %11 %30 %16 + %32 = OpInBoundsPtrAccessChain %8 %21 %31 + %33 = OpBitcast %6 %32 + OpStore %33 %29 Aligned 4 + OpReturn + OpFunctionEnd + )"; + + // clang-format off + const std::vector<const char*> expected_lines = { + "OpCapability FunctionVariantsINTEL", + "OpCapability SpecConditionalINTEL", + "OpCapability Kernel", + "OpCapability Addresses", + "OpCapability Int8", + "OpCapability Int64", + "OpCapability Linkage", + "OpConditionalCapabilityINTEL %dot4_fp16_spv Float16", + "OpExtension \"SPV_INTEL_function_variants\"", + "OpConditionalEntryPointINTEL %dot4_fp32_spv Kernel %dot4 \"dot4\" %__spirv_BuiltInGlobalInvocationId", + "OpConditionalEntryPointINTEL %dot4_fp16_spv Kernel %dot4_0 \"dot4\" %__spirv_BuiltInGlobalInvocationId_0", + "OpModuleProcessed \"SPV_INTEL_function_variants registry version 0\"", + "OpDecorate %ulong_30 ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %ulong_32 ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %uint_2 ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %__spirv_BuiltInGlobalInvocationId ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %dot4 ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %ulong_31 ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %ulong_32_0 ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %uint_2_0 ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %__spirv_BuiltInGlobalInvocationId_0 ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %dot4_0 ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %float ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %_ptr_CrossWorkgroup_float ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %18 ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %v4float ConditionalINTEL %dot4_fp32_spv", + "OpDecorate %half ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %_ptr_CrossWorkgroup_half ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %22 ConditionalINTEL %dot4_fp16_spv", + "OpDecorate %v4half ConditionalINTEL %dot4_fp16_spv", + "%float = OpTypeFloat 32", + "%_ptr_CrossWorkgroup_float = OpTypePointer CrossWorkgroup %float", + "%18 = OpTypeFunction %void %_ptr_CrossWorkgroup_float %_ptr_CrossWorkgroup_float %_ptr_CrossWorkgroup_uchar", + "%v4float = OpTypeVector %float 4", + "%ulong_30 = OpConstant %ulong 30", + "%ulong_32 = OpConstant %ulong 32", + "%uint_2 = OpConstant %uint 2", + "%__spirv_BuiltInGlobalInvocationId = OpVariable %_ptr_Input_v3ulong Input", + "%bool = OpTypeBool", + "%32 = OpSpecConstantArchitectureINTEL %bool 2 3 174 0", + "%33 = OpSpecConstantTargetINTEL %bool 7", + "%34 = OpSpecConstantTargetINTEL %bool 8", + "%35 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Int64 Int8", + "%36 = OpSpecConstantOp %bool LogicalOr %33 %34", + "%37 = OpSpecConstantOp %bool LogicalAnd %35 %32", + "%38 = OpSpecConstantOp %bool LogicalAnd %37 %36", + "%half = OpTypeFloat 16", + "%_ptr_CrossWorkgroup_half = OpTypePointer CrossWorkgroup %half", + "%22 = OpTypeFunction %void %_ptr_CrossWorkgroup_half %_ptr_CrossWorkgroup_half %_ptr_CrossWorkgroup_uchar", + "%v4half = OpTypeVector %half 4", + "%ulong_31 = OpConstant %ulong 31", + "%ulong_32_0 = OpConstant %ulong 32", + "%uint_2_0 = OpConstant %uint 2", + "%__spirv_BuiltInGlobalInvocationId_0 = OpVariable %_ptr_Input_v3ulong Input", + "%39 = OpSpecConstantArchitectureINTEL %bool 2 3 174 4", + "%40 = OpSpecConstantTargetINTEL %bool 7", + "%41 = OpSpecConstantTargetINTEL %bool 8", + "%42 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Float16 Int64 Int8", + "%43 = OpSpecConstantOp %bool LogicalOr %40 %41", + "%44 = OpSpecConstantOp %bool LogicalAnd %42 %39", + "%dot4_fp16_spv = OpSpecConstantOp %bool LogicalAnd %44 %43", + "%45 = OpSpecConstantOp %bool LogicalNot %dot4_fp16_spv", + "%dot4_fp32_spv = OpSpecConstantOp %bool LogicalAnd %38 %45", + "%dot4 = OpFunction %void None %18", + "%56 = OpDot %float %54 %55", + "%dot4_0 = OpFunction %void None %22", + "%71 = OpDot %half %69 %70", + }; + // clang-format on + + const std::string targets_csv = + "module,target,features\n" + "dot4_fp32.spv,7,\n" + "dot4_fp32.spv,8,\n" + "dot4_fp16.spv,7,\n" + "dot4_fp16.spv,8,\n"; + const std::string architectures_csv = + "module,category,family,op,architecture\n" + "dot4_fp32.spv,2,3,174,0\n" + "dot4_fp16.spv,2,3,174,4\n"; + const std::vector<std::string> sources = {dot4_fp32, dot4_fp16}; + const std::vector<std::string> in_files = {"dot4_fp32.spv", "dot4_fp16.spv"}; + + LinkerOptions options; + options.SetInFiles(in_files); + options.SetFnVarTargetsCsv(targets_csv); + options.SetFnVarArchitecturesCsv(architectures_csv); + options.SetHasFnVarCapabilities(true); + options.SetCreateLibrary(true); + options.SetVerifyIds(true); + + spvtest::Binary linked_binary; + spv_result_t res = AssembleAndLink(sources, &linked_binary, options); + EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_TRUE(Validate(linked_binary)); + + std::string linked_asm; + res = Disassemble(linked_binary, &linked_asm); + EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + for (const auto& expected : expected_lines) { + EXPECT_THAT(linked_asm, testing::HasSubstr(expected)); + } +} + +TEST_F(FunctionVariants, FAddAsm) { + constexpr const char* const foo_base = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int8 + OpCapability Int64 + OpCapability Linkage + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpSource OpenCL_CPP 100000 + OpName %2 "foo" + OpName %3 "add" + OpName %4 "entry" + OpName %5 "work" + OpName %6 "call" + OpName %7 "entry" + OpDecorate %2 LinkageAttributes "foo" Export + OpDecorate %5 LinkageAttributes "work" Export + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %9 %9 %9 + %11 = OpTypeInt 8 0 + %12 = OpTypePointer Function %11 + %13 = OpTypeInt 64 0 + %14 = OpConstant %13 28 + %15 = OpConstant %13 24 + %16 = OpConstant %13 20 + %17 = OpConstant %13 16 + %18 = OpConstant %13 12 + %19 = OpConstant %13 8 + %20 = OpConstant %13 4 + %2 = OpFunction %8 DontInline %10 + %21 = OpFunctionParameter %9 + %22 = OpFunctionParameter %9 + %23 = OpFunctionParameter %9 + %4 = OpLabel + %24 = OpLoad %8 %21 Aligned 4 + %25 = OpLoad %8 %22 Aligned 4 + %3 = OpFAdd %8 %24 %25 + OpStore %23 %3 Aligned 4 + OpReturnValue %3 + OpFunctionEnd + %5 = OpFunction %8 None %10 + %26 = OpFunctionParameter %9 + %27 = OpFunctionParameter %9 + %28 = OpFunctionParameter %9 + %7 = OpLabel + %6 = OpFunctionCall %8 %2 %26 %27 %28 + %29 = OpFDiv %8 %6 %6 + %30 = OpFDiv %8 %29 %6 + OpReturnValue %30 + OpFunctionEnd + )"; + + constexpr const char* const foo_asm = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability AsmINTEL + OpExtension "SPV_INTEL_inline_assembly" + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpSource OpenCL_CPP 100000 + OpName %2 "a" + OpName %3 "b" + OpName %4 "c" + OpName %5 "foo" + OpName %6 "add" + OpName %7 "entry" + OpDecorate %2 FuncParamAttr NoWrite + OpDecorate %2 FuncParamAttr NoAlias + OpDecorate %3 FuncParamAttr NoWrite + OpDecorate %3 FuncParamAttr NoAlias + OpDecorate %4 FuncParamAttr NoAlias + OpDecorate %5 LinkageAttributes "foo" Export + OpDecorate %8 SideEffectsINTEL + %9 = OpTypeFloat 32 + %10 = OpTypePointer Function %9 + %11 = OpTypeFunction %9 %10 %10 %10 + %12 = OpTypeVoid + %13 = OpTypeFunction %12 + %14 = OpAsmTargetINTEL "spirv64-unknown-unknown" + %8 = OpAsmINTEL %12 %13 %14 "nop1" "" + %5 = OpFunction %9 None %11 + %2 = OpFunctionParameter %10 + %3 = OpFunctionParameter %10 + %4 = OpFunctionParameter %10 + %7 = OpLabel + %15 = OpLoad %9 %2 Aligned 4 + %16 = OpLoad %9 %3 Aligned 4 + %6 = OpFSub %9 %15 %16 + OpStore %4 %6 Aligned 4 + %17 = OpAsmCallINTEL %12 %8 + OpReturnValue %6 + OpFunctionEnd + )"; + + // same as foo_asm, just with OpFMul and a different assembly string in + // OpAsmINTEL + constexpr const char* const foo_asm2 = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability AsmINTEL + OpExtension "SPV_INTEL_inline_assembly" + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpSource OpenCL_CPP 100000 + OpName %2 "a" + OpName %3 "b" + OpName %4 "c" + OpName %5 "foo" + OpName %6 "add" + OpName %7 "entry" + OpDecorate %2 FuncParamAttr NoWrite + OpDecorate %2 FuncParamAttr NoAlias + OpDecorate %3 FuncParamAttr NoWrite + OpDecorate %3 FuncParamAttr NoAlias + OpDecorate %4 FuncParamAttr NoAlias + OpDecorate %5 LinkageAttributes "foo" Export + OpDecorate %8 SideEffectsINTEL + %9 = OpTypeFloat 32 + %10 = OpTypePointer Function %9 + %11 = OpTypeFunction %9 %10 %10 %10 + %12 = OpTypeVoid + %13 = OpTypeFunction %12 + %14 = OpAsmTargetINTEL "spirv64-unknown-unknown" + %8 = OpAsmINTEL %12 %13 %14 "nop2" "" + %5 = OpFunction %9 None %11 + %2 = OpFunctionParameter %10 + %3 = OpFunctionParameter %10 + %4 = OpFunctionParameter %10 + %7 = OpLabel + %15 = OpLoad %9 %2 Aligned 4 + %16 = OpLoad %9 %3 Aligned 4 + %6 = OpFMul %9 %15 %16 + OpStore %4 %6 Aligned 4 + %17 = OpAsmCallINTEL %12 %8 + OpReturnValue %6 + OpFunctionEnd + )"; + + // clang-format off + const std::vector<const char*> expected_lines = { + "OpCapability FunctionVariantsINTEL", + "OpCapability SpecConditionalINTEL", + "OpCapability Kernel", + "OpCapability Addresses", + "OpConditionalCapabilityINTEL %foo_spv Int8", + "OpConditionalCapabilityINTEL %foo_spv Int64", + "OpCapability Linkage", + "OpConditionalCapabilityINTEL %2 AsmINTEL", + "OpExtension \"SPV_INTEL_function_variants\"", + "OpConditionalExtensionINTEL %2 \"SPV_INTEL_inline_assembly\"", + "OpModuleProcessed \"SPV_INTEL_function_variants registry version 0\"", + "OpDecorate %foo LinkageAttributes \"foo\" Export", + "OpDecorate %work LinkageAttributes \"work\" Export", + "OpDecorate %ulong_28 ConditionalINTEL %foo_spv", + "OpDecorate %ulong_24 ConditionalINTEL %foo_spv", + "OpDecorate %ulong_20 ConditionalINTEL %foo_spv", + "OpDecorate %ulong_16 ConditionalINTEL %foo_spv", + "OpDecorate %ulong_12 ConditionalINTEL %foo_spv", + "OpDecorate %ulong_8 ConditionalINTEL %foo_spv", + "OpDecorate %ulong_4 ConditionalINTEL %foo_spv", + "OpDecorate %foo ConditionalINTEL %foo_spv", + "OpDecorate %foo_0 LinkageAttributes \"foo\" Export", + "OpDecorate %32 ConditionalINTEL %foo_asm_spv", + "OpDecorate %31 ConditionalINTEL %foo_asm_spv", + "OpDecorate %foo_0 ConditionalINTEL %foo_asm_spv", + "OpDecorate %foo_1 LinkageAttributes \"foo\" Export", + "OpDecorate %34 ConditionalINTEL %foo_asm2_spv", + "OpDecorate %33 ConditionalINTEL %foo_asm2_spv", + "OpDecorate %foo_1 ConditionalINTEL %foo_asm2_spv", + "OpDecorate %call ConditionalINTEL %foo_spv", + "OpDecorate %35 ConditionalINTEL %foo_asm_spv", + "OpDecorate %36 ConditionalINTEL %foo_asm2_spv", + "OpDecorate %uchar ConditionalINTEL %foo_spv", + "OpDecorate %_ptr_Function_uchar ConditionalINTEL %foo_spv", + "OpDecorate %ulong ConditionalINTEL %foo_spv", + "OpDecorate %void ConditionalINTEL %2", + "OpDecorate %41 ConditionalINTEL %2", + "%ulong_28 = OpConstant %ulong 28", + "%ulong_24 = OpConstant %ulong 24", + "%ulong_20 = OpConstant %ulong 20", + "%ulong_16 = OpConstant %ulong 16", + "%ulong_12 = OpConstant %ulong 12", + "%ulong_8 = OpConstant %ulong 8", + "%ulong_4 = OpConstant %ulong 4", + "%bool = OpTypeBool", + "%46 = OpSpecConstantTargetINTEL %bool 4", + "%47 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Int64 Int8", + "%48 = OpSpecConstantOp %bool LogicalAnd %47 %46", + "%31 = OpAsmINTEL %void %41 %32 \"nop1\" \"\"", + "%49 = OpSpecConstantArchitectureINTEL %bool 1 1 170 1", + "%50 = OpSpecConstantTargetINTEL %bool 4 9 10", + "%51 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel AsmINTEL", + "%52 = OpSpecConstantOp %bool LogicalAnd %51 %49", + "%foo_asm_spv = OpSpecConstantOp %bool LogicalAnd %52 %50", + "%33 = OpAsmINTEL %void %41 %34 \"nop2\" \"\"", + "%53 = OpSpecConstantArchitectureINTEL %bool 1 7 174 1", + "%54 = OpSpecConstantArchitectureINTEL %bool 1 7 178 3", + "%55 = OpSpecConstantArchitectureINTEL %bool 1 8 170 1", + "%56 = OpSpecConstantArchitectureINTEL %bool 1 9 174 1", + "%57 = OpSpecConstantArchitectureINTEL %bool 1 9 178 3", + "%58 = OpSpecConstantTargetINTEL %bool 5 2 4 5", + "%59 = OpSpecConstantTargetINTEL %bool 6 2 4 5", + "%60 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel AsmINTEL", + "%61 = OpSpecConstantOp %bool LogicalAnd %53 %54", + "%62 = OpSpecConstantOp %bool LogicalAnd %56 %57", + "%63 = OpSpecConstantOp %bool LogicalOr %61 %55", + "%64 = OpSpecConstantOp %bool LogicalOr %63 %62", + "%65 = OpSpecConstantOp %bool LogicalOr %58 %59", + "%66 = OpSpecConstantOp %bool LogicalAnd %60 %64", + "%foo_asm2_spv = OpSpecConstantOp %bool LogicalAnd %66 %65", + "%67 = OpSpecConstantOp %bool LogicalOr %foo_asm_spv %foo_asm2_spv", + "%68 = OpSpecConstantOp %bool LogicalNot %67", + "%foo_spv = OpSpecConstantOp %bool LogicalAnd %48 %68", + "%2 = OpSpecConstantOp %bool LogicalOr %foo_asm_spv %foo_asm2_spv", + "%foo = OpFunction %float DontInline %44", + "%add = OpFAdd %float %72 %73", + "%work = OpFunction %float None %44", + "%call = OpFunctionCall %float %foo %74 %75 %76", + "%35 = OpFunctionCall %float %foo_0 %74 %75 %76", + "%36 = OpFunctionCall %float %foo_1 %74 %75 %76", + "%77 = OpConditionalCopyObjectINTEL %float %foo_spv %call %foo_asm_spv %35 %foo_asm2_spv %36", + "%78 = OpFDiv %float %77 %77", + "%79 = OpFDiv %float %78 %77", + "OpReturnValue %79", + "%foo_0 = OpFunction %float None %44", + "%add_0 = OpFSub %float %80 %81", + "%82 = OpAsmCallINTEL %void %31", + "%foo_1 = OpFunction %float None %44", + "%add_1 = OpFMul %float %83 %84", + "%85 = OpAsmCallINTEL %void %33", + }; + // clang-format on + + const std::string targets_csv = + "module,target,features\n" + "foo.spv,04,\n" // test leading zeros + "foo_asm.spv,4,9/0010\n" // test leading zeros + "foo_asm2.spv,5,2/4/5\n" + "foo_asm2.spv,6,2/4/5\n"; + const std::string architectures_csv = + "module,category,family,op,architecture\n" + "foo_asm.spv,1,1,170,1\n" + "foo_asm2.spv,1,7,174,1\n" + "foo_asm2.spv,1,7,178,3\n" + "foo_asm2.spv,1,8,170,1\n" + "foo_asm2.spv,1,9,174,1\n" + "foo_asm2.spv,1,9,178,3\n"; + const std::vector<std::string> sources = {foo_base, foo_asm, foo_asm2}; + const std::vector<std::string> in_files = {"foo.spv", "foo_asm.spv", + "foo_asm2.spv"}; + + LinkerOptions options; + options.SetInFiles(in_files); + options.SetFnVarTargetsCsv(targets_csv); + options.SetFnVarArchitecturesCsv(architectures_csv); + options.SetHasFnVarCapabilities(true); + options.SetCreateLibrary(true); + options.SetVerifyIds(true); + + spvtest::Binary linked_binary; + spv_result_t res = AssembleAndLink(sources, &linked_binary, options); + EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_TRUE(Validate(linked_binary)); + + std::string linked_asm; + res = Disassemble(linked_binary, &linked_asm); + EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + for (const auto& expected : expected_lines) { + EXPECT_THAT(linked_asm, testing::HasSubstr(expected)); + } +} + +TEST_F(FunctionVariants, InvalidNumber1) { + const std::string targets_csv = + "module,target,features\n" + "foo.spv,-4,9/10\n"; + const std::string architectures_csv = ""; + const std::vector<std::string> sources = {""}; + const std::vector<std::string> in_files = {"foo.spv"}; + + LinkerOptions options; + options.SetInFiles(in_files); + options.SetFnVarTargetsCsv(targets_csv); + options.SetFnVarArchitecturesCsv(architectures_csv); + options.SetHasFnVarCapabilities(true); + options.SetCreateLibrary(true); + options.SetVerifyIds(true); + + spvtest::Binary linked_binary; + spv_result_t res = AssembleAndLink(sources, &linked_binary, options); + EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting -4 to target."); +} + +TEST_F(FunctionVariants, InvalidNumber2) { + const std::string targets_csv = + "module,target,features\n" + "foo.spv,4,9/-10\n"; + const std::string architectures_csv = ""; + const std::vector<std::string> sources = {""}; + const std::vector<std::string> in_files = {"foo.spv"}; + + LinkerOptions options; + options.SetInFiles(in_files); + options.SetFnVarTargetsCsv(targets_csv); + options.SetFnVarArchitecturesCsv(architectures_csv); + options.SetHasFnVarCapabilities(true); + options.SetCreateLibrary(true); + options.SetVerifyIds(true); + + spvtest::Binary linked_binary; + spv_result_t res = AssembleAndLink(sources, &linked_binary, options); + EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), + "ERROR: 0: Error converting -10 in 9/-10 to target feature."); +} + +TEST_F(FunctionVariants, InvalidNumber3) { + const std::string targets_csv = + "module,target,features\n" + "foo.spv,4.0,9/10\n"; + const std::string architectures_csv = ""; + const std::vector<std::string> sources = {""}; + const std::vector<std::string> in_files = {"foo.spv"}; + + LinkerOptions options; + options.SetInFiles(in_files); + options.SetFnVarTargetsCsv(targets_csv); + options.SetFnVarArchitecturesCsv(architectures_csv); + options.SetHasFnVarCapabilities(true); + options.SetCreateLibrary(true); + options.SetVerifyIds(true); + + spvtest::Binary linked_binary; + spv_result_t res = AssembleAndLink(sources, &linked_binary, options); + EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting 4.0 to target."); +} + +} // namespace +} // namespace spvtools
diff --git a/test/link/linker_fixture.h b/test/link/linker_fixture.h index d005288..08a2880 100644 --- a/test/link/linker_fixture.h +++ b/test/link/linker_fixture.h
@@ -155,6 +155,19 @@ } } + void Match(const std::string& templateBody, + const spvtest::Binary& linked_binary) { + std::string result; + EXPECT_TRUE( + tools_.Disassemble(linked_binary, &result, disassemble_options_)) + << GetErrorMessage(); + auto match_res = effcee::Match(result, templateBody); + EXPECT_EQ(effcee::Result::Status::Ok, match_res.status()) + << match_res.message() << "\nExpanded from:\n" + << templateBody << "\nChecking result:\n" + << result; + } + // An alternative to ExpandAndCheck, which uses the |templateBody| as the // match pattern for the disassembled linked result. void ExpandAndMatch( @@ -165,15 +178,7 @@ EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n" << templateBody; if (res == SPV_SUCCESS) { - std::string result; - EXPECT_TRUE( - tools_.Disassemble(linked_binary, &result, disassemble_options_)) - << GetErrorMessage(); - auto match_res = effcee::Match(result, templateBody); - EXPECT_EQ(effcee::Result::Status::Ok, match_res.status()) - << match_res.message() << "\nExpanded from:\n" - << templateBody << "\nChecking result:\n" - << result; + Match(templateBody, linked_binary); } }
diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp index c7c962f..db94cb8 100644 --- a/test/link/matching_imports_to_exports_test.cpp +++ b/test/link/matching_imports_to_exports_test.cpp
@@ -15,6 +15,7 @@ #include <string> #include "gmock/gmock.h" +#include "gtest/gtest.h" #include "test/link/linker_fixture.h" namespace spvtools { @@ -904,5 +905,125 @@ } } +TEST_F(MatchingImportsToExports, LinkOnceODRLinkageVarSingle) { + const std::string body1 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" LinkOnceODR +%2 = OpTypeFloat 32 +%3 = OpConstant %2 3.1415 +%1 = OpVariable %2 Uniform %3 +)"; + + const std::string body2 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +)"; + + const std::string matchTemplate = R"( +; CHECK-NOT: OpDecorate {{.*}} Import +; CHECK-NOT: OpDecorate {{.*}} LinkOnceODR +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + Match(matchTemplate, linked_binary); +} + +TEST_F(MatchingImportsToExports, LinkOnceODRLinkageFunMultiple) { + const std::string body1 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" LinkOnceODR +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 Inline %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string body2 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 None %3 +OpFunctionEnd +)"; + + const std::string matchTemplate = R"( +; CHECK-NOT: OpDecorate {{.*}} Import +; CHECK-NOT: OpDecorate {{.*}} LinkOnceODR +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body1, body2}, &linked_binary)) + << GetErrorMessage(); + Match(matchTemplate, linked_binary); +} + +TEST_F(MatchingImportsToExports, LinkOnceODRAndExport) { + const std::string body1 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" LinkOnceODR +%2 = OpTypeFloat 32 +%3 = OpConstant %2 3.1415 +%1 = OpVariable %2 Uniform %3 +)"; + + const std::string body2 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 2.7183 +%1 = OpVariable %2 Uniform %3 +)"; + + const std::string body3 = R"( +OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpExtension "SPV_KHR_linkonce_odr" +OpMemoryModel Physical64 OpenCL +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +)"; + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2, body3}, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Combination of Export and LinkOnceODR is not allowed, found " + "for \"foo\"")); +} + } // namespace } // namespace spvtools
diff --git a/test/op_unknown_test.cpp b/test/op_unknown_test.cpp new file mode 100644 index 0000000..d41174e --- /dev/null +++ b/test/op_unknown_test.cpp
@@ -0,0 +1,128 @@ +// Copyright (c) 2025 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 <cassert> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/util/bitutils.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace utils { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using spvtest::ScopedContext; +using spvtest::TextToBinaryTest; +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::StrEq; + +using OpUnknownTest = TextToBinaryTest; + +TEST_F(OpUnknownTest, OpUnknown) { + SetText("OpUnknown(255, 1)"); + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str, + text.length, &binary, &diagnostic)); + EXPECT_EQ(0x000100FFu, binary->code[5]); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + } +} + +TEST_F(OpUnknownTest, HandlesOperands) { + EXPECT_THAT(CompiledInstructions("OpUnknown(24, 4) %a %b %123"), + Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3}))); + EXPECT_THAT(CompiledInstructions("OpUnknown(24, 4) !1 %b %123"), + Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 1, 2}))); +} + +TEST_F(OpUnknownTest, HandlesWhitespace) { + EXPECT_THAT(CompiledInstructions("OpUnknown ( 24 , 4 ) %a %b %123"), + Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3}))); + EXPECT_THAT(CompiledInstructions("OpUnknown(24,4) %a %b %123"), + Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3}))); +} + +TEST_F(OpUnknownTest, MultipleInstructions) { + EXPECT_THAT( + CompiledInstructions( + "%a = OpTypeFunction %b\nOpUnknown(21, 5) %c %d 32 1\nOpNop"), + Eq(Concatenate({MakeInstruction(spv::Op::OpTypeFunction, {1, 2}), + MakeInstruction(spv::Op::OpTypeInt, {3, 4, 32, 1}), + MakeInstruction(spv::Op::OpNop, {})}))); +} + +TEST_F(OpUnknownTest, OpUnknownInAssignment) { + EXPECT_EQ( + "OpUnknown not allowed in assignment. Use an explicit result id operand " + "instead.", + CompileFailure("%2 = OpUnknown(22, 3) 32")); + EXPECT_EQ("OpUnknown not allowed before =.", + CompileFailure("OpUnknown(22, 3) = OpTypeFloat 32")); +} + +TEST_F(OpUnknownTest, ParsingErrors) { + EXPECT_EQ("Expected '(', found end of stream.", CompileFailure("OpUnknown")); + EXPECT_EQ("'(' expected after OpUnknown but found 'a'.", + CompileFailure("OpUnknown abc")); + + EXPECT_EQ("Expected opcode enumerant, found end of stream.", + CompileFailure("OpUnknown(")); + EXPECT_EQ("Invalid opcode enumerant: \"abc\".", + CompileFailure("OpUnknown(abc")); + // Opcode enumerant must fit in 16 bits. + EXPECT_EQ("Invalid opcode enumerant: \"70000\".", + CompileFailure("OpUnknown(70000")); + + EXPECT_EQ("Expected ',', found end of stream.", + CompileFailure("OpUnknown(22")); + EXPECT_EQ("',' expected after opcode enumerant but found 'a'.", + CompileFailure("OpUnknown(22 abc")); + + EXPECT_EQ("Expected number of words, found end of stream.", + CompileFailure("OpUnknown(22,")); + EXPECT_EQ("Invalid number of words: \"abc\".", + CompileFailure("OpUnknown(22, abc")); + // Number of words must fit in 16 bits. + EXPECT_EQ("Invalid number of words: \"70000\".", + CompileFailure("OpUnknown(22, 70000")); + EXPECT_EQ( + "Number of words (which includes the opcode) must be greater than zero.", + CompileFailure("OpUnknown(22, 0")); + + EXPECT_EQ("Expected ')', found end of stream.", + CompileFailure("OpUnknown(22, 3")); + EXPECT_EQ("')' expected after number of words but found 'a'.", + CompileFailure("OpUnknown(22, 3 abc")); + + EXPECT_EQ( + "Unexpected start of new instruction: \"OpNop\". Expected 2 more " + "operands", + CompileFailure("OpUnknown(22, 3) OpNop")); + EXPECT_EQ("Expected 2 more operands, found end of stream.", + CompileFailure("OpUnknown(22, 3)")); + + EXPECT_EQ(CompileFailure("OpUnknown(21, 4) %c %d 32 1"), + "Expected <opcode> or <result-id> at the beginning of an " + "instruction, found '1'."); +} + +} // namespace +} // namespace utils +} // namespace spvtools
diff --git a/test/opcode_lookup_test.cpp b/test/opcode_lookup_test.cpp new file mode 100644 index 0000000..c3d2dd1 --- /dev/null +++ b/test/opcode_lookup_test.cpp
@@ -0,0 +1,232 @@ +// Copyright 2025 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 <array> +#include <iostream> + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "source/table2.h" +#include "test/unit_spirv.h" + +using ::testing::ContainerEq; +using ::testing::ValuesIn; + +namespace spvtools { +namespace { + +struct OpcodeLookupCase { + std::string name; + uint32_t opcode; + bool expect_pass = true; +}; + +std::ostream& operator<<(std::ostream& os, const OpcodeLookupCase& olc) { + os << "OLC('" << olc.name << "', " << olc.opcode << ", expect pass? " + << olc.expect_pass << ")"; + return os; +} + +using OpcodeLookupTest = ::testing::TestWithParam<OpcodeLookupCase>; + +TEST_P(OpcodeLookupTest, OpcodeLookup_ByName) { + const InstructionDesc* desc = nullptr; + auto status = LookupOpcode(GetParam().name.data(), &desc); + if (GetParam().expect_pass) { + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(static_cast<uint32_t>(desc->opcode), GetParam().opcode); + } else { + EXPECT_NE(status, SPV_SUCCESS); + EXPECT_EQ(desc, nullptr); + } +} + +TEST_P(OpcodeLookupTest, OpcodeLookup_ByOpcode_Success) { + const InstructionDesc* desc = nullptr; + if (GetParam().expect_pass) { + spv::Op opcode = static_cast<spv::Op>(GetParam().opcode); + auto status = LookupOpcode(opcode, &desc); + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(desc->opcode, opcode); + } +} + +INSTANTIATE_TEST_SUITE_P(Samples, OpcodeLookupTest, + ValuesIn(std::vector<OpcodeLookupCase>{ + {"Nop", 0}, + {"WritePipe", 275}, + {"TypeAccelerationStructureKHR", 5341}, + {"TypeAccelerationStructureNV", 5341}, + {"does not exist", 0, false}, + {"CopyLogical", 400}, + {"FPGARegINTEL", 5949}, + {"SubgroupMatrixMultiplyAccumulateINTEL", 6237}, + })); + +TEST(OpcodeLookupSingleTest, OpcodeLookup_ByOpcode_Fails) { + // This list may need adjusting over time. + std::array<uint32_t, 3> bad_opcodes = {{99999, 37737, 110101}}; + for (auto bad_opcode : bad_opcodes) { + const InstructionDesc* desc = nullptr; + spv::Op opcode = static_cast<spv::Op>(bad_opcode); + auto status = LookupOpcode(opcode, &desc); + EXPECT_NE(status, SPV_SUCCESS); + ASSERT_EQ(desc, nullptr); + } +} + +struct OpcodeLookupEnvCase { + std::string name; + uint32_t opcode; + spv_target_env env = SPV_ENV_UNIVERSAL_1_0; + bool expect_pass = true; +}; + +std::ostream& operator<<(std::ostream& os, const OpcodeLookupEnvCase& olec) { + os << "OLC('" << olec.name << "', " << olec.opcode << ", env " + << spvTargetEnvDescription(olec.env) << ", expect pass? " + << olec.expect_pass << ")"; + return os; +} + +using OpcodeLookupEnvTest = ::testing::TestWithParam<OpcodeLookupEnvCase>; + +TEST_P(OpcodeLookupEnvTest, OpcodeLookupForEnv_ByName) { + const InstructionDesc* desc = nullptr; + auto status = + LookupOpcodeForEnv(GetParam().env, GetParam().name.data(), &desc); + if (GetParam().expect_pass) { + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(static_cast<uint32_t>(desc->opcode), GetParam().opcode); + } else { + EXPECT_NE(status, SPV_SUCCESS); + EXPECT_EQ(desc, nullptr); + } +} + +TEST_P(OpcodeLookupEnvTest, OpcodeLookupForEnv_ByOpcode) { + const InstructionDesc* desc = nullptr; + spv::Op opcode = static_cast<spv::Op>(GetParam().opcode); + auto status = LookupOpcodeForEnv(GetParam().env, opcode, &desc); + if (GetParam().expect_pass) { + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(desc->opcode, opcode); + } else { + // Skip nonsense cases created for the lookup-by-name case. + if (GetParam().name != "does not exist") { + EXPECT_NE(status, SPV_SUCCESS); + EXPECT_EQ(desc, nullptr); + } + } +} + +INSTANTIATE_TEST_SUITE_P(Samples, OpcodeLookupEnvTest, + ValuesIn(std::vector<OpcodeLookupEnvCase>{ + {"Nop", 0}, + {"WritePipe", 275}, + {"TypeAccelerationStructureKHR", 5341}, + {"TypeAccelerationStructureNV", 5341}, + {"does not exist", 0, SPV_ENV_UNIVERSAL_1_0, + false}, + {"CopyLogical", 400, SPV_ENV_UNIVERSAL_1_0, false}, + {"CopyLogical", 400, SPV_ENV_UNIVERSAL_1_3, false}, + {"CopyLogical", 400, SPV_ENV_UNIVERSAL_1_4, true}, + {"FPGARegINTEL", 5949}, + {"SubgroupMatrixMultiplyAccumulateINTEL", 6237}, + })); + +TEST(OpcodeLookupExtInstTest, Operands) { + // The SPIR-V spec grammar has a single rule for OpExtInst, where the last + // item is "sequence of Ids". SPIRV-Tools handles it differently. It drops + // that last item, and instead specifies those operands as operands of the + // extended instruction enum, such as 'cos'. + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/233 + // Test the exact sequence of operand types extracted for OpExtInst. + const InstructionDesc* desc = nullptr; + auto status = LookupOpcode("ExtInst", &desc); + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + + EXPECT_EQ(desc->operands_range.count(), 4u); + + auto operands = desc->operands(); + using vtype = std::vector<spv_operand_type_t>; + + EXPECT_THAT( + vtype(operands.begin(), operands.end()), + ContainerEq(vtype{SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER})); +} + +// Test printingClass + +struct OpcodePrintingClassCase { + std::string name; + PrintingClass expected; +}; + +std::ostream& operator<<(std::ostream& os, + const OpcodePrintingClassCase& opcc) { + os << "OPCC('" << opcc.name << "', " << static_cast<int>(opcc.expected) + << ")"; + return os; +} + +using OpcodePrintingClassTest = + ::testing::TestWithParam<OpcodePrintingClassCase>; + +TEST_P(OpcodePrintingClassTest, OpcodeLookup_ByName) { + const InstructionDesc* desc = nullptr; + auto status = LookupOpcode(GetParam().name.data(), &desc); + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(desc->printingClass, GetParam().expected); +} + +INSTANTIATE_TEST_SUITE_P( + Samples, OpcodePrintingClassTest, + ValuesIn(std::vector<OpcodePrintingClassCase>{ + {"ConstantFunctionPointerINTEL", PrintingClass::k_exclude}, + {"Nop", PrintingClass::kMiscellaneous}, + {"SourceContinued", PrintingClass::kDebug}, + {"Decorate", PrintingClass::kAnnotation}, + {"Extension", PrintingClass::kExtension}, + {"MemoryModel", PrintingClass::kMode_Setting}, + {"Variable", PrintingClass::kMemory}, + {"CooperativeMatrixPerElementOpNV", PrintingClass::kFunction}, + {"SampledImage", PrintingClass::kImage}, + {"ConvertFToU", PrintingClass::kConversion}, + {"VectorExtractDynamic", PrintingClass::kComposite}, + {"IAdd", PrintingClass::kArithmetic}, + {"ShiftRightLogical", PrintingClass::kBit}, + {"Any", PrintingClass::kRelational_and_Logical}, + {"DPdx", PrintingClass::kDerivative}, + {"Branch", PrintingClass::kControl_Flow}, + {"AtomicLoad", PrintingClass::kAtomic}, + {"ControlBarrier", PrintingClass::kBarrier}, + {"GroupAll", PrintingClass::kGroup}, + {"EnqueueMarker", PrintingClass::kDevice_Side_Enqueue}, + {"ReadPipe", PrintingClass::kPipe}, + {"GroupNonUniformElect", PrintingClass::kNon_Uniform}, + // Skipping "Reserved" because it's probably an + // unstable class. + })); + +} // namespace +} // namespace spvtools
diff --git a/test/opcode_require_capabilities_test.cpp b/test/opcode_require_capabilities_test.cpp index 615c094..3694f14 100644 --- a/test/opcode_require_capabilities_test.cpp +++ b/test/opcode_require_capabilities_test.cpp
@@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "test/unit_spirv.h" - #include "source/enum_set.h" +#include "source/table2.h" +#include "test/unit_spirv.h" namespace spvtools { namespace { @@ -31,15 +31,12 @@ ::testing::TestWithParam<ExpectedOpCodeCapabilities>; TEST_P(OpcodeTableCapabilitiesTest, TableEntryMatchesExpectedCapabilities) { - auto env = SPV_ENV_UNIVERSAL_1_1; - spv_opcode_table opcodeTable; - ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&opcodeTable, env)); - spv_opcode_desc entry; - ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableValueLookup(env, opcodeTable, - GetParam().opcode, &entry)); - EXPECT_EQ( - ElementsIn(GetParam().capabilities), - ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities))); + const spvtools::InstructionDesc* desc = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvtools::LookupOpcode(GetParam().opcode, &desc)); + auto caps = desc->capabilities(); + EXPECT_EQ(ElementsIn(GetParam().capabilities), + ElementsIn(CapabilitySet(static_cast<uint32_t>(caps.size()), + caps.data()))); } INSTANTIATE_TEST_SUITE_P(
diff --git a/test/opcode_table_get_test.cpp b/test/opcode_table_get_test.cpp deleted file mode 100644 index 4ff67d9..0000000 --- a/test/opcode_table_get_test.cpp +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright (c) 2015-2016 The Khronos Group Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "gmock/gmock.h" -#include "test/unit_spirv.h" - -namespace spvtools { -namespace { - -using GetTargetOpcodeTableGetTest = ::testing::TestWithParam<spv_target_env>; -using ::testing::ValuesIn; - -TEST_P(GetTargetOpcodeTableGetTest, IntegrityCheck) { - spv_opcode_table table; - ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&table, GetParam())); - ASSERT_NE(0u, table->count); - ASSERT_NE(nullptr, table->entries); -} - -TEST_P(GetTargetOpcodeTableGetTest, InvalidPointerTable) { - ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOpcodeTableGet(nullptr, GetParam())); -} - -INSTANTIATE_TEST_SUITE_P(OpcodeTableGet, GetTargetOpcodeTableGetTest, - ValuesIn(spvtest::AllTargetEnvironments())); - -} // namespace -} // namespace spvtools
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 53dbe07..cecde9e 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp
@@ -23,6 +23,7 @@ #include "source/operand.h" #include "source/spirv_target_env.h" #include "source/table.h" +#include "source/table2.h" #include "spirv-tools/libspirv.h" #include "test/unit_spirv.h" @@ -80,13 +81,13 @@ const auto env = std::get<0>(GetParam()); const auto context = spvContextCreate(env); const AssemblyGrammar grammar(context); - spv_operand_desc entry; + const spvtools::OperandDesc* entry = nullptr; ASSERT_EQ(SPV_SUCCESS, - grammar.lookupOperand(std::get<1>(GetParam()).type, - std::get<1>(GetParam()).value, &entry)); - const auto cap_set = grammar.filterCapsAgainstTargetEnv( - entry->capabilities, entry->numCapabilities); + spvtools::LookupOperand(std::get<1>(GetParam()).type, + std::get<1>(GetParam()).value, &entry)); + const auto cap_set = + grammar.filterCapsAgainstTargetEnv(entry->capabilities()); EXPECT_THAT(ElementsIn(cap_set), Eq(ElementsIn(std::get<1>(GetParam()).expected_capabilities)))
diff --git a/test/operand_lookup_test.cpp b/test/operand_lookup_test.cpp new file mode 100644 index 0000000..8fbf7bf --- /dev/null +++ b/test/operand_lookup_test.cpp
@@ -0,0 +1,133 @@ +// Copyright 2025 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 <array> +#include <iostream> + +#include "gmock/gmock.h" +#include "source/operand.h" +#include "source/table2.h" +#include "spirv-tools/libspirv.h" +#include "test/unit_spirv.h" + +using ::testing::ContainerEq; +using ::testing::ValuesIn; + +namespace spvtools { +namespace { + +struct OperandLookupCase { + spv_operand_type_t type; + std::string name; + size_t name_length; + uint32_t value; + bool expect_pass = true; +}; + +std::ostream& operator<<(std::ostream& os, const OperandLookupCase& olc) { + os << "OLC('" << spvOperandTypeStr(olc.type) << " '" << olc.name + << "', len:" << olc.name_length << ", value:" << olc.value + << ", expect pass? " << olc.expect_pass << ")"; + return os; +} + +using OperandLookupTest = ::testing::TestWithParam<OperandLookupCase>; + +TEST_P(OperandLookupTest, OperandLookup_ByName) { + const OperandDesc* desc = nullptr; + auto status = LookupOperand(GetParam().type, GetParam().name.data(), + GetParam().name_length, &desc); + if (GetParam().expect_pass) { + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(desc->value, GetParam().value); + } else { + EXPECT_NE(status, SPV_SUCCESS); + EXPECT_EQ(desc, nullptr); + } +} + +TEST_P(OperandLookupTest, OperandLookup_ByValue_Success) { + const OperandDesc* desc = nullptr; + if (GetParam().expect_pass) { + const auto value = GetParam().value; + auto status = LookupOperand(GetParam().type, GetParam().value, &desc); + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + EXPECT_EQ(desc->value, value); + } +} + +INSTANTIATE_TEST_SUITE_P( + Samples, OperandLookupTest, + ValuesIn(std::vector<OperandLookupCase>{ + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed", 7, 0}, + // "None" is an alias for "Relaxed" + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "None", 4, 0}, + // "NonPrivatePointer" is the canonical name that appeared + // in an extension and incorporated in SPIR-V 1.5. + {SPV_OPERAND_TYPE_MEMORY_ACCESS, "NonPrivatePointer", 17, 32}, + // "NonPrivatePointerKHR" is the name from the extension. + {SPV_OPERAND_TYPE_MEMORY_ACCESS, "NonPrivatePointerKHR", 20, 32}, + // "NoAliasINTELMask" is only in an extension + {SPV_OPERAND_TYPE_MEMORY_ACCESS, "NoAliasINTELMask", 16, 0x20000}, + {SPV_OPERAND_TYPE_RAY_FLAGS, "TerminateOnFirstHitKHR", 22, 4}, + {SPV_OPERAND_TYPE_FPENCODING, "BFloat16KHR", 11, 0}, + // Lookup on An optional operand type should match the base lookup. + {SPV_OPERAND_TYPE_OPTIONAL_FPENCODING, "BFloat16KHR", 11, 0}, + // Lookup is type-specific. + {SPV_OPERAND_TYPE_OPTIONAL_FPENCODING, "Relaxed", 7, 0, false}, + // Invalid string + {SPV_OPERAND_TYPE_RAY_FLAGS, "does_not_exist", 14, 0, false}, + // Check lengths + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed", 6, 0, false}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed", 7, 0, true}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed|", 7, 0, true}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed|", 8, 0, false}, + })); + +TEST(OperandLookupSingleTest, OperandLookup_ByValue_Fails) { + // This list may need adjusting over time. + std::array<spv_operand_type_t, 3> types = { + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, SPV_OPERAND_TYPE_RAY_FLAGS, + SPV_OPERAND_TYPE_FPENCODING}}; + std::array<uint32_t, 3> bad_values = {{99999, 37737, 110101}}; + for (auto type : types) { + for (auto bad_value : bad_values) { + const OperandDesc* desc = nullptr; + auto status = LookupOperand(type, bad_value, &desc); + EXPECT_NE(status, SPV_SUCCESS); + ASSERT_EQ(desc, nullptr); + } + } +} + +TEST(OperandLookupOperands, Sample) { + // Check the operand list for a valid operand lookup. + const OperandDesc* desc = nullptr; + auto status = LookupOperand(SPV_OPERAND_TYPE_IMAGE, "Grad", 4, &desc); + EXPECT_EQ(status, SPV_SUCCESS); + ASSERT_NE(desc, nullptr); + + EXPECT_EQ(desc->operands_range.count(), 2u); + + auto operands = desc->operands(); + using vtype = std::vector<spv_operand_type_t>; + + EXPECT_THAT(vtype(operands.begin(), operands.end()), + ContainerEq(vtype{SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID})); +} + +} // namespace +} // namespace spvtools
diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp index 58b8a08..2138c3f 100644 --- a/test/operand_pattern_test.cpp +++ b/test/operand_pattern_test.cpp
@@ -69,13 +69,8 @@ using MaskExpansionTest = ::testing::TestWithParam<MaskExpansionCase>; TEST_P(MaskExpansionTest, Sample) { - spv_operand_table operandTable = nullptr; - auto env = SPV_ENV_UNIVERSAL_1_0; - ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable, env)); - spv_operand_pattern_t pattern(GetParam().initial); - spvPushOperandTypesForMask(env, operandTable, GetParam().type, - GetParam().mask, &pattern); + spvPushOperandTypesForMask(GetParam().type, GetParam().mask, &pattern); EXPECT_THAT(pattern, Eq(GetParam().expected)); }
diff --git a/test/operand_test.cpp b/test/operand_test.cpp index ec45da5..4cc0818 100644 --- a/test/operand_test.cpp +++ b/test/operand_test.cpp
@@ -19,25 +19,8 @@ namespace spvtools { namespace { -using GetTargetTest = ::testing::TestWithParam<spv_target_env>; using ::testing::ValuesIn; -TEST_P(GetTargetTest, Default) { - spv_operand_table table; - ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&table, GetParam())); - ASSERT_NE(0u, table->count); - ASSERT_NE(nullptr, table->types); -} - -TEST_P(GetTargetTest, InvalidPointerTable) { - ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOperandTableGet(nullptr, GetParam())); -} - -INSTANTIATE_TEST_SUITE_P(OperandTableGet, GetTargetTest, - ValuesIn(std::vector<spv_target_env>{ - SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, - SPV_ENV_VULKAN_1_0})); - TEST(OperandString, AllAreDefinedExceptVariable) { // None has no string, so don't test it. EXPECT_EQ(0u, SPV_OPERAND_TYPE_NONE);
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 4ef119d..e2fdccf 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt
@@ -70,6 +70,7 @@ ir_loader_test.cpp iterator_test.cpp line_debug_info_test.cpp + legalize_multidim_array_test.cpp local_access_chain_convert_test.cpp local_redundancy_elimination_test.cpp local_single_block_elim.cpp @@ -88,16 +89,19 @@ propagator_test.cpp reduce_load_size_test.cpp redundancy_elimination_test.cpp + canonicalize_ids_test.cpp remove_dontinline_test.cpp 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 + resolve_binding_conflicts_pass_test.cpp scalar_analysis.cpp scalar_replacement_test.cpp set_spec_const_default_value_test.cpp simplification_test.cpp + split_combined_image_sampler_pass_test.cpp spread_volatile_semantics_test.cpp strength_reduction_test.cpp strip_debug_info_test.cpp @@ -120,10 +124,9 @@ ) if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main) if (MSVC) - if (${MSVC_VERSION} LESS 1920) - # The VS 2017 debug build requires /bigobj on test_opt - # https://github.com/KhronosGroup/SPIRV-Tools/issues/5335 - target_compile_options(test_opt PRIVATE /bigobj) - endif() + # MSVC builds (e.g., VS 2017, VS 2022) require /bigobj on test_opt to avoid + # exceeding the object file section limit. + # https://github.com/KhronosGroup/SPIRV-Tools/issues/5335 + target_compile_options(test_opt PRIVATE /bigobj) endif() endif()
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 5b88117..2763afb 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -16,6 +16,7 @@ #include <string> #include <vector> +#include "gmock/gmock.h" #include "test/opt/assembly_builder.h" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" @@ -26,6 +27,8 @@ using AggressiveDCETest = PassTest<::testing::Test>; +using ::testing::HasSubstr; + TEST_F(AggressiveDCETest, EliminateExtendedInst) { // #version 140 // @@ -6754,79 +6757,78 @@ %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 + %75 = OpExtInst %void %1 DebugInfoNone + %76 = OpExtInst %void %1 DebugExpression + %77 = OpExtInst %void %1 DebugOperation %uint_0 + %78 = OpExtInst %void %1 DebugExpression %77 + %79 = OpExtInst %void %1 DebugSource %7 + %80 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %79 %uint_5 + %81 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0 + %82 = OpExtInst %void %1 DebugTypeVector %81 %uint_4 + %83 = OpExtInst %void %1 DebugTypeMember %10 %82 %79 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3 + %84 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %79 %uint_10 %uint_1 %80 %8 %uint_128 %uint_3 %83 + %85 = OpExtInst %void %1 DebugTypeVector %81 %uint_2 + %86 = OpExtInst %void %1 DebugTypeMember %12 %85 %79 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3 + %87 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %79 %uint_5 %uint_1 %80 %11 %uint_64 %uint_3 %86 + %88 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %79 %uint_0 %uint_0 %80 %14 %75 %uint_3 + %89 = OpExtInst %void %1 DebugTypeFunction %uint_3 %84 %87 + %90 = OpExtInst %void %1 DebugFunction %16 %89 %79 %uint_15 %uint_1 %80 %16 %uint_3 %uint_16 + %91 = OpExtInst %void %1 DebugLexicalBlock %79 %uint_16 %uint_1 %90 + %92 = OpExtInst %void %1 DebugLocalVariable %17 %85 %79 %uint_19 %uint_12 %91 %uint_4 + %93 = OpExtInst %void %1 DebugLocalVariable %18 %84 %79 %uint_17 %uint_15 %91 %uint_4 + %94 = OpExtInst %void %1 DebugLocalVariable %19 %87 %79 %uint_15 %uint_29 %90 %uint_4 %uint_1 + %95 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %79 %uint_0 %uint_0 %80 %21 %75 %uint_3 + %96 = OpExtInst %void %1 DebugGlobalVariable %22 %95 %79 %uint_3 %uint_14 %80 %22 %g_sAniso %uint_8 + %97 = OpExtInst %void %1 DebugGlobalVariable %23 %88 %79 %uint_1 %uint_11 %80 %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 + %98 = OpLabel + %99 = OpVariable %_ptr_Function_PS_OUTPUT Function + %100 = OpVariable %_ptr_Function_v2float Function + %101 = 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 + %102 = OpLoad %v2float %in_var_TEXCOORD2 + %103 = OpCompositeConstruct %PS_INPUT %102 + OpStore %param_var_i %103 + %104 = OpExtInst %void %1 DebugFunctionDefinition %90 %MainPs + %142 = OpExtInst %void %1 DebugScope %90 + %106 = OpExtInst %void %1 DebugDeclare %94 %param_var_i %76 + %143 = OpExtInst %void %1 DebugScope %91 + %108 = OpExtInst %void %1 DebugDeclare %93 %99 %76 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugFunctionDefinition %90 %MainPs +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %90 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %94 %param_var_i %76 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %91 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %93 %99 %76 + %109 = OpExtInst %void %1 DebugLine %79 %uint_19 %uint_19 %uint_17 %uint_30 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %79 %uint_19 %uint_19 %uint_17 %uint_30 + %110 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %111 = OpLoad %v2float %110 + %112 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_32 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_32 + OpStore %100 %111 +;CHECK-NOT: OpStore %100 %111 + %114 = OpExtInst %void %1 DebugValue %92 %111 %76 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %92 %111 %76 + %115 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_32 + %116 = OpLoad %type_2d_image %g_tColor + %117 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_41 %uint_48 + %118 = OpLoad %type_sampler %g_sAniso + %119 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_53 + %120 = OpSampledImage %type_sampled_image %116 %118 + %121 = OpImageSampleImplicitLod %v4float %120 %111 None + %122 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_5 %uint_53 + %123 = OpAccessChain %_ptr_Function_v4float %99 %int_0 + OpStore %123 %121 + %124 = OpExtInst %void %1 DebugLine %79 %uint_21 %uint_21 %uint_12 %uint_20 + %125 = OpLoad %PS_OUTPUT %99 + %126 = OpExtInst %void %1 DebugLine %79 %uint_21 %uint_21 %uint_5 %uint_20 + OpStore %101 %125 + %127 = OpExtInst %void %1 DebugNoLine ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine - %111 = OpExtInst %void %1 DebugNoScope + %144 = OpExtInst %void %1 DebugNoScope ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope - %100 = OpCompositeExtract %v4float %97 0 - OpStore %out_var_SV_Target0 %100 + %129 = OpCompositeExtract %v4float %125 0 + OpStore %out_var_SV_Target0 %129 OpReturn OpFunctionEnd )"; @@ -7695,8 +7697,7 @@ } ; " - %6 = OpString " -PS_OUTPUT MainPs ( ) + %6 = OpString "PS_OUTPUT MainPs ( ) { PS_OUTPUT ps_output ; ps_output . vColor = float4( 1.0, 0.0, 0.0, 0.0 ); @@ -7999,8 +8000,10 @@ const std::string text = R"( OpCapability CooperativeMatrixKHR OpCapability Shader + OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" - OpMemoryModel Logical GLSL450 + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %1 "main" %2 OpExecutionMode %1 LocalSize 64 1 1 OpSource HLSL 600 @@ -8233,6 +8236,1027 @@ SinglePassRunAndCheck<AggressiveDCEPass>(spirv, spirv, true, false); } +TEST_F(AggressiveDCETest, NoEliminateOpSource) { + // Should not eliminate OpSource + + const std::string text = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%4 = OpString "D:\\directxshadercompiler\\tools\\clang\\test\\CodeGenSPIRV\\spirv.debug.opsource.include.hlsl" +%5 = OpString "D:\\directxshadercompiler\\tools\\clang\\test\\CodeGenSPIRV/spirv.debug.opsource.include-file.hlsli" +OpSource HLSL 600 %4 "// RUN: %dxc -T ps_6_0 -E main -Zi %s -spirv | FileCheck %s +#include \"spirv.debug.opsource.include-file.hlsli\" + +struct ColorType +{ + float4 position : SV_POSITION; + float4 color : COLOR; +}; + +float4 main(UBER_TYPE(Color) input) : SV_TARGET +{ + return input.color; +} +" +OpSource HLSL 600 %5 "#define UBER_TYPE(x) x ## Type +" +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 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%11 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +OpLine %4 22 1 +%main = OpFunction %void None %11 +OpNoLine +%12 = OpLabel +OpLine %4 22 1 +%13 = OpLoad %v4float %in_var_COLOR +OpStore %out_var_SV_TARGET %13 +OpLine %4 25 1 +OpReturn +OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble<AggressiveDCEPass>( + text, /* skip_nop = */ true, /* skip_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT( + output, + HasSubstr("OpSource HLSL 600 %5 \"#define UBER_TYPE(x) x ## Type")); +} + +TEST_F(AggressiveDCETest, EliminateCopyLogical) { + const std::string before = R"( +; CHECK: [[float32:%\w+]] = OpTypeFloat 32 +; CHECK: [[v4float:%\w+]] = OpTypeVector [[float32]] 4 +; CHECK-NOT: %10 = OpTypeArray [[v4float]] %9 +; CHECK-NOT: %11 = OpTypeStruct %10 %10 +; CHECK-NOT: %22 = OpTypePointer Uniform %16 +; CHECK-NOT: %38 = OpTypePointer Function [[v4float]] +; CHECK-NOT: %43 = OpTypePointer Function %10 +; CHECK-NOT: %44 = OpVariable %42 Function +; CHECK-NOT: %23 = OpAccessChain %22 %19 %21 +; CHECK-NOT: %24 = OpLoad %16 %23 +; CHECK-NOT: %25 = OpCopyLogical %11 %24 +; CHECK-NOT: %46 = OpCompositeExtract %10 %25 0 +; CHECK-NOT: OpStore %44 %46 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %19 %30 %32 + OpSource GLSL 430 + OpName %4 "main" + OpDecorate %14 ArrayStride 16 + OpDecorate %15 ArrayStride 16 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 32 + OpDecorate %17 Block + OpMemberDecorate %17 0 Offset 0 + OpDecorate %19 Binding 0 + OpDecorate %19 DescriptorSet 0 + OpDecorate %28 Block + OpMemberDecorate %28 0 BuiltIn Position + OpMemberDecorate %28 1 BuiltIn PointSize + OpMemberDecorate %28 2 BuiltIn ClipDistance + OpDecorate %32 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 2 +%10 = OpTypeArray %7 %9 +%11 = OpTypeStruct %10 %10 +%14 = OpTypeArray %7 %9 +%15 = OpTypeArray %7 %9 +%16 = OpTypeStruct %14 %15 +%17 = OpTypeStruct %16 +%18 = OpTypePointer Uniform %17 +%19 = OpVariable %18 Uniform +%20 = OpTypeInt 32 1 +%21 = OpConstant %20 0 +%22 = OpTypePointer Uniform %16 +%26 = OpConstant %8 1 +%27 = OpTypeArray %6 %26 +%28 = OpTypeStruct %7 %6 %27 +%29 = OpTypePointer Output %28 +%30 = OpVariable %29 Output +%31 = OpTypePointer Input %7 +%32 = OpVariable %31 Input +%33 = OpConstant %8 0 +%34 = OpTypePointer Input %6 +%38 = OpTypePointer Function %7 +%41 = OpTypePointer Output %7 +%43 = OpTypePointer Function %10 +%48 = OpTypePointer Uniform %14 +%49 = OpTypePointer Uniform %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel +%44 = OpVariable %43 Function +%23 = OpAccessChain %22 %19 %21 +%24 = OpLoad %16 %23 +%25 = OpCopyLogical %11 %24 +%46 = OpCompositeExtract %10 %25 0 +%50 = OpAccessChain %48 %19 %21 %33 + OpStore %44 %46 +%35 = OpAccessChain %34 %32 %33 +%36 = OpLoad %6 %35 +%37 = OpConvertFToS %20 %36 +%47 = OpAccessChain %49 %50 %37 +%40 = OpLoad %7 %47 +%42 = OpAccessChain %41 %30 %21 + OpStore %42 %40 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch<AggressiveDCEPass>(before, true); +} + +TEST_F(AggressiveDCETest, KeepCopyLogical) { + const std::string before = R"( +; CHECK: OpCopyLogical + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %23 %38 + OpExecutionMode %4 LocalSize 32 32 1 + OpSource GLSL 430 + OpName %4 "main" + OpDecorate %10 ArrayStride 16 + OpDecorate %11 ArrayStride 16 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 1 Offset 2048 + OpDecorate %13 Block + OpMemberDecorate %13 0 NonReadable + OpMemberDecorate %13 0 Offset 0 + OpDecorate %15 NonReadable + OpDecorate %15 Binding 1 + OpDecorate %15 DescriptorSet 0 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 16 + OpMemberDecorate %20 0 Offset 0 + OpMemberDecorate %20 1 Offset 2048 + OpDecorate %21 Block + OpMemberDecorate %21 0 NonWritable + OpMemberDecorate %21 0 Offset 0 + OpDecorate %23 NonWritable + OpDecorate %23 Binding 0 + OpDecorate %23 DescriptorSet 0 + OpDecorate %30 ArrayStride 16 + OpDecorate %31 ArrayStride 16 + OpMemberDecorate %32 0 Offset 0 + OpMemberDecorate %32 1 Offset 2048 + OpDecorate %34 ArrayStride 4096 + OpMemberDecorate %35 0 Offset 0 + OpDecorate %36 Block + OpMemberDecorate %36 0 Offset 0 + OpDecorate %38 Binding 0 + OpDecorate %38 DescriptorSet 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 128 +%10 = OpTypeArray %7 %9 +%11 = OpTypeArray %7 %9 +%12 = OpTypeStruct %10 %11 +%13 = OpTypeStruct %12 +%14 = OpTypePointer StorageBuffer %13 +%15 = OpVariable %14 StorageBuffer +%16 = OpTypeInt 32 1 +%17 = OpConstant %16 0 +%18 = OpTypeArray %7 %9 +%19 = OpTypeArray %7 %9 +%20 = OpTypeStruct %18 %19 +%21 = OpTypeStruct %20 +%22 = OpTypePointer StorageBuffer %21 +%23 = OpVariable %22 StorageBuffer +%24 = OpTypePointer StorageBuffer %20 +%27 = OpTypePointer StorageBuffer %12 +%30 = OpTypeArray %7 %9 +%31 = OpTypeArray %7 %9 +%32 = OpTypeStruct %30 %31 +%33 = OpConstant %8 8 +%34 = OpTypeArray %32 %33 +%35 = OpTypeStruct %34 +%36 = OpTypeStruct %35 +%37 = OpTypePointer Uniform %36 +%38 = OpVariable %37 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel +%25 = OpAccessChain %24 %23 %17 +%26 = OpLoad %20 %25 +%28 = OpAccessChain %27 %15 %17 +%29 = OpCopyLogical %12 %26 + OpStore %28 %29 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch<AggressiveDCEPass>(before, true); +} +TEST_F(AggressiveDCETest, KeepOnlyLiveDebugValues) { + // DebugValue should replace dead Value with Undef. + const std::string before = + R"(OpCapability MinLod +OpCapability StorageImageWriteWithoutFormat +OpCapability StorageImageReadWithoutFormat +OpCapability FragmentShaderSampleInterlockEXT +OpCapability FragmentShaderPixelInterlockEXT +OpCapability FragmentShaderShadingRateInterlockEXT +OpCapability ComputeDerivativeGroupQuadsKHR +OpCapability ComputeDerivativeGroupLinearKHR +OpCapability RayQueryKHR +OpCapability GroupNonUniformPartitionedNV +OpCapability InterpolationFunction +OpCapability QuadControlKHR +OpCapability Shader +OpCapability SampledBuffer +OpCapability ImageBuffer +OpExtension "SPV_EXT_fragment_shader_interlock" +OpExtension "SPV_KHR_compute_shader_derivatives" +OpExtension "SPV_KHR_ray_query" +OpExtension "SPV_NV_shader_subgroup_partitioned" +OpExtension "SPV_KHR_quad_control" +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%3 = OpString "partially_unused_var.cs.hlsl" +%17 = OpString "int" +%25 = OpString "main" +%26 = OpString "" +%30 = OpString "a" +%33 = OpString "__dxc_setup" +%35 = OpString "cb000c74" +%36 = OpString " -E main -T cs_6_6 -spirv -fspv-print-all -fspv-debug=vulkan -Qembed_debug" +%39 = OpString "@type.buffer.image" +%40 = OpString "type.buffer.image" +%42 = OpString "TemplateParam" +%45 = OpString "b" +OpName %type_buffer_image "type.buffer.image" +OpName %b "b" +OpName %main "main" +OpDecorate %b DescriptorSet 0 +OpDecorate %b Binding 0 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%type_buffer_image = OpTypeImage %int Buffer 2 0 0 2 R32i +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_13 = OpConstant %uint 13 +%uint_7 = OpConstant %uint 7 +%uint_15 = OpConstant %uint 15 +%uint_8 = OpConstant %uint 8 +%49 = OpTypeFunction %void +%_arr_int_uint_3 = OpTypeArray %int %uint_3 +%_ptr_Function__arr_int_uint_3 = OpTypePointer Function %_arr_int_uint_3 +%v4int = OpTypeVector %int 4 +%uint_21 = OpConstant %uint 21 +%uint_27 = OpConstant %uint 27 +%uint_14 = OpConstant %uint 14 +%uint_31 = OpConstant %uint 31 +%_ptr_Function_int = OpTypePointer Function %int +%uint_6 = OpConstant %uint 6 +%uint_10 = OpConstant %uint 10 +%b = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +; CHECK: %281 = OpUndef %int +%38 = OpExtInst %void %1 DebugInfoNone +%16 = OpExtInst %void %1 DebugExpression +; CHECK: %216 = OpExtInst %void %1 DebugOperation %uint_0 +%216 = OpExtInst %void %1 DebugOperation %uint_0 +%18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_4 %uint_0 +%20 = OpExtInst %void %1 DebugTypeArray %18 %uint_3 +%21 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%22 = OpExtInst %void %1 DebugSource %3 +%23 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %22 %uint_5 +%27 = OpExtInst %void %1 DebugFunction %25 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4 +%28 = OpExtInst %void %1 DebugLexicalBlock %22 %uint_4 %uint_13 %27 +%31 = OpExtInst %void %1 DebugLocalVariable %30 %20 %22 %uint_5 %uint_7 %28 %uint_4 +%34 = OpExtInst %void %1 DebugFunction %33 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4 +%41 = OpExtInst %void %1 DebugTypeComposite %39 %uint_0 %22 %uint_0 %uint_0 %23 %40 %38 %uint_3 +%43 = OpExtInst %void %1 DebugTypeTemplateParameter %42 %18 %38 %22 %uint_0 %uint_0 +%44 = OpExtInst %void %1 DebugTypeTemplate %41 %43 +%46 = OpExtInst %void %1 DebugGlobalVariable %45 %44 %22 %uint_1 %uint_15 %23 %45 %b %uint_8 +%37 = OpExtInst %void %1 DebugEntryPoint %34 %23 %35 %36 +%138 = OpExtInst %void %1 DebugInlinedAt %uint_4 %34 +; CHECK: %215 = OpExtInst %void %1 DebugExpression %216 +%215 = OpExtInst %void %1 DebugExpression %216 +%main = OpFunction %void None %49 +%50 = OpLabel +%205 = OpVariable %_ptr_Function_int Function +%204 = OpVariable %_ptr_Function_int Function +%203 = OpVariable %_ptr_Function_int Function +%255 = OpExtInst %void %1 DebugScope %34 +%52 = OpExtInst %void %1 DebugFunctionDefinition %34 %main +%256 = OpExtInst %void %1 DebugScope %28 %138 +%155 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_15 %uint_15 +%141 = OpLoad %type_buffer_image %b +%142 = OpImageRead %v4int %141 %uint_1 None +%143 = OpCompositeExtract %int %142 0 +%158 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_21 %uint_21 +%144 = OpLoad %type_buffer_image %b +%145 = OpImageRead %v4int %144 %uint_2 None +%146 = OpCompositeExtract %int %145 0 +%161 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_27 %uint_27 +%147 = OpLoad %type_buffer_image %b +%148 = OpImageRead %v4int %147 %uint_3 None +%149 = OpCompositeExtract %int %148 0 +%164 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_14 %uint_31 +%150 = OpCompositeConstruct %_arr_int_uint_3 %143 %146 %149 +%207 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_3 %uint_31 +%206 = OpCompositeExtract %int %150 0 +; CHECK-NOT: OpStore %203 %206 +OpStore %203 %206 +; CHECK: %253 = OpExtInst %void %1 DebugValue %31 %206 %16 %int_0 +%253 = OpExtInst %void %1 DebugValue %31 %206 %16 %int_0 +; CHECK-NOT: %209 = OpCompositeExtract %int %150 1 +%209 = OpCompositeExtract %int %150 1 +; CHECK-NOT: OpStore %204 %209 +OpStore %204 %209 +; CHECK: %250 = OpExtInst %void %1 DebugValue %31 %281 %16 %int_1 +%250 = OpExtInst %void %1 DebugValue %31 %209 %16 %int_1 +; CHECK-NOT: %212 = OpCompositeExtract %int %150 2 +%212 = OpCompositeExtract %int %150 2 +; CHECK-NOT: OpStore %205 %212 +OpStore %205 %212 +; CHECK: %247 = OpExtInst %void %1 DebugValue %31 %281 %16 %int_2 +%247 = OpExtInst %void %1 DebugValue %31 %212 %16 %int_2 +%169 = OpExtInst %void %1 DebugLine %22 %uint_6 %uint_6 %uint_3 %uint_13 +%154 = OpLoad %type_buffer_image %b +OpImageWrite %154 %uint_0 %206 None +%257 = OpExtInst %void %1 DebugScope %34 +%55 = OpExtInst %void %1 DebugLine %22 %uint_7 %uint_7 %uint_1 %uint_1 +OpReturn +%258 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + 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<AggressiveDCEPass>(before, false); +} +TEST_F(AggressiveDCETest, KeepLocalDebugValue) { + // DebugValue should only be live when Value is live. + const std::string before = + R"( +OpCapability MinLod +OpCapability StorageImageWriteWithoutFormat +OpCapability StorageImageReadWithoutFormat +OpCapability FragmentShaderSampleInterlockEXT +OpCapability FragmentShaderPixelInterlockEXT +OpCapability FragmentShaderShadingRateInterlockEXT +OpCapability ComputeDerivativeGroupQuadsKHR +OpCapability ComputeDerivativeGroupLinearKHR +OpCapability RayQueryKHR +OpCapability GroupNonUniformPartitionedNV +OpCapability InterpolationFunction +OpCapability QuadControlKHR +OpCapability Shader +OpCapability SampledBuffer +OpCapability ImageBuffer +OpExtension "SPV_EXT_fragment_shader_interlock" +OpExtension "SPV_KHR_compute_shader_derivatives" +OpExtension "SPV_KHR_ray_query" +OpExtension "SPV_NV_shader_subgroup_partitioned" +OpExtension "SPV_KHR_quad_control" +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%3 = OpString "partially_unused_var2.cs.hlsl" +%17 = OpString "int" +%25 = OpString "main" +%26 = OpString "" +; CHECK: [[VarName:%\w+]] = OpString "some_real_obvious_name" +%30 = OpString "some_real_obvious_name" +%33 = OpString "__dxc_setup" +%35 = OpString "cb000c74" +%36 = OpString " -E main -T cs_6_6 -spirv -fspv-print-all -fspv-debug=vulkan -Qembed_debug" +%39 = OpString "@type.buffer.image" +%40 = OpString "type.buffer.image" +%42 = OpString "TemplateParam" +%45 = OpString "b" +OpName %type_buffer_image "type.buffer.image" +OpName %b "b" +OpName %main "main" +OpDecorate %b DescriptorSet 0 +OpDecorate %b Binding 0 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%type_buffer_image = OpTypeImage %int Buffer 2 0 0 2 R32i +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%void = OpTypeVoid +%uint_4 = OpConstant %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_13 = OpConstant %uint 13 +%uint_7 = OpConstant %uint 7 +%uint_15 = OpConstant %uint 15 +%uint_8 = OpConstant %uint 8 +%49 = OpTypeFunction %void +%uint_52 = OpConstant %uint 52 +%uint_6 = OpConstant %uint 6 +%uint_10 = OpConstant %uint 10 +%b = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +; CHECK: [[undef:%\w+]] = OpUndef %int +%198 = OpUndef %int +%38 = OpExtInst %void %1 DebugInfoNone +%16 = OpExtInst %void %1 DebugExpression +%18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_4 %uint_0 +%20 = OpExtInst %void %1 DebugTypeArray %18 %uint_3 +%21 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void +%22 = OpExtInst %void %1 DebugSource %3 +%23 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %22 %uint_5 +%27 = OpExtInst %void %1 DebugFunction %25 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4 +%28 = OpExtInst %void %1 DebugLexicalBlock %22 %uint_4 %uint_13 %27 +%31 = OpExtInst %void %1 DebugLocalVariable %30 %20 %22 %uint_5 %uint_7 %28 %uint_4 +; CHECK: [[var:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable [[VarName]] +%34 = OpExtInst %void %1 DebugFunction %33 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4 +%41 = OpExtInst %void %1 DebugTypeComposite %39 %uint_0 %22 %uint_0 %uint_0 %23 %40 %38 %uint_3 +%43 = OpExtInst %void %1 DebugTypeTemplateParameter %42 %18 %38 %22 %uint_0 %uint_0 +%44 = OpExtInst %void %1 DebugTypeTemplate %41 %43 +%46 = OpExtInst %void %1 DebugGlobalVariable %45 %44 %22 %uint_1 %uint_15 %23 %45 %b %uint_8 +%37 = OpExtInst %void %1 DebugEntryPoint %34 %23 %35 %36 +%133 = OpExtInst %void %1 DebugInlinedAt %uint_4 %34 +%main = OpFunction %void None %49 +%50 = OpLabel +%303 = OpExtInst %void %1 DebugScope %34 +%52 = OpExtInst %void %1 DebugFunctionDefinition %34 %main +%304 = OpExtInst %void %1 DebugScope %28 %133 +%199 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_3 %uint_52 +%245 = OpExtInst %void %1 DebugValue %31 %198 %16 %int_0 +%242 = OpExtInst %void %1 DebugValue %31 %198 %16 %int_1 +%239 = OpExtInst %void %1 DebugValue %31 %198 %16 %int_2 +; CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[var]] [[undef]] {{%\w+}} %int_0 +; CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[var]] [[undef]] {{%\w+}} %int_1 +; CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[var]] [[undef]] {{%\w+}} %int_2 +%160 = OpExtInst %void %1 DebugLine %22 %uint_6 %uint_6 %uint_3 %uint_10 +%147 = OpLoad %type_buffer_image %b +OpImageWrite %147 %uint_0 %int_0 None +%305 = OpExtInst %void %1 DebugScope %34 +%55 = OpExtInst %void %1 DebugLine %22 %uint_7 %uint_7 %uint_1 %uint_1 +OpReturn +%306 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + 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<AggressiveDCEPass>(before, false); +} + +TEST_F(AggressiveDCETest, UndefIsOutsideFunction) { + const std::string spirv = R"( + ; CHECK: OpUndef + ; CHECK: OpFunction %void + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + %3 = OpString "" + %4 = OpString "int" + %5 = OpString "x" + %6 = OpString "Val.set" + %7 = OpString "Val" + %8 = OpString "this" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_11 = OpConstant %uint 11 + %uint_5 = OpConstant %uint 5 + %uint_100 = OpConstant %uint 100 + %14 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %uint_0 = OpConstant %uint 0 + %uint_15 = OpConstant %uint 15 + %uint_6 = OpConstant %uint 6 + %uint_32 = OpConstant %uint 32 + %uint_4 = OpConstant %uint 4 +%uint_131072 = OpConstant %uint 131072 + %uint_1 = OpConstant %uint 1 + %uint_10 = OpConstant %uint 10 + %uint_8 = OpConstant %uint 8 + %26 = OpExtInst %void %1 DebugOperation %uint_0 + %27 = OpExtInst %void %1 DebugSource %3 %3 + %28 = OpExtInst %void %1 DebugCompilationUnit %uint_100 %uint_5 %27 %uint_11 + %29 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_4 %uint_131072 + %30 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void + %31 = OpExtInst %void %1 DebugFunction %6 %30 %27 %uint_6 %uint_10 %28 %6 %uint_0 %uint_6 + %32 = OpExtInst %void %1 DebugTypeMember %5 %29 %27 %uint_1 %uint_8 %uint_0 %uint_32 %uint_0 + %33 = OpExtInst %void %1 DebugTypeComposite %7 %uint_1 %27 %uint_1 %uint_8 %28 %7 %uint_32 %uint_131072 %32 + %34 = OpExtInst %void %1 DebugLocalVariable %8 %33 %27 %uint_6 %uint_10 %31 %uint_0 %uint_1 + %35 = OpExtInst %void %1 DebugExpression %26 + %2 = OpFunction %void None %14 + %36 = OpLabel + %37 = OpVariable %_ptr_Function_int Function + %38 = OpExtInst %void %1 DebugValue %34 %37 %35 %uint_0 + OpReturn + OpFunctionEnd + +)"; + + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} +TEST_F(AggressiveDCETest, ConvertDebugDeclareToDebugValue) { + const std::string spirv = + R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %input1 %input2 %output +OpExecutionMode %main OriginUpperLeft +%5 = OpString "test.hlsl" +OpSource HLSL 600 +OpName %main "main" +OpDecorate %input1 Location 0 +OpDecorate %input2 Location 1 +OpDecorate %output Location 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_32 = OpConstant %uint 32 +%uint_0 = OpConstant %uint 0 +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%_ptr_Input_v3float = OpTypePointer Input %v3float +%_ptr_Output_v3float = OpTypePointer Output %v3float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%input1 = OpVariable %_ptr_Input_v3float Input +%input2 = OpVariable %_ptr_Input_v3float Input +%output = OpVariable %_ptr_Output_v3float Output +%29 = OpTypeFunction %void +; CHECK: [[initial:%\w+]] = OpConstantComposite +; CHECK: [[expr:%\w+]] = OpExtInst %void {{%\w+}} DebugExpression +; CHECK: [[source:%\w+]] = OpExtInst %void {{%\w+}} DebugSource %6 +%30 = OpExtInst %void %1 DebugSource %5 +%31 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_3 %30 %uint_32 +; CHECK: [[basic:%\w+]] = OpExtInst %void {{%\w+}} DebugTypeBasic +%32 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 %uint_3 %uint_0 +; CHECK: [[vec_type:%\w+]] = OpExtInst %void {{%\w+}} DebugTypeVector [[basic]] %uint_4 +%33 = OpExtInst %void %1 DebugTypeVector %32 %uint_4 +%34 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void +%35 = OpExtInst %void %1 DebugFunction %5 %34 %30 %uint_1 %uint_0 %31 %5 %uint_0 %uint_1 +; CHECK: [[local:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable %6 [[vec_type]] [[source]] %uint_1 +%36 = OpExtInst %void %1 DebugLocalVariable %5 %33 %30 %uint_1 %uint_0 %35 %uint_0 +%38 = OpExtInst %void %1 DebugExpression +%initial_value = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 +%main = OpFunction %void None %29 +%39 = OpLabel +%dead_pos_w = OpVariable %_ptr_Function_v4float Function +%live_var1 = OpVariable %_ptr_Function_v4float Function +%live_var2 = OpVariable %_ptr_Function_v4float Function +%input1_value = OpLoad %v3float %input1 +%input2_value = OpLoad %v3float %input2 +; CHECK-NOT: DebugDeclare +%40 = OpExtInst %void %1 DebugDeclare %36 %dead_pos_w %38 +OpStore %dead_pos_w %initial_value +; CHECK: DebugValue %30 [[initial]] %31 +OpStore %live_var1 %initial_value +%computed1 = OpVectorTimesScalar %v3float %input1_value %float_1 +%computed2 = OpFAdd %v3float %computed1 %input2_value +; CHECK: [[new:%\w+]] = OpCompositeConstruct +%new_pos_w = OpCompositeConstruct %v4float %computed2 %float_1 +OpStore %dead_pos_w %new_pos_w +; CHECK: DebugValue %30 [[new]] %31 +OpStore %live_var2 %new_pos_w +%loaded1 = OpLoad %v4float %live_var1 +%loaded2 = OpLoad %v4float %live_var2 +%pos_xyz1 = OpVectorShuffle %v3float %loaded1 %loaded1 0 1 2 +%pos_xyz2 = OpVectorShuffle %v3float %loaded2 %loaded2 0 1 2 +%mixed_result = OpFAdd %v3float %pos_xyz1 %pos_xyz2 +OpStore %output %mixed_result +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, DebugDeclareConvertedToDebugValueKeepsDebugScope) { + const std::string spirv = R"( +; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; CHECK-DAG: [[inlined:%\w+]] = OpExtInst %void [[ext]] DebugInlinedAt +; CHECK-DAG: [[UniformVar:%\w+]] = OpVariable %_ptr_Uniform_float Uniform +; CHECK: [[scope:%\w+]] = OpExtInst %void [[ext]] DebugScope {{%\w+}} [[inlined]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK-NEXT: DebugScope +; CHECK-NEXT: DebugLine +; CHECK-NEXT: OpStore [[UniformVar]] %float_0 +; CHECK-NEXT: OpReturn + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + %3 = OpString "" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_0 = OpConstant %float 0 + %13 = OpVariable %_ptr_Uniform_float Uniform + %uint_57 = OpConstant %uint 57 + %15 = OpExtInst %void %1 DebugExpression + %16 = OpExtInst %void %1 DebugSource %3 %3 + %17 = OpExtInst %void %1 DebugCompilationUnit %uint_0 %uint_0 %16 %uint_0 + %18 = OpExtInst %void %1 DebugTypeBasic %3 %uint_0 %uint_0 %uint_0 + %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_3 + %20 = OpExtInst %void %1 DebugTypePointer %19 %uint_0 %uint_0 + %21 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void %20 + %22 = OpExtInst %void %1 DebugFunction %3 %21 %16 %uint_0 %uint_0 %17 %3 %uint_0 %uint_0 + %23 = OpExtInst %void %1 DebugTypeBasic %3 %uint_0 %uint_3 %uint_0 + %24 = OpExtInst %void %1 DebugTypeMember %3 %23 %16 %uint_0 %uint_0 %uint_0 %uint_0 %uint_0 + %25 = OpExtInst %void %1 DebugTypeComposite %3 %uint_0 %16 %uint_0 %uint_0 %17 %3 %uint_0 %uint_0 %24 + %26 = OpExtInst %void %1 DebugTypeFunction %uint_0 %23 %25 %23 + %27 = OpExtInst %void %1 DebugFunction %3 %26 %16 %uint_0 %uint_0 %17 %3 %uint_0 %uint_0 + %28 = OpExtInst %void %1 DebugLocalVariable %3 %23 %16 %uint_0 %uint_0 %27 %uint_0 %uint_0 + %29 = OpExtInst %void %1 DebugInlinedAt %uint_0 %22 + %2 = OpFunction %void None %6 + %30 = OpLabel + %31 = OpVariable %_ptr_Function_float Function + %32 = OpExtInst %void %1 DebugScope %27 %29 + %33 = OpExtInst %void %1 DebugNoLine + %34 = OpExtInst %void %1 DebugDeclare %28 %31 %15 + OpStore %31 %float_0 + %35 = OpExtInst %void %1 DebugScope %22 + %36 = OpExtInst %void %1 DebugLine %16 %uint_1 %uint_1 %uint_0 %uint_0 + OpStore %13 %float_0 + OpReturn + %37 = OpExtInst %void %1 DebugNoScope + OpFunctionEnd +)"; + + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, DebugValueWithDeadOperandKeepsDebugScope) { + const std::string spirv = R"( +; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; CHECK-DAG: [[inlined:%\w+]] = OpExtInst %void [[ext]] DebugInlinedAt +; CHECK-DAG: [[undef:%\w+]] = OpUndef %float +; CHECK: [[scope:%\w+]] = OpExtInst %void [[ext]] DebugScope {{%\w+}} [[inlined]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue {{%\w+}} [[undef]] {{%\w+}} + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + %4 = OpString "" + %5 = OpString "file.slang" + %6 = OpString "uint" + %7 = OpString "computeMain" + %8 = OpString "float" + %9 = OpString "x" + %10 = OpString "A" + %11 = OpString "test" + %12 = OpString "v" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_11 = OpConstant %uint 11 + %uint_5 = OpConstant %uint 5 + %uint_100 = OpConstant %uint 100 + %21 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint_32 = OpConstant %uint 32 + %uint_6 = OpConstant %uint 6 +%uint_131072 = OpConstant %uint 131072 + %uint_3 = OpConstant %uint 3 + %uint_7 = OpConstant %uint 7 + %uint_0 = OpConstant %uint 0 + %uint_46 = OpConstant %uint 46 + %uint_1 = OpConstant %uint 1 + %uint_8 = OpConstant %uint 8 +%_ptr_Function_float = OpTypePointer Function %float + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %uint_50 = OpConstant %uint 50 + %uint_34 = OpConstant %uint 34 + %uint_2 = OpConstant %uint 2 + %uint_56 = OpConstant %uint 56 +%_runtimearr_float = OpTypeRuntimeArray %float + %uint_57 = OpConstant %uint 57 + %42 = OpExtInst %void %1 DebugExpression + %43 = OpExtInst %void %1 DebugSource %5 %4 + %44 = OpExtInst %void %1 DebugCompilationUnit %uint_100 %uint_5 %43 %uint_11 + %45 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_6 %uint_131072 + %46 = OpExtInst %void %1 DebugTypeVector %45 %uint_3 + %47 = OpExtInst %void %1 DebugTypePointer %46 %uint_7 %uint_131072 + %48 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void %47 + %49 = OpExtInst %void %1 DebugFunction %7 %48 %43 %uint_46 %uint_6 %44 %7 %uint_0 %uint_46 + %50 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_3 %uint_131072 + %51 = OpExtInst %void %1 DebugTypeMember %9 %50 %43 %uint_8 %uint_11 %uint_0 %uint_32 %uint_0 + %52 = OpExtInst %void %1 DebugTypeComposite %10 %uint_1 %43 %uint_6 %uint_8 %44 %10 %uint_32 %uint_131072 %51 + %53 = OpExtInst %void %1 DebugTypeFunction %uint_0 %50 %52 %50 + %54 = OpExtInst %void %1 DebugFunction %11 %53 %43 %uint_34 %uint_7 %44 %11 %uint_0 %uint_34 + %55 = OpExtInst %void %1 DebugLocalVariable %12 %50 %43 %uint_34 %uint_7 %54 %uint_0 %uint_2 + %56 = OpExtInst %void %1 DebugInlinedAt %uint_50 %49 + %2 = OpFunction %void None %21 + %57 = OpLabel + %59 = OpExtInst %void %1 DebugScope %54 %56 + %60 = OpExtInst %void %1 DebugNoLine + %val = OpFAdd %float %float_0 %float_0 + %61 = OpExtInst %void %1 DebugValue %55 %val %42 + %62 = OpExtInst %void %1 DebugScope %49 + %63 = OpExtInst %void %1 DebugLine %43 %uint_56 %uint_56 %uint_5 %uint_6 + OpReturn + %66 = OpExtInst %void %1 DebugNoScope + OpFunctionEnd +)"; + + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, EliminateUntypedAccessChain) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability DescriptorHeapEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform + %void = OpTypeVoid + %float = OpTypeFloat 32 + %10 = OpTypeFunction %void +%type_1d_image = OpTypeImage %float 1D 2 0 0 1 Unknown +%_ptr_Function_type_1d_image = OpTypePointer Function %type_1d_image +%type_buffer_ext = OpTypeBufferEXT StorageBuffer +%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext +%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform + %main = OpFunction %void None %10 + %20 = OpLabel + %t1d = OpVariable %_ptr_Function_type_1d_image Function +; CHECK-NOT: OpUntypedAccessChainKHR + %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0 +; CHECK-NOT: OpLoad %type_1d_image + %22 = OpLoad %type_1d_image %21 +; CHECK-NOT: OpStore %t1d + OpStore %t1d %22 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, NoEliminateLiveUntypedAccessChain) { + const std::string spirv = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outColor + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform + %void = OpTypeVoid + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %outColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeFunction %void +%type_buffer_ext = OpTypeBufferEXT StorageBuffer +%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext +%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform + %main = OpFunction %void None %10 + %20 = OpLabel +; CHECK: OpUntypedAccessChainKHR + %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0 +; CHECK: OpLoad + %22 = OpLoad %v4float %21 + OpStore %outColor %22 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, EliminateUntypedAccessChainWithCopyObject) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability DescriptorHeapEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %type_1d_image "type_1d_image" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform + %void = OpTypeVoid + %float = OpTypeFloat 32 + %10 = OpTypeFunction %void +%type_1d_image = OpTypeImage %float 1D 2 0 0 1 Unknown +%_ptr_Function_type_1d_image = OpTypePointer Function %type_1d_image +%type_buffer_ext = OpTypeBufferEXT StorageBuffer +%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext +%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform + %main = OpFunction %void None %10 + %20 = OpLabel + %t1d = OpVariable %_ptr_Function_type_1d_image Function +; CHECK-NOT: OpUntypedAccessChainKHR + %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0 +; CHECK-NOT: OpCopyObject + %22 = OpCopyObject %type_untyped_pointer %21 +; CHECK-NOT: OpLoad %type_1d_image + %23 = OpLoad %type_1d_image %22 +; CHECK-NOT: OpStore %t1d + OpStore %t1d %23 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +// For now, aggressive DCE does not optimizes this pattern. If you implement +// it, remove this test. +TEST_F(AggressiveDCETest, EliminateUntypedAtomic) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Int64 + OpCapability DescriptorHeapEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform + %void = OpTypeVoid + %10 = OpTypeFunction %void +%type_buffer_ext = OpTypeBufferEXT StorageBuffer +%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext +%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform + %main = OpFunction %void None %10 + %20 = OpLabel + %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0 +; CHECK: [[ptr:%\w+]] = OpUntypedAccessChainKHR + %22 = OpAtomicLoad %uint %21 %uint_0 %uint_0 +; CHECK: OpAtomicLoad %uint [[ptr]] + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, EliminateUntypedAccessChainLoop) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability DescriptorHeapEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_10 = OpConstant %uint 10 +%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform + %void = OpTypeVoid + %bool = OpTypeBool + %10 = OpTypeFunction %void +%type_1d_image = OpTypeImage %uint 1D 2 0 0 1 Unknown +%type_buffer_ext = OpTypeBufferEXT StorageBuffer +%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext +%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform + %main = OpFunction %void None %10 + %20 = OpLabel + OpBranch %header + %header = OpLabel + %count = OpPhi %uint %uint_0 %20 %next %loop + %cond = OpULessThan %bool %count %uint_10 + OpLoopMerge %exit %loop None + OpBranchConditional %cond %loop %exit + %loop = OpLabel +; CHECK-NOT: OpUntypedAccessChainKHR + %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %count +; CHECK-NOT: OpLoad + %22 = OpLoad %type_1d_image %21 + %next = OpIAdd %uint %count %uint_1 + OpBranch %header + %exit = OpLabel + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + +TEST_F(AggressiveDCETest, KeepDebugBuildIdentifier) { + // Regression test for https://github.com/KhronosGroup/SPIRV-Tools/issues/6619 + // + // DebugBuildIdentifier was not added to the live-instruction worklist during + // initialization, so its operand dependencies (e.g. OpTypeInt used only by + // a constant that is only referenced by DebugBuildIdentifier) were never + // visited and were incorrectly eliminated. The surviving constant then + // referenced a deleted type, producing invalid SPIR-V. + // + // After the fix, DebugBuildIdentifier is enqueued in the worklist so its + // transitive operands (OpTypeInt 32 0, OpConstant %uint 0) are marked live. + + const std::string spirv = R"( +; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; CHECK: [[str:%\w+]] = OpString +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_0:%\w+]] = OpConstant [[uint]] 0 +; CHECK: OpExtInst %void [[ext]] DebugBuildIdentifier [[str]] [[uint_0]] + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %2 = OpString "01cfb4b77c321225f096da8ac72f29d42f0632a7" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %3 = OpTypeFunction %void + %4 = OpExtInst %void %1 DebugBuildIdentifier %2 %uint_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/canonicalize_ids_test.cpp b/test/opt/canonicalize_ids_test.cpp new file mode 100644 index 0000000..cc4b7bd --- /dev/null +++ b/test/opt/canonicalize_ids_test.cpp
@@ -0,0 +1,2118 @@ +// Copyright (c) 2025 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using CanonicalizeIdsTest = PassTest<::testing::Test>; + +// ported from remap.basic.everything.frag +TEST_F(CanonicalizeIdsTest, remap_basic) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %11 +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "main" +OpName %9 "outf4" +OpName %11 "inf" +OpDecorate %9 Location 0 +OpDecorate %11 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypePointer Output %7 +%9 = OpVariable %8 Output +%10 = OpTypePointer Input %6 +%11 = OpVariable %10 Input +%4 = OpFunction %2 None %3 +%5 = OpLabel +%12 = OpLoad %6 %11 +%13 = OpCompositeConstruct %7 %12 %12 %12 %12 +OpStore %9 %13 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %4539 %3773 +OpExecutionMode %5663 OriginUpperLeft +OpSource GLSL 450 +OpName %5663 "main" +OpName %4539 "outf4" +OpName %3773 "inf" +OpDecorate %4539 Location 0 +OpDecorate %3773 Location 0 +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%13 = OpTypeFloat 32 +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Output %29 +%4539 = OpVariable %666 Output +%650 = OpTypePointer Input %13 +%3773 = OpVariable %650 Input +%5663 = OpFunction %8 None %1282 +%24968 = OpLabel +%17486 = OpLoad %13 %3773 +%17691 = OpCompositeConstruct %29 %17486 %17486 %17486 %17486 +OpStore %4539 %17691 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.hlsl.sample.basic.everything.frag +TEST_F(CanonicalizeIdsTest, remap_hlsl_sample_basic) { + const std::string before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %188 %192 +OpExecutionMode %4 OriginUpperLeft +OpExecutionMode %4 DepthReplacing +OpSource HLSL 500 +OpName %4 "main" +OpName %8 "PS_OUTPUT" +OpMemberName %8 0 "Color" +OpMemberName %8 1 "Depth" +OpName %10 "@main(" +OpName %13 "MemberTest" +OpMemberName %13 0 "Sample" +OpMemberName %13 1 "CalculateLevelOfDetail" +OpMemberName %13 2 "CalculateLevelOfDetailUnclamped" +OpMemberName %13 3 "Gather" +OpMemberName %13 4 "GetDimensions" +OpMemberName %13 5 "GetSamplePosition" +OpMemberName %13 6 "Load" +OpMemberName %13 7 "SampleBias" +OpMemberName %13 8 "SampleCmp" +OpMemberName %13 9 "SampleCmpLevelZero" +OpMemberName %13 10 "SampleGrad" +OpMemberName %13 11 "SampleLevel" +OpName %15 "mtest" +OpName %54 "txval10" +OpName %45 "g_tTex1df4" +OpName %49 "g_sSamp" +OpName %66 "txval11" +OpName %60 "g_tTex1di4" +OpName %79 "txval12" +OpName %73 "g_tTex1du4" +OpName %90 "txval20" +OpName %83 "g_tTex2df4" +OpName %101 "txval21" +OpName %94 "g_tTex2di4" +OpName %113 "txval22" +OpName %105 "g_tTex2du4" +OpName %124 "txval30" +OpName %117 "g_tTex3df4" +OpName %134 "txval31" +OpName %128 "g_tTex3di4" +OpName %147 "txval32" +OpName %138 "g_tTex3du4" +OpName %156 "txval40" +OpName %151 "g_tTexcdf4" +OpName %165 "txval41" +OpName %160 "g_tTexcdi4" +OpName %174 "txval42" +OpName %169 "g_tTexcdu4" +OpName %176 "psout" +OpName %185 "flattenTemp" +OpName %188 "@entryPointOutput.Color" +OpName %192 "@entryPointOutput.Depth" +OpDecorate %45 Binding 0 +OpDecorate %45 DescriptorSet 0 +OpDecorate %49 Binding 0 +OpDecorate %49 DescriptorSet 0 +OpDecorate %60 Binding 2 +OpDecorate %60 DescriptorSet 0 +OpDecorate %73 Binding 3 +OpDecorate %73 DescriptorSet 0 +OpDecorate %83 Binding 4 +OpDecorate %83 DescriptorSet 0 +OpDecorate %94 Binding 5 +OpDecorate %94 DescriptorSet 0 +OpDecorate %105 Binding 6 +OpDecorate %105 DescriptorSet 0 +OpDecorate %117 Binding 7 +OpDecorate %117 DescriptorSet 0 +OpDecorate %128 Binding 8 +OpDecorate %128 DescriptorSet 0 +OpDecorate %138 Binding 9 +OpDecorate %138 DescriptorSet 0 +OpDecorate %151 Binding 10 +OpDecorate %151 DescriptorSet 0 +OpDecorate %160 Binding 11 +OpDecorate %160 DescriptorSet 0 +OpDecorate %169 Binding 12 +OpDecorate %169 DescriptorSet 0 +OpDecorate %188 Location 0 +OpDecorate %192 BuiltIn FragDepth +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeStruct %7 %6 +%9 = OpTypeFunction %8 +%12 = OpTypeInt 32 1 +%13 = OpTypeStruct %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 +%14 = OpTypePointer Function %13 +%16 = OpConstant %12 1 +%17 = OpTypePointer Function %12 +%19 = OpConstant %12 2 +%21 = OpConstant %12 3 +%23 = OpConstant %12 4 +%25 = OpConstant %12 5 +%27 = OpConstant %12 6 +%29 = OpConstant %12 0 +%31 = OpConstant %12 7 +%33 = OpConstant %12 8 +%35 = OpConstant %12 9 +%37 = OpConstant %12 10 +%39 = OpConstant %12 11 +%41 = OpTypePointer Function %7 +%43 = OpTypeImage %6 1D 0 0 0 1 Unknown +%44 = OpTypePointer UniformConstant %43 +%45 = OpVariable %44 UniformConstant +%47 = OpTypeSampler +%48 = OpTypePointer UniformConstant %47 +%49 = OpVariable %48 UniformConstant +%51 = OpTypeSampledImage %43 +%53 = OpConstant %6 0.100000001 +%55 = OpTypeVector %12 4 +%58 = OpTypeImage %12 1D 0 0 0 1 Unknown +%59 = OpTypePointer UniformConstant %58 +%60 = OpVariable %59 UniformConstant +%63 = OpTypeSampledImage %58 +%65 = OpConstant %6 0.200000003 +%67 = OpTypeInt 32 0 +%68 = OpTypeVector %67 4 +%71 = OpTypeImage %67 1D 0 0 0 1 Unknown +%72 = OpTypePointer UniformConstant %71 +%73 = OpVariable %72 UniformConstant +%76 = OpTypeSampledImage %71 +%78 = OpConstant %6 0.300000012 +%81 = OpTypeImage %6 2D 0 0 0 1 Unknown +%82 = OpTypePointer UniformConstant %81 +%83 = OpVariable %82 UniformConstant +%86 = OpTypeSampledImage %81 +%88 = OpTypeVector %6 2 +%89 = OpConstantComposite %88 %53 %65 +%92 = OpTypeImage %12 2D 0 0 0 1 Unknown +%93 = OpTypePointer UniformConstant %92 +%94 = OpVariable %93 UniformConstant +%97 = OpTypeSampledImage %92 +%99 = OpConstant %6 0.400000006 +%100 = OpConstantComposite %88 %78 %99 +%103 = OpTypeImage %67 2D 0 0 0 1 Unknown +%104 = OpTypePointer UniformConstant %103 +%105 = OpVariable %104 UniformConstant +%108 = OpTypeSampledImage %103 +%110 = OpConstant %6 0.5 +%111 = OpConstant %6 0.600000024 +%112 = OpConstantComposite %88 %110 %111 +%115 = OpTypeImage %6 3D 0 0 0 1 Unknown +%116 = OpTypePointer UniformConstant %115 +%117 = OpVariable %116 UniformConstant +%120 = OpTypeSampledImage %115 +%122 = OpTypeVector %6 3 +%123 = OpConstantComposite %122 %53 %65 %78 +%126 = OpTypeImage %12 3D 0 0 0 1 Unknown +%127 = OpTypePointer UniformConstant %126 +%128 = OpVariable %127 UniformConstant +%131 = OpTypeSampledImage %126 +%133 = OpConstantComposite %122 %99 %110 %111 +%136 = OpTypeImage %67 3D 0 0 0 1 Unknown +%137 = OpTypePointer UniformConstant %136 +%138 = OpVariable %137 UniformConstant +%141 = OpTypeSampledImage %136 +%143 = OpConstant %6 0.699999988 +%144 = OpConstant %6 0.800000012 +%145 = OpConstant %6 0.899999976 +%146 = OpConstantComposite %122 %143 %144 %145 +%149 = OpTypeImage %6 Cube 0 0 0 1 Unknown +%150 = OpTypePointer UniformConstant %149 +%151 = OpVariable %150 UniformConstant +%154 = OpTypeSampledImage %149 +%158 = OpTypeImage %12 Cube 0 0 0 1 Unknown +%159 = OpTypePointer UniformConstant %158 +%160 = OpVariable %159 UniformConstant +%163 = OpTypeSampledImage %158 +%167 = OpTypeImage %67 Cube 0 0 0 1 Unknown +%168 = OpTypePointer UniformConstant %167 +%169 = OpVariable %168 UniformConstant +%172 = OpTypeSampledImage %167 +%175 = OpTypePointer Function %8 +%177 = OpConstant %6 1 +%178 = OpConstantComposite %7 %177 %177 %177 %177 +%180 = OpTypePointer Function %6 +%187 = OpTypePointer Output %7 +%188 = OpVariable %187 Output +%191 = OpTypePointer Output %6 +%192 = OpVariable %191 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%185 = OpVariable %175 Function +%186 = OpFunctionCall %8 %10 +OpStore %185 %186 +%189 = OpAccessChain %41 %185 %29 +%190 = OpLoad %7 %189 +OpStore %188 %190 +%193 = OpAccessChain %180 %185 %16 +%194 = OpLoad %6 %193 +OpStore %192 %194 +OpReturn +OpFunctionEnd +%10 = OpFunction %8 None %9 +%11 = OpLabel +%15 = OpVariable %14 Function +%176 = OpVariable %175 Function +%18 = OpAccessChain %17 %15 %16 +OpStore %18 %16 +%20 = OpAccessChain %17 %15 %19 +OpStore %20 %16 +%22 = OpAccessChain %17 %15 %21 +OpStore %22 %16 +%24 = OpAccessChain %17 %15 %23 +OpStore %24 %16 +%26 = OpAccessChain %17 %15 %25 +OpStore %26 %16 +%28 = OpAccessChain %17 %15 %27 +OpStore %28 %16 +%30 = OpAccessChain %17 %15 %29 +OpStore %30 %16 +%32 = OpAccessChain %17 %15 %31 +OpStore %32 %16 +%34 = OpAccessChain %17 %15 %33 +OpStore %34 %16 +%36 = OpAccessChain %17 %15 %35 +OpStore %36 %16 +%38 = OpAccessChain %17 %15 %37 +OpStore %38 %16 +%40 = OpAccessChain %17 %15 %39 +OpStore %40 %16 +%46 = OpLoad %43 %45 +%50 = OpLoad %47 %49 +%52 = OpSampledImage %51 %46 %50 +%54 = OpImageSampleImplicitLod %7 %52 %53 +%61 = OpLoad %58 %60 +%62 = OpLoad %47 %49 +%64 = OpSampledImage %63 %61 %62 +%66 = OpImageSampleImplicitLod %55 %64 %65 +%74 = OpLoad %71 %73 +%75 = OpLoad %47 %49 +%77 = OpSampledImage %76 %74 %75 +%79 = OpImageSampleImplicitLod %68 %77 %78 +%84 = OpLoad %81 %83 +%85 = OpLoad %47 %49 +%87 = OpSampledImage %86 %84 %85 +%90 = OpImageSampleImplicitLod %7 %87 %89 +%95 = OpLoad %92 %94 +%96 = OpLoad %47 %49 +%98 = OpSampledImage %97 %95 %96 +%101 = OpImageSampleImplicitLod %55 %98 %100 +%106 = OpLoad %103 %105 +%107 = OpLoad %47 %49 +%109 = OpSampledImage %108 %106 %107 +%113 = OpImageSampleImplicitLod %68 %109 %112 +%118 = OpLoad %115 %117 +%119 = OpLoad %47 %49 +%121 = OpSampledImage %120 %118 %119 +%124 = OpImageSampleImplicitLod %7 %121 %123 +%129 = OpLoad %126 %128 +%130 = OpLoad %47 %49 +%132 = OpSampledImage %131 %129 %130 +%134 = OpImageSampleImplicitLod %55 %132 %133 +%139 = OpLoad %136 %138 +%140 = OpLoad %47 %49 +%142 = OpSampledImage %141 %139 %140 +%147 = OpImageSampleImplicitLod %68 %142 %146 +%152 = OpLoad %149 %151 +%153 = OpLoad %47 %49 +%155 = OpSampledImage %154 %152 %153 +%156 = OpImageSampleImplicitLod %7 %155 %123 +%161 = OpLoad %158 %160 +%162 = OpLoad %47 %49 +%164 = OpSampledImage %163 %161 %162 +%165 = OpImageSampleImplicitLod %55 %164 %133 +%170 = OpLoad %167 %169 +%171 = OpLoad %47 %49 +%173 = OpSampledImage %172 %170 %171 +%174 = OpImageSampleImplicitLod %68 %173 %146 +%179 = OpAccessChain %41 %176 %29 +OpStore %179 %178 +%181 = OpAccessChain %180 %176 %16 +OpStore %181 %177 +%182 = OpLoad %8 %176 +OpReturnValue %182 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %4253 %3709 +OpExecutionMode %5663 OriginUpperLeft +OpExecutionMode %5663 DepthReplacing +OpSource HLSL 500 +OpName %5663 "main" +OpName %1032 "PS_OUTPUT" +OpMemberName %1032 0 "Color" +OpMemberName %1032 1 "Depth" +OpName %3317 "@main(" +OpName %1335 "MemberTest" +OpMemberName %1335 0 "Sample" +OpMemberName %1335 1 "CalculateLevelOfDetail" +OpMemberName %1335 2 "CalculateLevelOfDetailUnclamped" +OpMemberName %1335 3 "Gather" +OpMemberName %1335 4 "GetDimensions" +OpMemberName %1335 5 "GetSamplePosition" +OpMemberName %1335 6 "Load" +OpMemberName %1335 7 "SampleBias" +OpMemberName %1335 8 "SampleCmp" +OpMemberName %1335 9 "SampleCmpLevelZero" +OpMemberName %1335 10 "SampleGrad" +OpMemberName %1335 11 "SampleLevel" +OpName %5830 "mtest" +OpName %3559 "txval10" +OpName %4727 "g_tTex1df4" +OpName %3305 "g_sSamp" +OpName %3560 "txval11" +OpName %4743 "g_tTex1di4" +OpName %3561 "txval12" +OpName %4807 "g_tTex1du4" +OpName %4568 "txval20" +OpName %5042 "g_tTex2df4" +OpName %4569 "txval21" +OpName %5058 "g_tTex2di4" +OpName %4570 "txval22" +OpName %5122 "g_tTex2du4" +OpName %5577 "txval30" +OpName %3967 "g_tTex3df4" +OpName %5578 "txval31" +OpName %3983 "g_tTex3di4" +OpName %5579 "txval32" +OpName %4047 "g_tTex3du4" +OpName %3575 "txval40" +OpName %3789 "g_tTexcdf4" +OpName %3576 "txval41" +OpName %3805 "g_tTexcdi4" +OpName %3577 "txval42" +OpName %3869 "g_tTexcdu4" +OpName %5072 "psout" +OpName %4104 "flattenTemp" +OpName %4253 "@entryPointOutput.Color" +OpName %3709 "@entryPointOutput.Depth" +OpDecorate %4727 Binding 0 +OpDecorate %4727 DescriptorSet 0 +OpDecorate %3305 Binding 0 +OpDecorate %3305 DescriptorSet 0 +OpDecorate %4743 Binding 2 +OpDecorate %4743 DescriptorSet 0 +OpDecorate %4807 Binding 3 +OpDecorate %4807 DescriptorSet 0 +OpDecorate %5042 Binding 4 +OpDecorate %5042 DescriptorSet 0 +OpDecorate %5058 Binding 5 +OpDecorate %5058 DescriptorSet 0 +OpDecorate %5122 Binding 6 +OpDecorate %5122 DescriptorSet 0 +OpDecorate %3967 Binding 7 +OpDecorate %3967 DescriptorSet 0 +OpDecorate %3983 Binding 8 +OpDecorate %3983 DescriptorSet 0 +OpDecorate %4047 Binding 9 +OpDecorate %4047 DescriptorSet 0 +OpDecorate %3789 Binding 10 +OpDecorate %3789 DescriptorSet 0 +OpDecorate %3805 Binding 11 +OpDecorate %3805 DescriptorSet 0 +OpDecorate %3869 Binding 12 +OpDecorate %3869 DescriptorSet 0 +OpDecorate %4253 Location 0 +OpDecorate %3709 BuiltIn FragDepth +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%13 = OpTypeFloat 32 +%29 = OpTypeVector %13 4 +%1032 = OpTypeStruct %29 %13 +%319 = OpTypeFunction %1032 +%12 = OpTypeInt 32 1 +%1335 = OpTypeStruct %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 +%1972 = OpTypePointer Function %1335 +%2574 = OpConstant %12 1 +%649 = OpTypePointer Function %12 +%2577 = OpConstant %12 2 +%2580 = OpConstant %12 3 +%2583 = OpConstant %12 4 +%2586 = OpConstant %12 5 +%2589 = OpConstant %12 6 +%2571 = OpConstant %12 0 +%2592 = OpConstant %12 7 +%2595 = OpConstant %12 8 +%2598 = OpConstant %12 9 +%2601 = OpConstant %12 10 +%2604 = OpConstant %12 11 +%666 = OpTypePointer Function %29 +%149 = OpTypeImage %13 1D 0 0 0 1 Unknown +%786 = OpTypePointer UniformConstant %149 +%4727 = OpVariable %786 UniformConstant +%508 = OpTypeSampler +%1145 = OpTypePointer UniformConstant %508 +%3305 = OpVariable %1145 UniformConstant +%510 = OpTypeSampledImage %149 +%2935 = OpConstant %13 0.100000001 +%26 = OpTypeVector %12 4 +%148 = OpTypeImage %12 1D 0 0 0 1 Unknown +%785 = OpTypePointer UniformConstant %148 +%4743 = OpVariable %785 UniformConstant +%511 = OpTypeSampledImage %148 +%2821 = OpConstant %13 0.200000003 +%11 = OpTypeInt 32 0 +%23 = OpTypeVector %11 4 +%147 = OpTypeImage %11 1D 0 0 0 1 Unknown +%784 = OpTypePointer UniformConstant %147 +%4807 = OpVariable %784 UniformConstant +%512 = OpTypeSampledImage %147 +%2151 = OpConstant %13 0.300000012 +%150 = OpTypeImage %13 2D 0 0 0 1 Unknown +%787 = OpTypePointer UniformConstant %150 +%5042 = OpVariable %787 UniformConstant +%513 = OpTypeSampledImage %150 +%19 = OpTypeVector %13 2 +%1825 = OpConstantComposite %19 %2935 %2821 +%151 = OpTypeImage %12 2D 0 0 0 1 Unknown +%788 = OpTypePointer UniformConstant %151 +%5058 = OpVariable %788 UniformConstant +%514 = OpTypeSampledImage %151 +%2707 = OpConstant %13 0.400000006 +%2028 = OpConstantComposite %19 %2151 %2707 +%152 = OpTypeImage %11 2D 0 0 0 1 Unknown +%789 = OpTypePointer UniformConstant %152 +%5122 = OpVariable %789 UniformConstant +%515 = OpTypeSampledImage %152 +%252 = OpConstant %13 0.5 +%2037 = OpConstant %13 0.600000024 +%2684 = OpConstantComposite %19 %252 %2037 +%153 = OpTypeImage %13 3D 0 0 0 1 Unknown +%790 = OpTypePointer UniformConstant %153 +%3967 = OpVariable %790 UniformConstant +%516 = OpTypeSampledImage %153 +%24 = OpTypeVector %13 3 +%1660 = OpConstantComposite %24 %2935 %2821 %2151 +%154 = OpTypeImage %12 3D 0 0 0 1 Unknown +%791 = OpTypePointer UniformConstant %154 +%3983 = OpVariable %791 UniformConstant +%517 = OpTypeSampledImage %154 +%2174 = OpConstantComposite %24 %2707 %252 %2037 +%155 = OpTypeImage %11 3D 0 0 0 1 Unknown +%792 = OpTypePointer UniformConstant %155 +%4047 = OpVariable %792 UniformConstant +%518 = OpTypeSampledImage %155 +%808 = OpConstant %13 0.699999988 +%2593 = OpConstant %13 0.800000012 +%1364 = OpConstant %13 0.899999976 +%2476 = OpConstantComposite %24 %808 %2593 %1364 +%156 = OpTypeImage %13 Cube 0 0 0 1 Unknown +%793 = OpTypePointer UniformConstant %156 +%3789 = OpVariable %793 UniformConstant +%519 = OpTypeSampledImage %156 +%157 = OpTypeImage %12 Cube 0 0 0 1 Unknown +%794 = OpTypePointer UniformConstant %157 +%3805 = OpVariable %794 UniformConstant +%520 = OpTypeSampledImage %157 +%158 = OpTypeImage %11 Cube 0 0 0 1 Unknown +%795 = OpTypePointer UniformConstant %158 +%3869 = OpVariable %795 UniformConstant +%521 = OpTypeSampledImage %158 +%1669 = OpTypePointer Function %1032 +%138 = OpConstant %13 1 +%1284 = OpConstantComposite %29 %138 %138 %138 %138 +%650 = OpTypePointer Function %13 +%667 = OpTypePointer Output %29 +%4253 = OpVariable %667 Output +%651 = OpTypePointer Output %13 +%3709 = OpVariable %651 Output +%5663 = OpFunction %8 None %1282 +%24877 = OpLabel +%4104 = OpVariable %1669 Function +%18803 = OpFunctionCall %1032 %3317 +OpStore %4104 %18803 +%13396 = OpAccessChain %666 %4104 %2571 +%7967 = OpLoad %29 %13396 +OpStore %4253 %7967 +%16622 = OpAccessChain %650 %4104 %2574 +%11539 = OpLoad %13 %16622 +OpStore %3709 %11539 +OpReturn +OpFunctionEnd +%3317 = OpFunction %1032 None %319 +%12442 = OpLabel +%5830 = OpVariable %1972 Function +%5072 = OpVariable %1669 Function +%22671 = OpAccessChain %649 %5830 %2574 +OpStore %22671 %2574 +%20306 = OpAccessChain %649 %5830 %2577 +OpStore %20306 %2574 +%20307 = OpAccessChain %649 %5830 %2580 +OpStore %20307 %2574 +%20308 = OpAccessChain %649 %5830 %2583 +OpStore %20308 %2574 +%20309 = OpAccessChain %649 %5830 %2586 +OpStore %20309 %2574 +%20310 = OpAccessChain %649 %5830 %2589 +OpStore %20310 %2574 +%20311 = OpAccessChain %649 %5830 %2571 +OpStore %20311 %2574 +%20312 = OpAccessChain %649 %5830 %2592 +OpStore %20312 %2574 +%20313 = OpAccessChain %649 %5830 %2595 +OpStore %20313 %2574 +%20314 = OpAccessChain %649 %5830 %2598 +OpStore %20314 %2574 +%20315 = OpAccessChain %649 %5830 %2601 +OpStore %20315 %2574 +%20230 = OpAccessChain %649 %5830 %2604 +OpStore %20230 %2574 +%15508 = OpLoad %149 %4727 +%12260 = OpLoad %508 %3305 +%12514 = OpSampledImage %510 %15508 %12260 +%3559 = OpImageSampleImplicitLod %29 %12514 %2935 +%9477 = OpLoad %148 %4743 +%16280 = OpLoad %508 %3305 +%12515 = OpSampledImage %511 %9477 %16280 +%3560 = OpImageSampleImplicitLod %26 %12515 %2821 +%9478 = OpLoad %147 %4807 +%16281 = OpLoad %508 %3305 +%12516 = OpSampledImage %512 %9478 %16281 +%3561 = OpImageSampleImplicitLod %23 %12516 %2151 +%9479 = OpLoad %150 %5042 +%16282 = OpLoad %508 %3305 +%12517 = OpSampledImage %513 %9479 %16282 +%4568 = OpImageSampleImplicitLod %29 %12517 %1825 +%9480 = OpLoad %151 %5058 +%16283 = OpLoad %508 %3305 +%12518 = OpSampledImage %514 %9480 %16283 +%4569 = OpImageSampleImplicitLod %26 %12518 %2028 +%9481 = OpLoad %152 %5122 +%16284 = OpLoad %508 %3305 +%12519 = OpSampledImage %515 %9481 %16284 +%4570 = OpImageSampleImplicitLod %23 %12519 %2684 +%9482 = OpLoad %153 %3967 +%16285 = OpLoad %508 %3305 +%12520 = OpSampledImage %516 %9482 %16285 +%5577 = OpImageSampleImplicitLod %29 %12520 %1660 +%9483 = OpLoad %154 %3983 +%16286 = OpLoad %508 %3305 +%12521 = OpSampledImage %517 %9483 %16286 +%5578 = OpImageSampleImplicitLod %26 %12521 %2174 +%9484 = OpLoad %155 %4047 +%16287 = OpLoad %508 %3305 +%12522 = OpSampledImage %518 %9484 %16287 +%5579 = OpImageSampleImplicitLod %23 %12522 %2476 +%9485 = OpLoad %156 %3789 +%16288 = OpLoad %508 %3305 +%12523 = OpSampledImage %519 %9485 %16288 +%3575 = OpImageSampleImplicitLod %29 %12523 %1660 +%9486 = OpLoad %157 %3805 +%16289 = OpLoad %508 %3305 +%12524 = OpSampledImage %520 %9486 %16289 +%3576 = OpImageSampleImplicitLod %26 %12524 %2174 +%9487 = OpLoad %158 %3869 +%16290 = OpLoad %508 %3305 +%12590 = OpSampledImage %521 %9487 %16290 +%3577 = OpImageSampleImplicitLod %23 %12590 %2476 +%14275 = OpAccessChain %666 %5072 %2571 +OpStore %14275 %1284 +%20231 = OpAccessChain %650 %5072 %2574 +OpStore %20231 %138 +%8692 = OpLoad %1032 %5072 +OpReturnValue %8692 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.hlsl.templatetypes.everything.frag +TEST_F(CanonicalizeIdsTest, remap_hlsl_templatetypes) { + const std::string before = + R"(OpCapability Shader +OpCapability Float64 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %153 %156 +OpExecutionMode %4 OriginUpperLeft +OpSource HLSL 500 +OpName %4 "main" +OpName %11 "@main(vf4;" +OpName %10 "input" +OpName %18 "r00" +OpName %21 "r01" +OpName %25 "r12" +OpName %29 "r13" +OpName %14 "r14" +OpName %35 "r15" +OpName %39 "r16" +OpName %44 "r20" +OpName %49 "r21" +OpName %53 "r22" +OpName %58 "r23" +OpName %63 "r24" +OpName %67 "r30" +OpName %72 "r31" +OpName %76 "r32" +OpName %81 "r33" +OpName %86 "r34" +OpName %90 "r40" +OpName %95 "r41" +OpName %18 "r42" +OpName %101 "r43" +OpName %106 "r44" +OpName %125 "r50" +OpName %125 "r51" +OpName %131 "r61" +OpName %137 "r62" +OpName %142 "r65" +OpName %148 "r66" +OpName %154 "input" +OpName %153 "input" +OpName %156 "@entryPointOutput" +OpName %157 "param" +OpDecorate %153 Location 0 +OpDecorate %156 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypePointer Function %7 +%9 = OpTypeFunction %6 %8 +%14 = OpConstant %6 1 +%15 = OpConstant %6 2 +%16 = OpConstant %6 3 +%17 = OpConstant %6 4 +%18 = OpConstantComposite %7 %14 %15 %16 %17 +%20 = OpConstant %6 5 +%21 = OpConstantComposite %7 %15 %16 %17 %20 +%22 = OpTypeBool +%25 = OpConstantFalse %22 +%26 = OpTypeInt 32 1 +%29 = OpConstant %26 1 +%32 = OpTypeFloat 64 +%35 = OpConstant %32 1 +%36 = OpTypeInt 32 0 +%39 = OpConstant %36 1 +%40 = OpTypeVector %22 2 +%43 = OpConstantTrue %22 +%44 = OpConstantComposite %40 %25 %43 +%45 = OpTypeVector %26 2 +%48 = OpConstant %26 2 +%49 = OpConstantComposite %45 %29 %48 +%50 = OpTypeVector %6 2 +%53 = OpConstantComposite %50 %14 %15 +%54 = OpTypeVector %32 2 +%57 = OpConstant %32 2 +%58 = OpConstantComposite %54 %35 %57 +%59 = OpTypeVector %36 2 +%62 = OpConstant %36 2 +%63 = OpConstantComposite %59 %39 %62 +%64 = OpTypeVector %22 3 +%67 = OpConstantComposite %64 %25 %43 %43 +%68 = OpTypeVector %26 3 +%71 = OpConstant %26 3 +%72 = OpConstantComposite %68 %29 %48 %71 +%73 = OpTypeVector %6 3 +%76 = OpConstantComposite %73 %14 %15 %16 +%77 = OpTypeVector %32 3 +%80 = OpConstant %32 3 +%81 = OpConstantComposite %77 %35 %57 %80 +%82 = OpTypeVector %36 3 +%85 = OpConstant %36 3 +%86 = OpConstantComposite %82 %39 %62 %85 +%87 = OpTypeVector %22 4 +%90 = OpConstantComposite %87 %25 %43 %43 %25 +%91 = OpTypeVector %26 4 +%94 = OpConstant %26 4 +%95 = OpConstantComposite %91 %29 %48 %71 %94 +%97 = OpTypeVector %32 4 +%100 = OpConstant %32 4 +%101 = OpConstantComposite %97 %35 %57 %80 %100 +%102 = OpTypeVector %36 4 +%105 = OpConstant %36 4 +%106 = OpConstantComposite %102 %39 %62 %85 %105 +%107 = OpTypeMatrix %7 4 +%110 = OpConstant %6 0 +%111 = OpConstantComposite %7 %110 %14 %15 %16 +%112 = OpConstant %6 6 +%113 = OpConstant %6 7 +%114 = OpConstantComposite %7 %17 %20 %112 %113 +%115 = OpConstant %6 8 +%116 = OpConstant %6 9 +%117 = OpConstant %6 10 +%118 = OpConstant %6 11 +%119 = OpConstantComposite %7 %115 %116 %117 %118 +%120 = OpConstant %6 12 +%121 = OpConstant %6 13 +%122 = OpConstant %6 14 +%123 = OpConstant %6 15 +%124 = OpConstantComposite %7 %120 %121 %122 %123 +%125 = OpConstantComposite %107 %111 %114 %119 %124 +%127 = OpTypeMatrix %73 2 +%130 = OpConstantComposite %73 %17 %20 %112 +%131 = OpConstantComposite %127 %76 %130 +%132 = OpTypeMatrix %50 3 +%135 = OpConstantComposite %50 %16 %17 +%136 = OpConstantComposite %50 %20 %112 +%137 = OpConstantComposite %132 %53 %135 %136 +%138 = OpTypeMatrix %50 4 +%141 = OpConstantComposite %50 %113 %115 +%142 = OpConstantComposite %138 %53 %135 %136 %141 +%143 = OpTypeMatrix %73 4 +%146 = OpConstantComposite %73 %113 %115 %116 +%147 = OpConstantComposite %73 %117 %118 %120 +%148 = OpConstantComposite %143 %76 %130 %146 %147 +%152 = OpTypePointer Input %7 +%153 = OpVariable %152 Input +%155 = OpTypePointer Output %6 +%156 = OpVariable %155 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%157 = OpVariable %8 Function +%154 = OpLoad %7 %153 +OpStore %157 %154 +%159 = OpFunctionCall %6 %11 %157 +OpStore %156 %159 +OpReturn +OpFunctionEnd +%11 = OpFunction %6 None %9 +%10 = OpFunctionParameter %8 +%12 = OpLabel +OpReturnValue %110 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Float64 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %4872 %4045 +OpExecutionMode %5663 OriginUpperLeft +OpSource HLSL 500 +OpName %5663 "main" +OpName %3917 "@main(vf4;" +OpName %10636 "input" +OpName %1616 "r00" +OpName %590 "r01" +OpName %1927 "r12" +OpName %2574 "r13" +OpName %138 "r14" +OpName %1201 "r15" +OpName %2573 "r16" +OpName %311 "r20" +OpName %1848 "r21" +OpName %312 "r22" +OpName %490 "r23" +OpName %1840 "r24" +OpName %869 "r30" +OpName %2668 "r31" +OpName %1271 "r32" +OpName %1108 "r33" +OpName %2654 "r34" +OpName %340 "r40" +OpName %56 "r41" +OpName %1616 "r42" +OpName %1328 "r43" +OpName %35 "r44" +OpName %1294 "r50" +OpName %1294 "r51" +OpName %1207 "r61" +OpName %162 "r62" +OpName %2695 "r65" +OpName %55 "r66" +OpName %24021 "input" +OpName %4872 "input" +OpName %4045 "@entryPointOutput" +OpName %5786 "param" +OpDecorate %4872 Location 0 +OpDecorate %4045 Location 0 +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%13 = OpTypeFloat 32 +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Function %29 +%255 = OpTypeFunction %13 %666 +%138 = OpConstant %13 1 +%24 = OpConstant %13 2 +%2978 = OpConstant %13 3 +%2921 = OpConstant %13 4 +%1616 = OpConstantComposite %29 %138 %24 %2978 %2921 +%1387 = OpConstant %13 5 +%590 = OpConstantComposite %29 %24 %2978 %2921 %1387 +%9 = OpTypeBool +%1927 = OpConstantFalse %9 +%12 = OpTypeInt 32 1 +%2574 = OpConstant %12 1 +%14 = OpTypeFloat 64 +%1201 = OpConstant %14 1 +%11 = OpTypeInt 32 0 +%2573 = OpConstant %11 1 +%15 = OpTypeVector %9 2 +%1926 = OpConstantTrue %9 +%311 = OpConstantComposite %15 %1927 %1926 +%18 = OpTypeVector %12 2 +%2577 = OpConstant %12 2 +%1848 = OpConstantComposite %18 %2574 %2577 +%19 = OpTypeVector %13 2 +%312 = OpConstantComposite %19 %138 %24 +%20 = OpTypeVector %14 2 +%2572 = OpConstant %14 2 +%490 = OpConstantComposite %20 %1201 %2572 +%17 = OpTypeVector %11 2 +%2576 = OpConstant %11 2 +%1840 = OpConstantComposite %17 %2573 %2576 +%16 = OpTypeVector %9 3 +%869 = OpConstantComposite %16 %1927 %1926 %1926 +%22 = OpTypeVector %12 3 +%2580 = OpConstant %12 3 +%2668 = OpConstantComposite %22 %2574 %2577 %2580 +%25 = OpTypeVector %13 3 +%1271 = OpConstantComposite %25 %138 %24 %2978 +%26 = OpTypeVector %14 3 +%1057 = OpConstant %14 3 +%1108 = OpConstantComposite %26 %1201 %2572 %1057 +%21 = OpTypeVector %11 3 +%2579 = OpConstant %11 3 +%2654 = OpConstantComposite %21 %2573 %2576 %2579 +%23 = OpTypeVector %9 4 +%340 = OpConstantComposite %23 %1927 %1926 %1926 %1927 +%27 = OpTypeVector %12 4 +%2583 = OpConstant %12 4 +%56 = OpConstantComposite %27 %2574 %2577 %2580 %2583 +%30 = OpTypeVector %14 4 +%2553 = OpConstant %14 4 +%1328 = OpConstantComposite %30 %1201 %2572 %1057 %2553 +%28 = OpTypeVector %11 4 +%2582 = OpConstant %11 4 +%35 = OpConstantComposite %28 %2573 %2576 %2579 %2582 +%101 = OpTypeMatrix %29 4 +%2575 = OpConstant %13 0 +%1199 = OpConstantComposite %29 %2575 %138 %24 %2978 +%2864 = OpConstant %13 6 +%1330 = OpConstant %13 7 +%2290 = OpConstantComposite %29 %2921 %1387 %2864 %1330 +%2807 = OpConstant %13 8 +%2040 = OpConstant %13 9 +%1273 = OpConstant %13 10 +%506 = OpConstant %13 11 +%694 = OpConstantComposite %29 %2807 %2040 %1273 %506 +%2750 = OpConstant %13 12 +%1983 = OpConstant %13 13 +%1216 = OpConstant %13 14 +%449 = OpConstant %13 15 +%2679 = OpConstantComposite %29 %2750 %1983 %1216 %449 +%1294 = OpConstantComposite %101 %1199 %2290 %694 %2679 +%54 = OpTypeMatrix %25 2 +%837 = OpConstantComposite %25 %2921 %1387 %2864 +%1207 = OpConstantComposite %54 %1271 %837 +%60 = OpTypeMatrix %19 3 +%2354 = OpConstantComposite %19 %2978 %2921 +%364 = OpConstantComposite %19 %1387 %2864 +%162 = OpConstantComposite %60 %312 %2354 %364 +%71 = OpTypeMatrix %19 4 +%2976 = OpConstantComposite %19 %1330 %2807 +%2695 = OpConstantComposite %71 %312 %2354 %364 %2976 +%86 = OpTypeMatrix %25 4 +%635 = OpConstantComposite %25 %1330 %2807 %2040 +%832 = OpConstantComposite %25 %1273 %506 %2750 +%55 = OpConstantComposite %86 %1271 %837 %635 %832 +%667 = OpTypePointer Input %29 +%4872 = OpVariable %667 Input +%650 = OpTypePointer Output %13 +%4045 = OpVariable %650 Output +%5663 = OpFunction %8 None %1282 +%24953 = OpLabel +%5786 = OpVariable %666 Function +%24021 = OpLoad %29 %4872 +OpStore %5786 %24021 +%9338 = OpFunctionCall %13 %3917 %5786 +OpStore %4045 %9338 +OpReturn +OpFunctionEnd +%3917 = OpFunction %13 None %255 +%10636 = OpFunctionParameter %666 +%10637 = OpLabel +OpReturnValue %2575 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.if.everything.frag +TEST_F(CanonicalizeIdsTest, remap_if) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %8 %17 +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "main" +OpName %8 "inf" +OpName %17 "outf4" +OpDecorate %8 Location 0 +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypePointer Input %6 +%8 = OpVariable %7 Input +%10 = OpConstant %6 2 +%11 = OpTypeBool +%15 = OpTypeVector %6 4 +%16 = OpTypePointer Output %15 +%17 = OpVariable %16 Output +%22 = OpConstant %6 -0.5 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%9 = OpLoad %6 %8 +%12 = OpFOrdGreaterThan %11 %9 %10 +OpSelectionMerge %14 None +OpBranchConditional %12 %13 %20 +%13 = OpLabel +%18 = OpLoad %6 %8 +%19 = OpCompositeConstruct %15 %18 %18 %18 %18 +OpStore %17 %19 +OpBranch %14 +%20 = OpLabel +%21 = OpLoad %6 %8 +%23 = OpFAdd %6 %21 %22 +%24 = OpCompositeConstruct %15 %23 %23 %23 %23 +OpStore %17 %24 +OpBranch %14 +%14 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %3773 %4539 +OpExecutionMode %5663 OriginUpperLeft +OpSource GLSL 450 +OpName %5663 "main" +OpName %3773 "inf" +OpName %4539 "outf4" +OpDecorate %3773 Location 0 +OpDecorate %4539 Location 0 +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%13 = OpTypeFloat 32 +%650 = OpTypePointer Input %13 +%3773 = OpVariable %650 Input +%24 = OpConstant %13 2 +%9 = OpTypeBool +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Output %29 +%4539 = OpVariable %666 Output +%947 = OpConstant %13 -0.5 +%5663 = OpFunction %8 None %1282 +%7911 = OpLabel +%21734 = OpLoad %13 %3773 +%13508 = OpFOrdGreaterThan %9 %21734 %24 +OpSelectionMerge %19578 None +OpBranchConditional %13508 %13182 %10142 +%13182 = OpLabel +%9496 = OpLoad %13 %3773 +%17615 = OpCompositeConstruct %29 %9496 %9496 %9496 %9496 +OpStore %4539 %17615 +OpBranch %19578 +%10142 = OpLabel +%22854 = OpLoad %13 %3773 +%9982 = OpFAdd %13 %22854 %947 +%12421 = OpCompositeConstruct %29 %9982 %9982 %9982 %9982 +OpStore %4539 %12421 +OpBranch %19578 +%19578 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.similar_1a.everything.frag +TEST_F(CanonicalizeIdsTest, remap_similar_1a) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %53 %73 %75 +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "Test1(i1;" +OpName %10 "bound" +OpName %14 "Test2(i1;" +OpName %13 "bound" +OpName %17 "r" +OpName %19 "x" +OpName %44 "param" +OpName %53 "ini4" +OpName %73 "outf4" +OpName %75 "inf" +OpName %78 "param" +OpName %82 "param" +OpDecorate %53 Flat +OpDecorate %53 Location 1 +OpDecorate %73 Location 0 +OpDecorate %75 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpTypeFloat 32 +%9 = OpTypeFunction %8 %7 +%16 = OpTypePointer Function %8 +%18 = OpConstant %8 0 +%20 = OpConstant %6 0 +%28 = OpTypeBool +%30 = OpConstant %8 0.5 +%34 = OpConstant %6 1 +%40 = OpConstant %6 2 +%51 = OpTypeVector %6 4 +%52 = OpTypePointer Input %51 +%53 = OpVariable %52 Input +%54 = OpTypeInt 32 0 +%55 = OpConstant %54 1 +%56 = OpTypePointer Input %6 +%59 = OpConstant %54 2 +%64 = OpConstant %54 0 +%71 = OpTypeVector %8 4 +%72 = OpTypePointer Output %71 +%73 = OpVariable %72 Output +%74 = OpTypePointer Input %8 +%75 = OpVariable %74 Input +%4 = OpFunction %2 None %3 +%5 = OpLabel +%78 = OpVariable %7 Function +%82 = OpVariable %7 Function +%76 = OpLoad %8 %75 +%77 = OpConvertFToS %6 %76 +OpStore %78 %77 +%79 = OpFunctionCall %8 %11 %78 +%80 = OpLoad %8 %75 +%81 = OpConvertFToS %6 %80 +OpStore %82 %81 +%83 = OpFunctionCall %8 %14 %82 +%84 = OpFAdd %8 %79 %83 +%85 = OpCompositeConstruct %71 %84 %84 %84 %84 +OpStore %73 %85 +OpReturn +OpFunctionEnd +%11 = OpFunction %8 None %9 +%10 = OpFunctionParameter %7 +%12 = OpLabel +%17 = OpVariable %16 Function +%19 = OpVariable %7 Function +OpStore %17 %18 +OpStore %19 %20 +OpBranch %21 +%21 = OpLabel +OpLoopMerge %23 %24 None +OpBranch %25 +%25 = OpLabel +%26 = OpLoad %6 %19 +%27 = OpLoad %6 %10 +%29 = OpSLessThan %28 %26 %27 +OpBranchConditional %29 %22 %23 +%22 = OpLabel +%31 = OpLoad %8 %17 +%32 = OpFAdd %8 %31 %30 +OpStore %17 %32 +OpBranch %24 +%24 = OpLabel +%33 = OpLoad %6 %19 +%35 = OpIAdd %6 %33 %34 +OpStore %19 %35 +OpBranch %21 +%23 = OpLabel +%36 = OpLoad %8 %17 +OpReturnValue %36 +OpFunctionEnd +%14 = OpFunction %8 None %9 +%13 = OpFunctionParameter %7 +%15 = OpLabel +%44 = OpVariable %7 Function +%39 = OpLoad %6 %13 +%41 = OpSGreaterThan %28 %39 %40 +OpSelectionMerge %43 None +OpBranchConditional %41 %42 %48 +%42 = OpLabel +%45 = OpLoad %6 %13 +OpStore %44 %45 +%46 = OpFunctionCall %8 %11 %44 +OpReturnValue %46 +%48 = OpLabel +%49 = OpLoad %6 %13 +%50 = OpIMul %6 %49 %40 +%57 = OpAccessChain %56 %53 %55 +%58 = OpLoad %6 %57 +%60 = OpAccessChain %56 %53 %59 +%61 = OpLoad %6 %60 +%62 = OpIMul %6 %58 %61 +%63 = OpIAdd %6 %50 %62 +%65 = OpAccessChain %56 %53 %64 +%66 = OpLoad %6 %65 +%67 = OpIAdd %6 %63 %66 +%68 = OpConvertSToF %8 %67 +OpReturnValue %68 +%43 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %4201 %4539 %3773 +OpExecutionMode %5663 OriginUpperLeft +OpSource GLSL 450 +OpName %5663 "main" +OpName %3782 "Test1(i1;" +OpName %6931 "bound" +OpName %3836 "Test2(i1;" +OpName %4408 "bound" +OpName %4292 "r" +OpName %4298 "x" +OpName %22102 "param" +OpName %4201 "ini4" +OpName %4539 "outf4" +OpName %3773 "inf" +OpName %18415 "param" +OpName %5786 "param" +OpDecorate %4201 Flat +OpDecorate %4201 Location 1 +OpDecorate %4539 Location 0 +OpDecorate %3773 Location 0 +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%12 = OpTypeInt 32 1 +%649 = OpTypePointer Function %12 +%13 = OpTypeFloat 32 +%204 = OpTypeFunction %13 %649 +%650 = OpTypePointer Function %13 +%2572 = OpConstant %13 0 +%2571 = OpConstant %12 0 +%9 = OpTypeBool +%252 = OpConstant %13 0.5 +%2574 = OpConstant %12 1 +%2577 = OpConstant %12 2 +%26 = OpTypeVector %12 4 +%663 = OpTypePointer Input %26 +%4201 = OpVariable %663 Input +%11 = OpTypeInt 32 0 +%2573 = OpConstant %11 1 +%651 = OpTypePointer Input %12 +%2576 = OpConstant %11 2 +%2570 = OpConstant %11 0 +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Output %29 +%4539 = OpVariable %666 Output +%652 = OpTypePointer Input %13 +%3773 = OpVariable %652 Input +%5663 = OpFunction %8 None %1282 +%24915 = OpLabel +%18415 = OpVariable %649 Function +%5786 = OpVariable %649 Function +%8366 = OpLoad %13 %3773 +%8654 = OpConvertFToS %12 %8366 +OpStore %18415 %8654 +%17256 = OpFunctionCall %13 %3782 %18415 +%14512 = OpLoad %13 %3773 +%7041 = OpConvertFToS %12 %14512 +OpStore %5786 %7041 +%23993 = OpFunctionCall %13 %3836 %5786 +%9180 = OpFAdd %13 %17256 %23993 +%15728 = OpCompositeConstruct %29 %9180 %9180 %9180 %9180 +OpStore %4539 %15728 +OpReturn +OpFunctionEnd +%3782 = OpFunction %13 None %204 +%6931 = OpFunctionParameter %649 +%12220 = OpLabel +%4292 = OpVariable %650 Function +%4298 = OpVariable %649 Function +OpStore %4292 %2572 +OpStore %4298 %2571 +OpBranch %14924 +%14924 = OpLabel +OpLoopMerge %8882 %6488 None +OpBranch %11857 +%11857 = OpLabel +%13755 = OpLoad %12 %4298 +%22731 = OpLoad %12 %6931 +%20007 = OpSLessThan %9 %13755 %22731 +OpBranchConditional %20007 %24750 %8882 +%24750 = OpLabel +%22912 = OpLoad %13 %4292 +%19471 = OpFAdd %13 %22912 %252 +OpStore %4292 %19471 +OpBranch %6488 +%6488 = OpLabel +%19050 = OpLoad %12 %4298 +%8593 = OpIAdd %12 %19050 %2574 +OpStore %4298 %8593 +OpBranch %14924 +%8882 = OpLabel +%11601 = OpLoad %13 %4292 +OpReturnValue %11601 +OpFunctionEnd +%3836 = OpFunction %13 None %204 +%4408 = OpFunctionParameter %649 +%12143 = OpLabel +%22102 = OpVariable %649 Function +%24151 = OpLoad %12 %4408 +%13868 = OpSGreaterThan %9 %24151 %2577 +OpSelectionMerge %14966 None +OpBranchConditional %13868 %9492 %17416 +%9492 = OpLabel +%15624 = OpLoad %12 %4408 +OpStore %22102 %15624 +%17278 = OpFunctionCall %13 %3782 %22102 +OpReturnValue %17278 +%17416 = OpLabel +%19506 = OpLoad %12 %4408 +%22773 = OpIMul %12 %19506 %2577 +%13472 = OpAccessChain %651 %4201 %2573 +%15280 = OpLoad %12 %13472 +%18079 = OpAccessChain %651 %4201 %2576 +%15199 = OpLoad %12 %18079 +%9343 = OpIMul %12 %15280 %15199 +%11462 = OpIAdd %12 %22773 %9343 +%11885 = OpAccessChain %651 %4201 %2570 +%21176 = OpLoad %12 %11885 +%10505 = OpIAdd %12 %11462 %21176 +%14626 = OpConvertSToF %13 %10505 +OpReturnValue %14626 +%14966 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.similar_1b.everything.frag +TEST_F(CanonicalizeIdsTest, remap_similar_1b) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %58 %78 %80 +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "Test1(i1;" +OpName %10 "bound" +OpName %14 "Test2(i1;" +OpName %13 "bound" +OpName %17 "r" +OpName %19 "x" +OpName %49 "param" +OpName %58 "ini4" +OpName %78 "outf4" +OpName %80 "inf" +OpName %83 "param" +OpName %87 "param" +OpDecorate %58 Flat +OpDecorate %58 Location 0 +OpDecorate %78 Location 0 +OpDecorate %80 Location 1 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpTypeFloat 32 +%9 = OpTypeFunction %8 %7 +%16 = OpTypePointer Function %8 +%18 = OpConstant %8 0 +%20 = OpConstant %6 0 +%28 = OpTypeBool +%30 = OpConstant %8 0.5 +%34 = OpConstant %6 1 +%36 = OpConstant %8 0.200000003 +%43 = OpConstant %6 2 +%54 = OpConstant %6 4 +%56 = OpTypeVector %6 4 +%57 = OpTypePointer Input %56 +%58 = OpVariable %57 Input +%59 = OpTypeInt 32 0 +%60 = OpConstant %59 1 +%61 = OpTypePointer Input %6 +%64 = OpConstant %59 2 +%69 = OpConstant %59 0 +%76 = OpTypeVector %8 4 +%77 = OpTypePointer Output %76 +%78 = OpVariable %77 Output +%79 = OpTypePointer Input %8 +%80 = OpVariable %79 Input +%4 = OpFunction %2 None %3 +%5 = OpLabel +%83 = OpVariable %7 Function +%87 = OpVariable %7 Function +%81 = OpLoad %8 %80 +%82 = OpConvertFToS %6 %81 +OpStore %83 %82 +%84 = OpFunctionCall %8 %11 %83 +%85 = OpLoad %8 %80 +%86 = OpConvertFToS %6 %85 +OpStore %87 %86 +%88 = OpFunctionCall %8 %14 %87 +%89 = OpFAdd %8 %84 %88 +%90 = OpCompositeConstruct %76 %89 %89 %89 %89 +OpStore %78 %90 +OpReturn +OpFunctionEnd +%11 = OpFunction %8 None %9 +%10 = OpFunctionParameter %7 +%12 = OpLabel +%17 = OpVariable %16 Function +%19 = OpVariable %7 Function +OpStore %17 %18 +OpStore %19 %20 +OpBranch %21 +%21 = OpLabel +OpLoopMerge %23 %24 None +OpBranch %25 +%25 = OpLabel +%26 = OpLoad %6 %19 +%27 = OpLoad %6 %10 +%29 = OpSLessThan %28 %26 %27 +OpBranchConditional %29 %22 %23 +%22 = OpLabel +%31 = OpLoad %8 %17 +%32 = OpFAdd %8 %31 %30 +OpStore %17 %32 +OpBranch %24 +%24 = OpLabel +%33 = OpLoad %6 %19 +%35 = OpIAdd %6 %33 %34 +OpStore %19 %35 +OpBranch %21 +%23 = OpLabel +%37 = OpLoad %8 %17 +%38 = OpFAdd %8 %37 %36 +OpStore %17 %38 +%39 = OpLoad %8 %17 +OpReturnValue %39 +OpFunctionEnd +%14 = OpFunction %8 None %9 +%13 = OpFunctionParameter %7 +%15 = OpLabel +%49 = OpVariable %7 Function +%42 = OpLoad %6 %13 +%44 = OpSGreaterThan %28 %42 %43 +OpSelectionMerge %46 None +OpBranchConditional %44 %45 %52 +%45 = OpLabel +%47 = OpLoad %6 %13 +%48 = OpIMul %6 %47 %43 +OpStore %49 %48 +%50 = OpFunctionCall %8 %11 %49 +OpReturnValue %50 +%52 = OpLabel +%53 = OpLoad %6 %13 +%55 = OpIMul %6 %53 %54 +%62 = OpAccessChain %61 %58 %60 +%63 = OpLoad %6 %62 +%65 = OpAccessChain %61 %58 %64 +%66 = OpLoad %6 %65 +%67 = OpIMul %6 %63 %66 +%68 = OpIAdd %6 %55 %67 +%70 = OpAccessChain %61 %58 %69 +%71 = OpLoad %6 %70 +%72 = OpIAdd %6 %68 %71 +%73 = OpConvertSToF %8 %72 +OpReturnValue %73 +%46 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %4201 %4539 %3773 +OpExecutionMode %5663 OriginUpperLeft +OpSource GLSL 450 +OpName %5663 "main" +OpName %3782 "Test1(i1;" +OpName %6931 "bound" +OpName %3836 "Test2(i1;" +OpName %4408 "bound" +OpName %4292 "r" +OpName %4298 "x" +OpName %22102 "param" +OpName %4201 "ini4" +OpName %4539 "outf4" +OpName %3773 "inf" +OpName %18415 "param" +OpName %5786 "param" +OpDecorate %4201 Flat +OpDecorate %4201 Location 0 +OpDecorate %4539 Location 0 +OpDecorate %3773 Location 1 +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%12 = OpTypeInt 32 1 +%649 = OpTypePointer Function %12 +%13 = OpTypeFloat 32 +%204 = OpTypeFunction %13 %649 +%650 = OpTypePointer Function %13 +%2572 = OpConstant %13 0 +%2571 = OpConstant %12 0 +%9 = OpTypeBool +%252 = OpConstant %13 0.5 +%2574 = OpConstant %12 1 +%2821 = OpConstant %13 0.200000003 +%2577 = OpConstant %12 2 +%2583 = OpConstant %12 4 +%26 = OpTypeVector %12 4 +%663 = OpTypePointer Input %26 +%4201 = OpVariable %663 Input +%11 = OpTypeInt 32 0 +%2573 = OpConstant %11 1 +%651 = OpTypePointer Input %12 +%2576 = OpConstant %11 2 +%2570 = OpConstant %11 0 +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Output %29 +%4539 = OpVariable %666 Output +%652 = OpTypePointer Input %13 +%3773 = OpVariable %652 Input +%5663 = OpFunction %8 None %1282 +%24915 = OpLabel +%18415 = OpVariable %649 Function +%5786 = OpVariable %649 Function +%8366 = OpLoad %13 %3773 +%8654 = OpConvertFToS %12 %8366 +OpStore %18415 %8654 +%17256 = OpFunctionCall %13 %3782 %18415 +%14512 = OpLoad %13 %3773 +%7041 = OpConvertFToS %12 %14512 +OpStore %5786 %7041 +%23993 = OpFunctionCall %13 %3836 %5786 +%9180 = OpFAdd %13 %17256 %23993 +%15728 = OpCompositeConstruct %29 %9180 %9180 %9180 %9180 +OpStore %4539 %15728 +OpReturn +OpFunctionEnd +%3782 = OpFunction %13 None %204 +%6931 = OpFunctionParameter %649 +%12220 = OpLabel +%4292 = OpVariable %650 Function +%4298 = OpVariable %649 Function +OpStore %4292 %2572 +OpStore %4298 %2571 +OpBranch %14924 +%14924 = OpLabel +OpLoopMerge %6507 %6488 None +OpBranch %11857 +%11857 = OpLabel +%13755 = OpLoad %12 %4298 +%22731 = OpLoad %12 %6931 +%20007 = OpSLessThan %9 %13755 %22731 +OpBranchConditional %20007 %24750 %6507 +%24750 = OpLabel +%22912 = OpLoad %13 %4292 +%19471 = OpFAdd %13 %22912 %252 +OpStore %4292 %19471 +OpBranch %6488 +%6488 = OpLabel +%19050 = OpLoad %12 %4298 +%8593 = OpIAdd %12 %19050 %2574 +OpStore %4298 %8593 +OpBranch %14924 +%6507 = OpLabel +%18877 = OpLoad %13 %4292 +%15899 = OpFAdd %13 %18877 %2821 +OpStore %4292 %15899 +%20342 = OpLoad %13 %4292 +OpReturnValue %20342 +OpFunctionEnd +%3836 = OpFunction %13 None %204 +%4408 = OpFunctionParameter %649 +%12143 = OpLabel +%22102 = OpVariable %649 Function +%24151 = OpLoad %12 %4408 +%13868 = OpSGreaterThan %9 %24151 %2577 +OpSelectionMerge %14966 None +OpBranchConditional %13868 %10822 %17416 +%10822 = OpLabel +%22680 = OpLoad %12 %4408 +%23216 = OpIMul %12 %22680 %2577 +OpStore %22102 %23216 +%7042 = OpFunctionCall %13 %3782 %22102 +OpReturnValue %7042 +%17416 = OpLabel +%19506 = OpLoad %12 %4408 +%22773 = OpIMul %12 %19506 %2583 +%13472 = OpAccessChain %651 %4201 %2573 +%15280 = OpLoad %12 %13472 +%18079 = OpAccessChain %651 %4201 %2576 +%15199 = OpLoad %12 %18079 +%9343 = OpIMul %12 %15280 %15199 +%11462 = OpIAdd %12 %22773 %9343 +%11885 = OpAccessChain %651 %4201 %2570 +%21176 = OpLoad %12 %11885 +%10505 = OpIAdd %12 %11462 %21176 +%14626 = OpConvertSToF %13 %10505 +OpReturnValue %14626 +%14966 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.specconst.comp +TEST_F(CanonicalizeIdsTest, remap_specconst) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %4 "main" +OpExecutionMode %4 LocalSize 1 1 1 +OpSource GLSL 450 +OpName %4 "main" +OpDecorate %7 SpecId 0 +OpDecorate %8 SpecId 1 +OpDecorate %9 SpecId 2 +OpDecorate %11 BuiltIn WorkgroupSize +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%7 = OpSpecConstant %6 1 +%8 = OpSpecConstant %6 1 +%9 = OpSpecConstant %6 1 +%10 = OpTypeVector %6 3 +%11 = OpSpecConstantComposite %10 %7 %8 %9 +%14 = OpSpecConstantOp %6 CompositeExtract %11 0 +%16 = OpSpecConstantOp %6 CompositeExtract %11 1 +%18 = OpSpecConstantOp %6 CompositeExtract %11 2 +%19 = OpSpecConstantOp %6 IMul %16 %18 +%20 = OpSpecConstantOp %6 IAdd %14 %19 +%4 = OpFunction %2 None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %5663 "main" +OpExecutionMode %5663 LocalSize 1 1 1 +OpSource GLSL 450 +OpName %5663 "main" +OpDecorate %2 SpecId 0 +OpDecorate %3 SpecId 1 +OpDecorate %4 SpecId 2 +OpDecorate %5 BuiltIn WorkgroupSize +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%11 = OpTypeInt 32 0 +%2 = OpSpecConstant %11 1 +%3 = OpSpecConstant %11 1 +%4 = OpSpecConstant %11 1 +%20 = OpTypeVector %11 3 +%5 = OpSpecConstantComposite %20 %2 %3 %4 +%6 = OpSpecConstantOp %11 CompositeExtract %5 0 +%7 = OpSpecConstantOp %11 CompositeExtract %5 1 +%9 = OpSpecConstantOp %11 CompositeExtract %5 2 +%10 = OpSpecConstantOp %11 IMul %7 %9 +%12 = OpSpecConstantOp %11 IAdd %6 %10 +%5663 = OpFunction %8 None %1282 +%16103 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.switch.everything.frag +TEST_F(CanonicalizeIdsTest, remap_switch) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %23 +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "main" +OpName %9 "in0" +OpName %23 "FragColor" +OpDecorate %9 Location 0 +OpDecorate %23 RelaxedPrecision +OpDecorate %23 Location 0 +OpDecorate %29 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypePointer Input %7 +%9 = OpVariable %8 Input +%10 = OpTypeInt 32 0 +%11 = OpConstant %10 3 +%12 = OpTypePointer Input %6 +%15 = OpTypeInt 32 1 +%22 = OpTypePointer Output %7 +%23 = OpVariable %22 Output +%24 = OpConstant %10 0 +%27 = OpConstant %6 0 +%31 = OpConstant %10 1 +%34 = OpConstant %6 1 +%38 = OpConstant %10 2 +%41 = OpConstant %6 2 +%45 = OpConstant %6 -1 +%46 = OpConstantComposite %7 %45 %45 %45 %45 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%13 = OpAccessChain %12 %9 %11 +%14 = OpLoad %6 %13 +%16 = OpConvertFToS %15 %14 +OpSelectionMerge %21 None +OpSwitch %16 %20 0 %17 1 %18 2 %19 +%20 = OpLabel +OpStore %23 %46 +OpBranch %21 +%17 = OpLabel +%25 = OpAccessChain %12 %9 %24 +%26 = OpLoad %6 %25 +%28 = OpFAdd %6 %26 %27 +%29 = OpCompositeConstruct %7 %28 %28 %28 %28 +OpStore %23 %29 +OpBranch %21 +%18 = OpLabel +%32 = OpAccessChain %12 %9 %31 +%33 = OpLoad %6 %32 +%35 = OpFAdd %6 %33 %34 +%36 = OpCompositeConstruct %7 %35 %35 %35 %35 +OpStore %23 %36 +OpBranch %21 +%19 = OpLabel +%39 = OpAccessChain %12 %9 %38 +%40 = OpLoad %6 %39 +%42 = OpFAdd %6 %40 %41 +%43 = OpCompositeConstruct %7 %42 %42 %42 %42 +OpStore %23 %43 +OpBranch %21 +%21 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %3719 %3994 +OpExecutionMode %5663 OriginUpperLeft +OpSource GLSL 450 +OpName %5663 "main" +OpName %3719 "in0" +OpName %3994 "FragColor" +OpDecorate %3719 Location 0 +OpDecorate %3994 RelaxedPrecision +OpDecorate %3994 Location 0 +OpDecorate %12421 RelaxedPrecision +OpDecorate %12422 RelaxedPrecision +OpDecorate %12423 RelaxedPrecision +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%13 = OpTypeFloat 32 +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Input %29 +%3719 = OpVariable %666 Input +%11 = OpTypeInt 32 0 +%2579 = OpConstant %11 3 +%650 = OpTypePointer Input %13 +%12 = OpTypeInt 32 1 +%667 = OpTypePointer Output %29 +%3994 = OpVariable %667 Output +%2570 = OpConstant %11 0 +%2572 = OpConstant %13 0 +%2573 = OpConstant %11 1 +%138 = OpConstant %13 1 +%2576 = OpConstant %11 2 +%24 = OpConstant %13 2 +%833 = OpConstant %13 -1 +%1284 = OpConstantComposite %29 %833 %833 %833 %833 +%5663 = OpFunction %8 None %1282 +%23915 = OpLabel +%7984 = OpAccessChain %650 %3719 %2579 +%11376 = OpLoad %13 %7984 +%16859 = OpConvertFToS %12 %11376 +OpSelectionMerge %19578 None +OpSwitch %16859 %15971 0 %8158 1 %8159 2 %8160 +%15971 = OpLabel +OpStore %3994 %1284 +OpBranch %19578 +%8158 = OpLabel +%21848 = OpAccessChain %650 %3719 %2570 +%23987 = OpLoad %13 %21848 +%19989 = OpFAdd %13 %23987 %2572 +%12421 = OpCompositeConstruct %29 %19989 %19989 %19989 %19989 +OpStore %3994 %12421 +OpBranch %19578 +%8159 = OpLabel +%21849 = OpAccessChain %650 %3719 %2573 +%23988 = OpLoad %13 %21849 +%19990 = OpFAdd %13 %23988 %138 +%12422 = OpCompositeConstruct %29 %19990 %19990 %19990 %19990 +OpStore %3994 %12422 +OpBranch %19578 +%8160 = OpLabel +%21850 = OpAccessChain %650 %3719 %2576 +%23989 = OpLoad %13 %21850 +%19991 = OpFAdd %13 %23989 %24 +%12423 = OpCompositeConstruct %29 %19991 %19991 %19991 %19991 +OpStore %3994 %12423 +OpBranch %19578 +%19578 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +// ported from remap.uniformarray.everything.frag +TEST_F(CanonicalizeIdsTest, remap_uniformarray) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %14 %25 %43 %54 +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 140 +OpName %4 "main" +OpName %9 "texColor" +OpName %14 "color" +OpName %25 "inColor" +OpName %43 "alpha" +OpName %54 "gl_FragColor" +OpDecorate %14 Location 1 +OpDecorate %25 Location 0 +OpDecorate %43 Location 7 +OpDecorate %54 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypePointer Function %7 +%10 = OpTypeInt 32 0 +%11 = OpConstant %10 6 +%12 = OpTypeArray %7 %11 +%13 = OpTypePointer Input %12 +%14 = OpVariable %13 Input +%15 = OpTypeInt 32 1 +%16 = OpConstant %15 1 +%17 = OpTypePointer Input %7 +%23 = OpTypeVector %6 3 +%24 = OpTypePointer Input %23 +%25 = OpVariable %24 Input +%30 = OpConstant %10 0 +%31 = OpTypePointer Function %6 +%34 = OpConstant %10 1 +%37 = OpConstant %10 2 +%40 = OpConstant %10 16 +%41 = OpTypeArray %6 %40 +%42 = OpTypePointer Input %41 +%43 = OpVariable %42 Input +%44 = OpConstant %15 12 +%45 = OpTypePointer Input %6 +%48 = OpConstant %10 3 +%53 = OpTypePointer Output %7 +%54 = OpVariable %53 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%9 = OpVariable %8 Function +%18 = OpAccessChain %17 %14 %16 +%19 = OpLoad %7 %18 +%20 = OpAccessChain %17 %14 %16 +%21 = OpLoad %7 %20 +%22 = OpFAdd %7 %19 %21 +OpStore %9 %22 +%26 = OpLoad %23 %25 +%27 = OpLoad %7 %9 +%28 = OpVectorShuffle %23 %27 %27 0 1 2 +%29 = OpFAdd %23 %28 %26 +%32 = OpAccessChain %31 %9 %30 +%33 = OpCompositeExtract %6 %29 0 +OpStore %32 %33 +%35 = OpAccessChain %31 %9 %34 +%36 = OpCompositeExtract %6 %29 1 +OpStore %35 %36 +%38 = OpAccessChain %31 %9 %37 +%39 = OpCompositeExtract %6 %29 2 +OpStore %38 %39 +%46 = OpAccessChain %45 %43 %44 +%47 = OpLoad %6 %46 +%49 = OpAccessChain %31 %9 %48 +%50 = OpLoad %6 %49 +%51 = OpFAdd %6 %50 %47 +%52 = OpAccessChain %31 %9 %48 +OpStore %52 %51 +%55 = OpLoad %7 %9 +OpStore %54 %55 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %5663 "main" %3608 %4957 %4339 %5139 +OpExecutionMode %5663 OriginUpperLeft +OpSource GLSL 140 +OpName %5663 "main" +OpName %4902 "texColor" +OpName %3608 "color" +OpName %4957 "inColor" +OpName %4339 "alpha" +OpName %5139 "gl_FragColor" +OpDecorate %3608 Location 1 +OpDecorate %4957 Location 0 +OpDecorate %4339 Location 7 +OpDecorate %5139 Location 0 +%8 = OpTypeVoid +%1282 = OpTypeFunction %8 +%13 = OpTypeFloat 32 +%29 = OpTypeVector %13 4 +%666 = OpTypePointer Function %29 +%11 = OpTypeInt 32 0 +%2588 = OpConstant %11 6 +%740 = OpTypeArray %29 %2588 +%1377 = OpTypePointer Input %740 +%3608 = OpVariable %1377 Input +%12 = OpTypeInt 32 1 +%2574 = OpConstant %12 1 +%667 = OpTypePointer Input %29 +%24 = OpTypeVector %13 3 +%661 = OpTypePointer Input %24 +%4957 = OpVariable %661 Input +%2570 = OpConstant %11 0 +%650 = OpTypePointer Function %13 +%2573 = OpConstant %11 1 +%2576 = OpConstant %11 2 +%2618 = OpConstant %11 16 +%709 = OpTypeArray %13 %2618 +%1346 = OpTypePointer Input %709 +%4339 = OpVariable %1346 Input +%2607 = OpConstant %12 12 +%651 = OpTypePointer Input %13 +%2579 = OpConstant %11 3 +%668 = OpTypePointer Output %29 +%5139 = OpVariable %668 Output +%5663 = OpFunction %8 None %1282 +%25029 = OpLabel +%4902 = OpVariable %666 Function +%10645 = OpAccessChain %667 %3608 %2574 +%8181 = OpLoad %29 %10645 +%21370 = OpAccessChain %667 %3608 %2574 +%11355 = OpLoad %29 %21370 +%23084 = OpFAdd %29 %8181 %11355 +OpStore %4902 %23084 +%21218 = OpLoad %24 %4957 +%13695 = OpLoad %29 %4902 +%23959 = OpVectorShuffle %24 %13695 %13695 0 1 2 +%14937 = OpFAdd %24 %23959 %21218 +%15653 = OpAccessChain %650 %4902 %2570 +%21354 = OpCompositeExtract %13 %14937 0 +OpStore %15653 %21354 +%16378 = OpAccessChain %650 %4902 %2573 +%15746 = OpCompositeExtract %13 %14937 1 +OpStore %16378 %15746 +%16379 = OpAccessChain %650 %4902 %2576 +%15747 = OpCompositeExtract %13 %14937 2 +OpStore %16379 %15747 +%19895 = OpAccessChain %651 %4339 %2607 +%7372 = OpLoad %13 %19895 +%21371 = OpAccessChain %650 %4902 %2579 +%11412 = OpLoad %13 %21371 +%22584 = OpFAdd %13 %11412 %7372 +%17318 = OpAccessChain %650 %4902 %2579 +OpStore %17318 %22584 +%17934 = OpLoad %29 %4902 +OpStore %5139 %17934 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false); +} + +TEST_F(CanonicalizeIdsTest, NonSemanticDebugInfo) { + std::string before = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +%4 = OpString "" +OpDecorate %6 ArrayStride 4 +OpDecorate %7 Block +OpMemberDecorate %7 0 Offset 0 +OpDecorate %3 Binding 0 +OpDecorate %3 DescriptorSet 0 +%8 = OpTypeVoid +%9 = OpTypeInt 32 0 +%10 = OpConstant %9 100 +%11 = OpTypeFunction %8 +%12 = OpTypeInt 32 1 +%13 = OpConstant %12 0 +%14 = OpTypeFloat 32 +%5 = OpTypePointer StorageBuffer %14 +%6 = OpTypeRuntimeArray %14 +%7 = OpTypeStruct %6 +%15 = OpTypePointer StorageBuffer %7 +%16 = OpConstant %14 0 +%3 = OpVariable %15 StorageBuffer +%17 = OpExtInst %8 %1 DebugInfoNone +%18 = OpExtInst %8 %1 DebugSource %4 %4 +%19 = OpExtInst %8 %1 DebugCompilationUnit %10 %10 %18 %10 +%20 = OpExtInst %8 %1 DebugTypeFunction %10 %8 +%21 = OpExtInst %8 %1 DebugFunction %4 %20 %18 %10 %10 %19 %4 %10 %10 +%22 = OpExtInst %8 %1 DebugEntryPoint %21 %19 %4 %4 +%23 = OpExtInst %8 %1 DebugTypeBasic %4 %10 %10 %10 +%24 = OpExtInst %8 %1 DebugTypeArray %23 %10 +%25 = OpExtInst %8 %1 DebugTypeMember %4 %24 %18 %10 %10 %10 %10 %10 +%26 = OpExtInst %8 %1 DebugTypeComposite %4 %10 %18 %10 %10 %19 %4 %10 %10 %25 +%27 = OpExtInst %8 %1 DebugGlobalVariable %4 %26 %18 %10 %10 %19 %4 %3 %10 +%2 = OpFunction %8 None %11 +%28 = OpLabel +%29 = OpExtInst %8 %1 DebugFunctionDefinition %21 %2 +%30 = OpExtInst %8 %1 DebugScope %21 +%31 = OpExtInst %8 %1 DebugLine %18 %10 %10 %10 %10 +%32 = OpAccessChain %5 %3 %13 %13 +OpStore %32 %16 +OpReturn +%33 = OpExtInst %8 %1 DebugNoScope +OpFunctionEnd +)"; + + std::string after = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %10742 "main" +OpExecutionMode %10742 LocalSize 1 1 1 +%3 = OpString "" +OpDecorate %2002 ArrayStride 4 +OpDecorate %1952 Block +OpMemberDecorate %1952 0 Offset 0 +OpDecorate %2 Binding 0 +OpDecorate %2 DescriptorSet 0 +%8 = OpTypeVoid +%11 = OpTypeInt 32 0 +%2870 = OpConstant %11 100 +%1282 = OpTypeFunction %8 +%12 = OpTypeInt 32 1 +%2571 = OpConstant %12 0 +%13 = OpTypeFloat 32 +%650 = OpTypePointer StorageBuffer %13 +%2002 = OpTypeRuntimeArray %13 +%1952 = OpTypeStruct %2002 +%2589 = OpTypePointer StorageBuffer %1952 +%2572 = OpConstant %13 0 +%2 = OpVariable %2589 StorageBuffer +%4 = OpExtInst %8 %1 DebugInfoNone +%5 = OpExtInst %8 %1 DebugSource %3 %3 +%6 = OpExtInst %8 %1 DebugCompilationUnit %2870 %2870 %5 %2870 +%7 = OpExtInst %8 %1 DebugTypeFunction %2870 %8 +%9 = OpExtInst %8 %1 DebugFunction %3 %7 %5 %2870 %2870 %6 %3 %2870 %2870 +%10 = OpExtInst %8 %1 DebugEntryPoint %9 %6 %3 %3 +%14 = OpExtInst %8 %1 DebugTypeBasic %3 %2870 %2870 %2870 +%15 = OpExtInst %8 %1 DebugTypeArray %14 %2870 +%16 = OpExtInst %8 %1 DebugTypeMember %3 %15 %5 %2870 %2870 %2870 %2870 %2870 +%17 = OpExtInst %8 %1 DebugTypeComposite %3 %2870 %5 %2870 %2870 %6 %3 %2870 %2870 %16 +%18 = OpExtInst %8 %1 DebugGlobalVariable %3 %17 %5 %2870 %2870 %6 %3 %2 %2870 +%10742 = OpFunction %8 None %1282 +%9442 = OpLabel +%15450 = OpExtInst %8 %1 DebugFunctionDefinition %9 %10742 +%20275 = OpExtInst %8 %1 DebugScope %9 +%19 = OpExtInst %8 %1 DebugLine %5 %2870 %2870 %2870 %2870 +%20274 = OpAccessChain %650 %2 %2571 %2571 +OpStore %20274 %2572 +OpReturn +%20276 = OpExtInst %8 %1 DebugNoScope +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp index a8e9557..cb67a2f 100644 --- a/test/opt/ccp_test.cpp +++ b/test/opt/ccp_test.cpp
@@ -581,7 +581,7 @@ EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); } -TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) { +TEST_F(CCPTest, FoldConstantCompositeInstructionsWithSpecConst) { const std::string spv_asm = R"( OpCapability Shader OpMemoryModel Logical GLSL450 @@ -595,7 +595,7 @@ %true = OpConstantTrue %bool ; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool %11 = OpSpecConstantComposite %v3bool %true %true %true - %12 = OpConstantComposite %_struct_8 %11 + %12 = OpSpecConstantComposite %_struct_8 %11 ; CHECK: OpFunction %1 = OpFunction %void None %4 %29 = OpLabel @@ -926,6 +926,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<CCPPass>(text, true); } @@ -1237,6 +1238,34 @@ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); } +TEST_F(CCPTest, CCPSuccessWithNoCCPOnExtendedInstruction) { + // We don't expect to have Constant Propagation on certain + // extended instructions with 16 bit integers. This is to + // test that this stage doesn't throw errors. If we add support + // for 16 bit integers, this test should change. + + const std::string text = R"(OpCapability Shader +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +%void = OpTypeVoid +%half = OpTypeFloat 16 +%half_0x1p_0 = OpConstant %half 0x1p+0 +%6 = OpTypeFunction %void +%main = OpFunction %void None %6 +%7 = OpLabel +%8 = OpExtInst %half %1 Cos %half_0x1p_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck<CCPPass>(text, text, false); +} + TEST_F(CCPTest, FunctionDeclaration) { // Make sure the pass works with a function declaration that is called. const std::string text = R"(OpCapability Addresses
diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp index ef7addc..677f9bc 100644 --- a/test/opt/combine_access_chains_test.cpp +++ b/test/opt/combine_access_chains_test.cpp
@@ -573,6 +573,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<CombineAccessChains>(text, true); } @@ -610,6 +611,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<CombineAccessChains>(text, true); } @@ -648,6 +650,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<CombineAccessChains>(text, true); } @@ -763,6 +766,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<CombineAccessChains>(text, true); }
diff --git a/test/opt/constant_manager_test.cpp b/test/opt/constant_manager_test.cpp index 54c8652..a0a1317 100644 --- a/test/opt/constant_manager_test.cpp +++ b/test/opt/constant_manager_test.cpp
@@ -102,6 +102,59 @@ EXPECT_EQ(inst, nullptr); } +TEST_F(ConstantManagerTest, ConstantCompositeReplicateExtMapping) { + const std::string text = R"( +OpCapability Shader +OpCapability ReplicatedCompositesEXT +OpExtension "SPV_EXT_replicated_composites" +OpMemoryModel Logical Simple +%1 = OpTypeInt 32 1 +%2 = OpTypeVector %1 4 +%3 = OpConstant %1 0 +%4 = OpConstantCompositeReplicateEXT %2 %3 +%5 = OpSpecConstantCompositeReplicateEXT %2 %3 + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_5, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + ConstantManager* const_mgr = context->get_constant_mgr(); + const Constant* base_constant = const_mgr->FindDeclaredConstant(3); + ASSERT_NE(base_constant, nullptr); + + const Constant* composite_constant = const_mgr->FindDeclaredConstant(4); + ASSERT_NE(composite_constant, nullptr); + const Vector* vector_type = composite_constant->type()->AsVector(); + ASSERT_NE(vector_type, nullptr); + + const CompositeConstant* composite = + composite_constant->AsCompositeConstant(); + ASSERT_NE(composite, nullptr); + ASSERT_EQ(composite->GetComponents().size(), + static_cast<size_t>(vector_type->element_count())); + ASSERT_FALSE(composite->GetComponents().empty()); + for (const Constant* component : composite->GetComponents()) { + EXPECT_EQ(component, base_constant); + } + + const Constant* spec_composite_constant = const_mgr->FindDeclaredConstant(5); + ASSERT_NE(spec_composite_constant, nullptr); + const Vector* spec_vector_type = spec_composite_constant->type()->AsVector(); + ASSERT_NE(spec_vector_type, nullptr); + + const CompositeConstant* spec_composite = + spec_composite_constant->AsCompositeConstant(); + ASSERT_NE(spec_composite, nullptr); + ASSERT_EQ(spec_composite->GetComponents().size(), + static_cast<size_t>(spec_vector_type->element_count())); + ASSERT_FALSE(spec_composite->GetComponents().empty()); + for (const Constant* component : spec_composite->GetComponents()) { + EXPECT_EQ(component, base_constant); + } +} + } // namespace } // namespace analysis } // namespace opt
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp index c577404..169ab29 100644 --- a/test/opt/convert_relaxed_to_half_test.cpp +++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -514,28 +514,30 @@ %14 = OpFAdd %half %55 %56 %16 = OpCompositeConstruct %v2half %13 %14 %58 = OpFConvert %float %14 -%18 = OpImageSampleDrefImplicitLod %float %46 %16 %58 +%59 = OpFConvert %v2float %16 +%18 = OpImageSampleDrefImplicitLod %float %46 %59 %58 %48 = OpLoad %27 %g_tTex1df4 %49 = OpLoad %29 %g_sSamp %50 = OpSampledImage %31 %48 %49 -%59 = OpFConvert %half %12 -%60 = OpFConvert %half %float_0_200000003 -%15 = OpFMul %half %59 %60 +%60 = OpFConvert %half %12 +%61 = OpFConvert %half %float_0_200000003 +%15 = OpFMul %half %60 %61 %51 = OpAccessChain %_ptr_Uniform_float %_ %int_1 %17 = OpLoad %float %51 -%61 = OpFConvert %half %17 -%62 = OpFConvert %half %float_0_200000003 -%19 = OpFAdd %half %61 %62 +%62 = OpFConvert %half %17 +%63 = OpFConvert %half %float_0_200000003 +%19 = OpFAdd %half %62 %63 %20 = OpCompositeConstruct %v2half %15 %19 -%63 = OpFConvert %float %19 -%21 = OpImageSampleDrefImplicitLod %float %50 %20 %63 -%64 = OpFConvert %half %18 -%65 = OpFConvert %half %21 -%22 = OpFAdd %half %64 %65 -%66 = OpFConvert %half %float_0_5 -%23 = OpFMul %half %22 %66 -%67 = OpFConvert %float %23 -OpStore %_entryPointOutput_Color %67 +%64 = OpFConvert %float %19 +%65 = OpFConvert %v2float %20 +%21 = OpImageSampleDrefImplicitLod %float %50 %65 %64 +%66 = OpFConvert %half %18 +%67 = OpFConvert %half %21 +%22 = OpFAdd %half %66 %67 +%68 = OpFConvert %half %float_0_5 +%23 = OpFMul %half %22 %68 +%69 = OpFConvert %float %23 +OpStore %_entryPointOutput_Color %69 OpReturn OpFunctionEnd )"; @@ -1616,6 +1618,7 @@ TEST_F(ConvertToHalfTest, PreserveImageOperandPrecision) { // Ensure that a non-relaxed texture coordinate does not get relaxed nor // converted to half precision if the image instruction is marked relaxed. + // Update - coordinates are not allowed to be 16-bit regardless // Also ensure that a relaxed local variable does get converted to half // precision before being passed to an image opeartor. @@ -1639,7 +1642,7 @@ OpCapability Float16 %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %13 %25 + OpEntryPoint Fragment %4 "main" %13 %26 OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpDecorate %9 RelaxedPrecision @@ -1648,8 +1651,8 @@ OpDecorate %17 DescriptorSet 3 OpDecorate %17 Binding 0 OpDecorate %18 RelaxedPrecision - OpDecorate %23 RelaxedPrecision - OpDecorate %25 Location 10 + OpDecorate %24 RelaxedPrecision + OpDecorate %26 Location 10 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 @@ -1669,13 +1672,13 @@ %17 = OpVariable %16 UniformConstant %19 = OpTypeVector %6 2 ;CHECK: [[vec2_t:%\w+]] = OpTypeVector [[float32_t]] 2 - %24 = OpTypePointer Input %7 + %25 = OpTypePointer Input %7 ;CHECK: [[input_ptr_t:%\w+]] = OpTypePointer Input [[vec4_t]] - %25 = OpVariable %24 Input - %29 = OpTypeFloat 16 + %26 = OpVariable %25 Input + %30 = OpTypeFloat 16 ;CHECK: [[float16_t:%\w+]] = OpTypeFloat 16 - %30 = OpTypeVector %29 4 - %33 = OpTypeVector %29 2 + %31 = OpTypeVector %30 4 + %34 = OpTypeVector %30 2 ;CHECK: [[vec2_16b_t:%\w+]] = OpTypeVector [[float16_t]] 2 %4 = OpFunction %2 None %3 %5 = OpLabel @@ -1686,26 +1689,27 @@ OpStore %9 %11 %18 = OpLoad %15 %17 %20 = OpLoad %7 %9 - %31 = OpFConvert %30 %20 - %32 = OpFConvert %30 %20 + %32 = OpFConvert %31 %20 + %33 = OpFConvert %31 %20 -; The first sample op should get a 16b coordinate - %21 = OpVectorShuffle %33 %31 %32 0 1 +; The first sample op should get a 32b coordinate, even if relaxed + %21 = OpVectorShuffle %34 %32 %33 0 1 ;CHECK: [[uv_16b:%\w+]] = OpVectorShuffle [[vec2_16b_t]] - %22 = OpImageSampleImplicitLod %7 %18 %21 -;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_16b]] + %22 = OpFConvert %19 %21 + %23 = OpImageSampleImplicitLod %7 %18 %22 +;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} {{%\w+}} - OpStore %13 %22 - %23 = OpLoad %15 %17 - %26 = OpLoad %7 %25 + OpStore %13 %23 + %24 = OpLoad %15 %17 + %27 = OpLoad %7 %26 ; The second sample op should get a 32b coordinate - %27 = OpVectorShuffle %19 %26 %26 0 1 + %28 = OpVectorShuffle %19 %27 %27 0 1 ;CHECK: [[uv_32b:%\w+]] = OpVectorShuffle [[vec2_t]] - %28 = OpImageSampleImplicitLod %7 %23 %27 + %29 = OpImageSampleImplicitLod %7 %24 %28 ;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_32b]] - OpStore %13 %28 + OpStore %13 %29 OpReturn OpFunctionEnd )";
diff --git a/test/opt/convert_to_sampled_image_test.cpp b/test/opt/convert_to_sampled_image_test.cpp index 37f6560..31a1786 100644 --- a/test/opt/convert_to_sampled_image_test.cpp +++ b/test/opt/convert_to_sampled_image_test.cpp
@@ -83,7 +83,7 @@ // 6. maximum spec id {"4294967295:0", true, VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{ - 4294967295, 0}})}, + 4294967295u, 0}})}, // 7. minimum spec id {"0:100", true, VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0,
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp index 7d8a325..de25ab9 100644 --- a/test/opt/copy_prop_array_test.cpp +++ b/test/opt/copy_prop_array_test.cpp
@@ -2146,6 +2146,335 @@ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SinglePassRunAndMatch<CopyPropagateArrays>(before, true); } + +TEST_F(CopyPropArrayPassTest, PropCopyLogical) { + const std::string before = R"( +; CHECK: [[v4array_ptr:%\w+]] = OpTypePointer Uniform %14 +; CHECK: [[v4_ptr:%\w+]] = OpTypePointer Uniform %7 +; CHECK: [[ac:%\w+]] = OpAccessChain [[v4array_ptr]] %19 %21 %33 +; CHECK: %47 = OpAccessChain [[v4_ptr]] [[ac]] %37 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %19 %30 %32 + OpSource GLSL 430 + OpName %4 "main" + OpDecorate %14 ArrayStride 16 + OpDecorate %15 ArrayStride 16 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 32 + OpDecorate %17 Block + OpMemberDecorate %17 0 Offset 0 + OpDecorate %19 Binding 0 + OpDecorate %19 DescriptorSet 0 + OpDecorate %28 Block + OpMemberDecorate %28 0 BuiltIn Position + OpMemberDecorate %28 1 BuiltIn PointSize + OpMemberDecorate %28 2 BuiltIn ClipDistance + OpDecorate %32 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 2 +%10 = OpTypeArray %7 %9 +%11 = OpTypeStruct %10 %10 +%14 = OpTypeArray %7 %9 +%15 = OpTypeArray %7 %9 +%16 = OpTypeStruct %14 %15 +%17 = OpTypeStruct %16 +%18 = OpTypePointer Uniform %17 +%19 = OpVariable %18 Uniform +%20 = OpTypeInt 32 1 +%21 = OpConstant %20 0 +%22 = OpTypePointer Uniform %16 +%26 = OpConstant %8 1 +%27 = OpTypeArray %6 %26 +%28 = OpTypeStruct %7 %6 %27 +%29 = OpTypePointer Output %28 +%30 = OpVariable %29 Output +%31 = OpTypePointer Input %7 +%32 = OpVariable %31 Input +%33 = OpConstant %8 0 +%34 = OpTypePointer Input %6 +%38 = OpTypePointer Function %7 +%41 = OpTypePointer Output %7 +%43 = OpTypePointer Function %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel +%44 = OpVariable %43 Function +%23 = OpAccessChain %22 %19 %21 +%24 = OpLoad %16 %23 +%25 = OpCopyLogical %11 %24 +%46 = OpCompositeExtract %10 %25 0 + OpStore %44 %46 +%35 = OpAccessChain %34 %32 %33 +%36 = OpLoad %6 %35 +%37 = OpConvertFToS %20 %36 +%47 = OpAccessChain %38 %44 %37 +%40 = OpLoad %7 %47 +%42 = OpAccessChain %41 %30 %21 + OpStore %42 %40 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch<CopyPropagateArrays>(before, true); +} + +// Ensure that the use of the global variable in a debug instruction does not +// stop copy propagation. We expect the image operand to OpImageTexelPointer to +// be replaced. +TEST_F(CopyPropArrayPassTest, DebugInstNotStore) { + const std::string before = R"( + OpCapability Shader + OpCapability SampledBuffer + OpExtension "SPV_KHR_non_semantic_info" + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "maincomp" + OpExecutionMode %3 LocalSize 16 16 1 + %4 = OpString "" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_6 = OpConstant %uint 6 + %uint_8 = OpConstant %uint 8 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_3 = OpConstant %uint 3 + %uint_32 = OpConstant %uint 32 + %uint_16 = OpConstant %uint 16 + %uint_4 = OpConstant %uint 4 + %16 = OpTypeImage %uint Buffer 2 0 0 2 R32ui +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 + %void = OpTypeVoid + %uint_5 = OpConstant %uint 5 + %uint_70 = OpConstant %uint 70 + %uint_71 = OpConstant %uint 71 + %uint_72 = OpConstant %uint 72 + %uint_17 = OpConstant %uint 17 + %uint_9 = OpConstant %uint 9 + %uint_25 = OpConstant %uint 25 + %uint_14 = OpConstant %uint 14 + %uint_24 = OpConstant %uint 24 + %uint_13 = OpConstant %uint 13 + %29 = OpTypeFunction %void +%_ptr_Function_16 = OpTypePointer Function %16 +%_ptr_Image_uint = OpTypePointer Image %uint +; CHECK: [[GV:%\w+]] = OpVariable {{%\w+}} UniformConstant + %32 = OpVariable %_ptr_UniformConstant_16 UniformConstant + %33 = OpExtInst %void %2 DebugInfoNone + %34 = OpExtInst %void %2 DebugExpression + %35 = OpExtInst %void %2 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0 + %36 = OpExtInst %void %2 DebugTypeVector %35 %uint_3 + %37 = OpExtInst %void %2 DebugSource %4 %4 + %38 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %37 %uint_5 + %39 = OpExtInst %void %2 DebugTypeTemplateParameter %4 %36 %33 %37 %uint_0 %uint_0 + %40 = OpExtInst %void %2 DebugTypeBasic %4 %uint_32 %uint_6 %uint_0 + %41 = OpExtInst %void %2 DebugSource %4 %4 + %42 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %41 %uint_5 + %43 = OpExtInst %void %2 DebugTypeComposite %4 %uint_0 %37 %uint_0 %uint_0 %38 %4 %33 %uint_3 + %44 = OpExtInst %void %2 DebugTypeTemplateParameter %4 %40 %33 %37 %uint_0 %uint_0 + %45 = OpExtInst %void %2 DebugTypeTemplate %43 %44 + %46 = OpExtInst %void %2 DebugTypeFunction %uint_3 %void %40 + %47 = OpExtInst %void %2 DebugFunction %4 %46 %37 %uint_70 %uint_1 %38 %4 %uint_3 %uint_71 + %48 = OpExtInst %void %2 DebugLexicalBlock %37 %uint_71 %uint_1 %47 + %49 = OpExtInst %void %2 DebugLocalVariable %4 %45 %37 %uint_72 %uint_17 %48 %uint_4 + %50 = OpExtInst %void %2 DebugTypeFunction %uint_3 %void + %51 = OpExtInst %void %2 DebugSource %4 %4 + %52 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %51 %uint_5 + %53 = OpExtInst %void %2 DebugFunction %4 %50 %51 %uint_24 %uint_1 %52 %4 %uint_3 %uint_25 + %54 = OpExtInst %void %2 DebugGlobalVariable %4 %45 %37 %uint_17 %uint_16 %38 %4 %32 %uint_8 + %55 = OpExtInst %void %2 DebugTypeMember %4 %40 %37 %uint_14 %uint_7 %uint_0 %uint_32 %uint_3 + %56 = OpExtInst %void %2 DebugTypeComposite %4 %uint_1 %37 %uint_13 %uint_9 %38 %4 %uint_32 %uint_3 %55 + %57 = OpExtInst %void %2 DebugEntryPoint %53 %42 %4 %4 + %3 = OpFunction %void None %29 + %58 = OpLabel + %59 = OpVariable %_ptr_Function_16 Function + %60 = OpLoad %16 %32 + OpStore %59 %60 + %61 = OpExtInst %void %2 DebugDeclare %49 %59 %34 + %62 = OpExtInst %void %2 DebugLine %37 %uint_0 %uint_0 %uint_2 %uint_2 +; CHECK: OpImageTexelPointer %_ptr_Image_uint [[GV]] %uint_0 %uint_0 + %63 = OpImageTexelPointer %_ptr_Image_uint %59 %uint_0 %uint_0 + %64 = OpAtomicIAdd %uint %63 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_1); + SinglePassRunAndMatch<CopyPropagateArrays>(before, false); +} + +TEST_F(CopyPropArrayPassTest, DebugInstNotDominatingStore) { + // Move the debug value to after the new access chain instruction. + const std::string before = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %2 "main" %3 + OpExecutionMode %2 LocalSize 1 1 1 + OpExecutionMode %2 OutputTrianglesEXT + OpExecutionMode %2 OutputVertices 64 + OpExecutionMode %2 OutputPrimitivesEXT 124 + %4 = OpString "" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 + %_struct_10 = OpTypeStruct %_arr_uint_uint_32 +%_ptr_TaskPayloadWorkgroupEXT__struct_10 = OpTypePointer TaskPayloadWorkgroupEXT %_struct_10 + %uint_124 = OpConstant %uint 124 + %uint_64 = OpConstant %uint 64 + %void = OpTypeVoid + %uint_6 = OpConstant %uint 6 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_1024 = OpConstant %uint 1024 + %uint_7 = OpConstant %uint 7 + %uint_8 = OpConstant %uint 8 + %uint_3 = OpConstant %uint 3 + %uint_96 = OpConstant %uint 96 + %uint_11 = OpConstant %uint 11 + %uint_12 = OpConstant %uint 12 + %uint_10 = OpConstant %uint 10 + %uint_16 = OpConstant %uint 16 + %uint_21 = OpConstant %uint 21 + %uint_17 = OpConstant %uint 17 + %uint_26 = OpConstant %uint 26 + %32 = OpTypeFunction %void +%_ptr_Function__arr_uint_uint_32 = OpTypePointer Function %_arr_uint_uint_32 + %3 = OpVariable %_ptr_TaskPayloadWorkgroupEXT__struct_10 TaskPayloadWorkgroupEXT + %34 = OpExtInst %void %1 DebugOperation %uint_0 + %35 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_6 %uint_0 + %36 = OpExtInst %void %1 DebugSource %4 %4 + %37 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %36 %uint_5 + %38 = OpExtInst %void %1 DebugTypeArray %35 %uint_32 + %39 = OpExtInst %void %1 DebugTypeMember %4 %38 %36 %uint_7 %uint_8 %uint_0 %uint_1024 %uint_3 + %40 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_6 %uint_8 %37 %4 %uint_1024 %uint_3 %39 + %41 = OpExtInst %void %1 DebugTypeVector %35 %uint_3 + %42 = OpExtInst %void %1 DebugTypeArray %41 %uint_124 + %43 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0 + %44 = OpExtInst %void %1 DebugTypeVector %43 %uint_3 + %45 = OpExtInst %void %1 DebugTypeMember %4 %44 %36 %uint_11 %uint_12 %uint_0 %uint_96 %uint_3 + %46 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_10 %uint_8 %37 %4 %uint_96 %uint_3 %45 + %47 = OpExtInst %void %1 DebugTypeArray %46 %uint_64 + %48 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void %40 %35 %42 %47 + %49 = OpExtInst %void %1 DebugFunction %4 %48 %36 %uint_16 %uint_1 %37 %4 %uint_3 %uint_21 + %50 = OpExtInst %void %1 DebugLocalVariable %4 %40 %36 %uint_17 %uint_26 %49 %uint_4 %uint_1 + %51 = OpExtInst %void %1 DebugExpression %34 +; CHECK: OpFunction + %2 = OpFunction %void None %32 + %52 = OpLabel + %53 = OpVariable %_ptr_Function__arr_uint_uint_32 Function +; CHECK: [[new_ptr:%\w+]] = OpAccessChain %_ptr_TaskPayloadWorkgroupEXT__arr_uint_uint_32 +; CHECK: OpExtInst %void %1 DebugValue {{%\w+}} [[new_ptr]] + %54 = OpExtInst %void %1 DebugValue %50 %53 %51 %int_0 + %55 = OpLoad %_struct_10 %3 + %56 = OpCompositeExtract %_arr_uint_uint_32 %55 0 + OpStore %53 %56 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_1); + SinglePassRunAndMatch<CopyPropagateArrays>(before, false); +} + +TEST_F(CopyPropArrayPassTest, DebugInstNotDominatingStoreInDifferentBB) { + // Move the debug value to after the new access chain instruction. + const std::string before = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %2 "main" %3 + OpExecutionMode %2 LocalSize 1 1 1 + OpExecutionMode %2 OutputTrianglesEXT + OpExecutionMode %2 OutputVertices 64 + OpExecutionMode %2 OutputPrimitivesEXT 124 + %4 = OpString "" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 + %_struct_10 = OpTypeStruct %_arr_uint_uint_32 +%_ptr_TaskPayloadWorkgroupEXT__struct_10 = OpTypePointer TaskPayloadWorkgroupEXT %_struct_10 + %uint_124 = OpConstant %uint 124 + %uint_64 = OpConstant %uint 64 + %void = OpTypeVoid + %uint_6 = OpConstant %uint 6 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_1024 = OpConstant %uint 1024 + %uint_7 = OpConstant %uint 7 + %uint_8 = OpConstant %uint 8 + %uint_3 = OpConstant %uint 3 + %uint_96 = OpConstant %uint 96 + %uint_11 = OpConstant %uint 11 + %uint_12 = OpConstant %uint 12 + %uint_10 = OpConstant %uint 10 + %uint_16 = OpConstant %uint 16 + %uint_21 = OpConstant %uint 21 + %uint_17 = OpConstant %uint 17 + %uint_26 = OpConstant %uint 26 + %32 = OpTypeFunction %void +%_ptr_Function__arr_uint_uint_32 = OpTypePointer Function %_arr_uint_uint_32 + %3 = OpVariable %_ptr_TaskPayloadWorkgroupEXT__struct_10 TaskPayloadWorkgroupEXT + %34 = OpExtInst %void %1 DebugOperation %uint_0 + %35 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_6 %uint_0 + %36 = OpExtInst %void %1 DebugSource %4 %4 + %37 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %36 %uint_5 + %38 = OpExtInst %void %1 DebugTypeArray %35 %uint_32 + %39 = OpExtInst %void %1 DebugTypeMember %4 %38 %36 %uint_7 %uint_8 %uint_0 %uint_1024 %uint_3 + %40 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_6 %uint_8 %37 %4 %uint_1024 %uint_3 %39 + %41 = OpExtInst %void %1 DebugTypeVector %35 %uint_3 + %42 = OpExtInst %void %1 DebugTypeArray %41 %uint_124 + %43 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0 + %44 = OpExtInst %void %1 DebugTypeVector %43 %uint_3 + %45 = OpExtInst %void %1 DebugTypeMember %4 %44 %36 %uint_11 %uint_12 %uint_0 %uint_96 %uint_3 + %46 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_10 %uint_8 %37 %4 %uint_96 %uint_3 %45 + %47 = OpExtInst %void %1 DebugTypeArray %46 %uint_64 + %48 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void %40 %35 %42 %47 + %49 = OpExtInst %void %1 DebugFunction %4 %48 %36 %uint_16 %uint_1 %37 %4 %uint_3 %uint_21 + %50 = OpExtInst %void %1 DebugLocalVariable %4 %40 %36 %uint_17 %uint_26 %49 %uint_4 %uint_1 + %51 = OpExtInst %void %1 DebugExpression %34 +; CHECK: OpFunction + %2 = OpFunction %void None %32 + %52 = OpLabel + %53 = OpVariable %_ptr_Function__arr_uint_uint_32 Function +; CHECK: [[new_ptr:%\w+]] = OpAccessChain %_ptr_TaskPayloadWorkgroupEXT__arr_uint_uint_32 +; CHECK: OpExtInst %void %1 DebugValue {{%\w+}} [[new_ptr]] + %54 = OpExtInst %void %1 DebugValue %50 %53 %51 %int_0 + %55 = OpLoad %_struct_10 %3 + %56 = OpCompositeExtract %_arr_uint_uint_32 %55 0 + OpBranch %57 + %57 = OpLabel + OpStore %53 %56 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_1); + SinglePassRunAndMatch<CopyPropagateArrays>(before, false); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp index 9c75728..08d8d99 100644 --- a/test/opt/debug_info_manager_test.cpp +++ b/test/opt/debug_info_manager_test.cpp
@@ -767,8 +767,6 @@ %101 = OpExtInst %void %1 DebugScope %22 OpLine %5 13 7 OpStore %100 %31 - OpNoLine - %102 = OpExtInst %void %1 DebugNoScope %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13 OpReturn OpFunctionEnd @@ -877,6 +875,44 @@ EXPECT_EQ(dbg_var->GetInOperand(8), originalOperands[10]); } +TEST(DebugInfoManagerTest, DebugInstructionWithForwardRefsKHR) { + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + OpExtension "SPV_KHR_relaxed_extended_instruction" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_3 = OpConstant %uint 3 + %10 = OpExtInstWithForwardRefsKHR %void %1 DebugTypeFunction %uint_3 %11 + %11 = OpExtInst %void %1 DebugInfoNone + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + Instruction* forward_ref_inst = context->get_def_use_mgr()->GetDef(10); + ASSERT_NE(nullptr, forward_ref_inst); + EXPECT_EQ(forward_ref_inst->opcode(), spv::Op::OpExtInstWithForwardRefsKHR); + + EXPECT_EQ(forward_ref_inst->GetShaderDebugOpcode(), + NonSemanticShaderDebugInfoDebugTypeFunction); + + EXPECT_EQ(forward_ref_inst->GetCommonDebugOpcode(), + CommonDebugInfoDebugTypeFunction); +} + } // namespace } // namespace analysis } // namespace opt
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp index bb0ec03..6fdf91b 100644 --- a/test/opt/eliminate_dead_member_test.cpp +++ b/test/opt/eliminate_dead_member_test.cpp
@@ -958,7 +958,7 @@ ; CHECK: OpMemberDecorate %type__Globals 1 Offset 16 ; CHECK: %type__Globals = OpTypeStruct %float %float ; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 -; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_1 %uint_0 +; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_0 ; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_1 OpCapability Shader OpCapability VariablePointersStorageBuffer @@ -995,12 +995,13 @@ %main = OpFunction %void None %14 %16 = OpLabel %17 = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 - %18 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_1 %uint_0 + %18 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_0 %19 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_2 OpReturn OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); } @@ -1060,6 +1061,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); }
diff --git a/test/opt/feature_manager_test.cpp b/test/opt/feature_manager_test.cpp index 7e8f92c..6366d42 100644 --- a/test/opt/feature_manager_test.cpp +++ b/test/opt/feature_manager_test.cpp
@@ -179,6 +179,45 @@ EXPECT_TRUE(capabilities.contains(spv::Capability::Matrix)); } +TEST_F(FeatureManagerTest, HasConditionalExtension) { + const std::string text = R"( + OpCapability Linkage + OpCapability Shader + OpCapability SpecConditionalINTEL + OpExtension "SPV_INTEL_function_variants" + OpConditionalExtensionINTEL %1 "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %1 = OpSpecConstantTrue %bool + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_TRUE(context->get_feature_mgr()->HasExtension( + Extension::kSPV_KHR_variable_pointers)); +} + +TEST_F(FeatureManagerTest, HasConditionalCapability) { + const std::string text = R"( + OpCapability Linkage + OpCapability Shader + OpCapability SpecConditionalINTEL + OpConditionalCapabilityINTEL %1 Int8 + OpExtension "SPV_INTEL_function_variants" + OpMemoryModel Logical GLSL450 + %bool = OpTypeBool + %1 = OpSpecConstantTrue %bool + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_TRUE(context->get_feature_mgr()->HasCapability(spv::Capability::Int8)); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp index 335e0f5..2d5af28 100644 --- a/test/opt/fold_spec_const_op_composite_test.cpp +++ b/test/opt/fold_spec_const_op_composite_test.cpp
@@ -1,4 +1,5 @@ // Copyright (c) 2016 Google Inc. +// Copyright (c) 2025 Arm Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -737,6 +738,9 @@ "%inner_struct = OpTypeStruct %bool %int %float", "%outer_struct = OpTypeStruct %inner_struct %int", "%flat_struct = OpTypeStruct %bool %int %float", + "%flat_struct_replicate = OpTypeStruct %int %int %int", + "%inner_struct_replicate = OpTypeStruct %float %float %float", + "%outer_struct_replicate = OpTypeStruct %inner_struct_replicate %inner_struct_replicate", // common constants // scalar constants: @@ -826,7 +830,7 @@ StripOpNameInstructions(optimized)); } -// Tests that OpSpecConstantComposite opcodes are replace with +// Tests that OpSpecConstantComposite opcodes are replaced with // OpConstantComposite correctly. INSTANTIATE_TEST_SUITE_P( Composite, FoldSpecConstantOpAndCompositePassTest, @@ -946,6 +950,113 @@ // clang-format on }))); +// Tests that OpSpecConstantCompositeReplicateEXT opcodes are replaced with +// OpConstantCompositeReplicateEXT correctly. +INSTANTIATE_TEST_SUITE_P( + CompositeReplicate, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // normal vector + { + // original + { + "%spec_v2bool = OpSpecConstantCompositeReplicateEXT %v2bool %bool_true", + "%spec_v2uint = OpSpecConstantCompositeReplicateEXT %v2uint %unsigned_one", + "%spec_v2int_a = OpSpecConstantCompositeReplicateEXT %v2int %signed_two", + // Spec constants whose value can not be fully resolved should + // not be processed. + "%spec_int = OpSpecConstant %int 99", + "%spec_v2int_b = OpSpecConstantCompositeReplicateEXT %v2int %spec_int", + }, + // expected + { + "%spec_v2bool = OpConstantCompositeReplicateEXT %v2bool %bool_true", + "%spec_v2uint = OpConstantCompositeReplicateEXT %v2uint %unsigned_one", + "%spec_v2int_a = OpConstantCompositeReplicateEXT %v2int %signed_two", + "%spec_int = OpSpecConstant %int 99", + "%spec_v2int_b = OpSpecConstantCompositeReplicateEXT %v2int %spec_int", + }, + }, + // vector with null constants + { + // original + { + "%null_bool = OpConstantNull %bool", + "%null_int = OpConstantNull %int", + "%spec_v2bool = OpSpecConstantCompositeReplicateEXT %v2bool %null_bool", + "%spec_v3int = OpSpecConstantCompositeReplicateEXT %v3int %null_int", + "%spec_v4int = OpSpecConstantCompositeReplicateEXT %v4int %null_int", + }, + // expected + { + "%null_bool = OpConstantNull %bool", + "%null_int = OpConstantNull %int", + "%spec_v2bool = OpConstantCompositeReplicateEXT %v2bool %null_bool", + "%spec_v3int = OpConstantCompositeReplicateEXT %v3int %null_int", + "%spec_v4int = OpConstantCompositeReplicateEXT %v4int %null_int", + }, + }, + // flat struct + { + // original + { + "%int_1 = OpConstant %int 1", + "%flat_1 = OpSpecConstantCompositeReplicateEXT %flat_struct_replicate %int_1", + // following struct should not be folded as the value of + // %spec_float is not determined. + "%spec_int = OpSpecConstant %int 1", + "%flat_2 = OpSpecConstantCompositeReplicateEXT %flat_struct_replicate %spec_int", + }, + // expected + { + "%int_1 = OpConstant %int 1", + "%flat_1 = OpConstantCompositeReplicateEXT %flat_struct_replicate %int_1", + "%spec_int = OpSpecConstant %int 1", + "%flat_2 = OpSpecConstantCompositeReplicateEXT %flat_struct_replicate %spec_int", + } + }, + // nested struct + { + // original + { + "%float_1 = OpConstant %float 1", + "%inner_1 = OpSpecConstantCompositeReplicateEXT %inner_struct_replicate %float_1", + "%outer_1 = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner_1", + // following structs should not be folded as the value of + // %spec_float is not determined. + "%spec_float = OpSpecConstant %float 1", + "%inner_2 = OpSpecConstantCompositeReplicateEXT %inner_struct_replicate %spec_float", + "%outer_2 = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner_2", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%inner_1 = OpConstantCompositeReplicateEXT %inner_struct_replicate %float_1", + "%outer_1 = OpConstantCompositeReplicateEXT %outer_struct_replicate %inner_1", + "%spec_float = OpSpecConstant %float 1", + "%inner_2 = OpSpecConstantCompositeReplicateEXT %inner_struct_replicate %spec_float", + "%outer_2 = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner_2", + } + }, + // composite constants touched by OpUndef should be skipped + { + // original + { + "%undef = OpUndef %float", + "%inner = OpConstantCompositeReplicateEXT %inner_struct_replicate %undef", + "%outer = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner", + }, + // expected + { + "%undef = OpUndef %float", + "%inner = OpConstantCompositeReplicateEXT %inner_struct_replicate %undef", + "%outer = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner", + }, + }, + // clang-format on + }))); + // Tests for operations that resulting in different types. INSTANTIATE_TEST_SUITE_P( Cast, FoldSpecConstantOpAndCompositePassTest,
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 003c449..8db1350 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp
@@ -208,6 +208,15 @@ #define VEC4_0_ID 106 #define DVEC4_0_ID 106 #define HALF_0_ID 108 +#define UINT_0_ID 109 +#define INT_NULL_ID 110 +#define UINT_NULL_ID 111 +#define ULONG_NULL_ID 120 +#define UBYTE_NULL_ID 121 +#define USHORT_NULL_ID 122 +#define V2USHORT_NULL_ID 123 +#define HALF_3_ID 112 +#define FLOAT_NULL_ID 113 const std::string& Header() { static const std::string header = R"(OpCapability Shader OpCapability Float16 @@ -243,16 +252,23 @@ %ulong = OpTypeInt 64 0 %v2int = OpTypeVector %int 2 %v4int = OpTypeVector %int 4 +%v2ubyte = OpTypeVector %ubyte 2 +%v4ubyte = OpTypeVector %ubyte 4 +%v4byte = OpTypeVector %byte 4 +%v2ushort = OpTypeVector %ushort 2 +%v4ushort = OpTypeVector %ushort 4 %v2short = OpTypeVector %short 2 %v2long = OpTypeVector %long 2 %v4long = OpTypeVector %long 4 %v4float = OpTypeVector %float 4 %v4double = OpTypeVector %double 4 +%v4uint = OpTypeVector %uint 4 %v2uint = OpTypeVector %uint 2 %v2ulong = OpTypeVector %ulong 2 %v2float = OpTypeVector %float 2 %v2double = OpTypeVector %double 2 %v2half = OpTypeVector %half 2 +%v4half = OpTypeVector %half 4 %v2bool = OpTypeVector %bool 2 %m2x2int = OpTypeMatrix %v2int 2 %mat4v2float = OpTypeMatrix %v2float 4 @@ -268,22 +284,38 @@ %_ptr_half = OpTypePointer Function %half %_ptr_long = OpTypePointer Function %long %_ptr_ulong = OpTypePointer Function %ulong +%_ptr_ubyte = OpTypePointer Function %ubyte +%_ptr_ushort = OpTypePointer Function %ushort %_ptr_v2int = OpTypePointer Function %v2int %_ptr_v4int = OpTypePointer Function %v4int %_ptr_v4float = OpTypePointer Function %v4float %_ptr_v4double = OpTypePointer Function %v4double +%_ptr_v4half = OpTypePointer Function %v4half %_ptr_struct_v2int_int_int = OpTypePointer Function %struct_v2int_int_int %_ptr_v2float = OpTypePointer Function %v2float %_ptr_v2double = OpTypePointer Function %v2double +%_ptr_v2ushort = OpTypePointer Function %v2ushort %int_2 = OpConstant %int 2 %int_arr_2 = OpTypeArray %int %int_2 +%short_n1 = OpConstant %short -1 %short_0 = OpConstant %short 0 %short_2 = OpConstant %short 2 %short_3 = OpConstant %short 3 %short_n5 = OpConstant %short -5 %ubyte_1 = OpConstant %ubyte 1 +%ubyte_a = OpConstant %ubyte 10 +%ubyte_b = OpConstant %ubyte 11 +%ubyte_c = OpConstant %ubyte 12 +%ubyte_d = OpConstant %ubyte 13 %byte_n1 = OpConstant %byte -1 +%byte_0 = OpConstant %byte 0 +%ushort_0 = OpConstant %ushort 0 +%ushort_1 = OpConstant %ushort 1 +%ushort_2 = OpConstant %ushort 2 +%ushort_3 = OpConstant %ushort 3 +%122 = OpConstantNull %ushort ; Need a def with an numerical id to define id maps. %100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps. +%110 = OpConstantNull %int ; Need a def with an numerical id to define id maps. %103 = OpConstant %int 7 ; Need a def with an numerical id to define id maps. %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 @@ -314,6 +346,11 @@ %long_max = OpConstant %long 9223372036854775807 %ulong_7 = OpConstant %ulong 7 %ulong_4611686018427387904 = OpConstant %ulong 4611686018427387904 +%109 = OpConstant %uint 0 ; Need a def with an numerical id to define id maps. +%111 = OpConstantNull %uint ; Need a def with an numerical id to define id maps. +%120 = OpConstantNull %ulong ; Need a def with an numerical id to define id maps. +%121 = OpConstantNull %ubyte ; Need a def with an numerical id to define id maps. +%123 = OpConstantNull %v2ushort ; Need a def with an numerical id to define id maps. %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 %uint_2 = OpConstant %uint 2 @@ -323,6 +360,7 @@ %uint_42 = OpConstant %uint 42 %uint_2147483649 = OpConstant %uint 2147483649 %uint_max = OpConstant %uint 4294967295 +%uint_0x0000ffff = OpConstant %uint 65535 %ulong_0 = OpConstant %ulong 0 %ulong_1 = OpConstant %ulong 1 %ulong_2 = OpConstant %ulong 2 @@ -338,6 +376,8 @@ %v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4 %v2int_min_max = OpConstantComposite %v2int %int_min %int_max %v2short_2_n5 = OpConstantComposite %v2short %short_2 %short_n5 +%v2short_n1_0 = OpConstantComposite %v2short %short_n1 %short_0 +%v2short_null = OpConstantNull %v2short %v2long_2_2 = OpConstantComposite %v2long %long_2 %long_2 %v2long_2_3 = OpConstantComposite %v2long %long_2 %long_3 %v2bool_null = OpConstantNull %v2bool @@ -352,6 +392,7 @@ %struct_undef_0_0 = OpConstantComposite %struct_v2int_int_int %v2int_undef %int_0 %int_0 %float_n1 = OpConstant %float -1 %104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps. +%113 = OpConstantNull %float ; Need a def with an numerical id to define id maps. %float_null = OpConstantNull %float %float_0 = OpConstant %float 0 %float_n0 = OpConstant %float -0.0 @@ -396,9 +437,13 @@ %v2double_2_0p5 = OpConstantComposite %v2double %double_2 %double_0p5 %v2double_null = OpConstantNull %v2double %108 = OpConstant %half 0 +%half_0p5 = OpConstant %half 0.5 %half_1 = OpConstant %half 1 %half_2 = OpConstant %half 2 +%112 = OpConstant %half 3 +%half_null = OpConstantNull %half %half_0_1 = OpConstantComposite %v2half %108 %half_1 +%v4half_0_1_0_0 = OpConstantComposite %v4half %108 %half_1 %108 %108 %106 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 %v4float_0_0_0_0 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 %v4float_0_0_0_1 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 @@ -425,6 +470,14 @@ %uint_0x3f800000 = OpConstant %uint 0x3f800000 %uint_0xbf800000 = OpConstant %uint 0xbf800000 %v2uint_0x3f800000_0xbf800000 = OpConstantComposite %v2uint %uint_0x3f800000 %uint_0xbf800000 +%v4uint_1_0x0000ffff_uint_0_uint_max = OpConstantComposite %v4uint %uint_1 %uint_0x0000ffff %uint_0 %uint_max +%v2uint_1_null = OpConstantComposite %v2uint %uint_1 %111 +%v2uint_null = OpConstantNull %v2uint +%v2ushort_1_null = OpConstantComposite %v2ushort %ushort_1 %122 +%v4ushort_0_1_2_3 = OpConstantComposite %v4ushort %ushort_0 %ushort_1 %ushort_2 %ushort_3 +%v2ubyte_a_b = OpConstantComposite %v2ubyte %ubyte_a %ubyte_b +%v4ubyte_a_b_c_d = OpConstantComposite %v4ubyte %ubyte_a %ubyte_b %ubyte_c %ubyte_d +%v4byte_n1_0_0_0 = OpConstantComposite %v4byte %byte_n1 %byte_0 %byte_0 %byte_0 %long_0xbf8000003f800000 = OpConstant %long 0xbf8000003f800000 %int_0x3FF00000 = OpConstant %int 0x3FF00000 %int_0x00000000 = OpConstant %int 0x00000000 @@ -765,7 +818,7 @@ InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%2 = OpExtInst %int %1 UMin %int_3 %int_4\n" + + "%2 = OpExtInst %int %1 SMin %int_3 %int_4\n" + "OpReturn\n" + "OpFunctionEnd", 2, 3), @@ -777,7 +830,39 @@ "OpReturn\n" + "OpFunctionEnd", 2, 2), - // Test case 34: fold UMax 3 4 + // Test case 34: fold SMin short -5 2 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %short %1 SMin %short_n5 %short_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -5), + // Test case 35: fold UMin ushort 2 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %ushort %1 UMin %ushort_2 %ushort_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 36: fold SMin int OpConstNull -1 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SMin %110 %int_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -1), + // Test case 37: fold SMin int -1 OpConstNull + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SMin %int_n1 %110\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -1), + // Test case 38: fold UMax 3 4 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -785,7 +870,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 4), - // Test case 35: fold UMax 3 2 + // Test case 39: fold UMax 3 2 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -793,15 +878,15 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 36: fold SMax 3 4 + // Test case 40: fold SMax 3 4 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%2 = OpExtInst %int %1 UMax %int_3 %int_4\n" + + "%2 = OpExtInst %int %1 SMax %int_3 %int_4\n" + "OpReturn\n" + "OpFunctionEnd", 2, 4), - // Test case 37: fold SMax 3 2 + // Test case 41: fold SMax 3 2 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -809,7 +894,31 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 38: fold UClamp 2 3 4 + // Test case 42: fold SMax short -5 2 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %short %1 SMax %short_n5 %short_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 43: fold UMax ushort 2 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %ushort %1 UMax %ushort_2 %ushort_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 44: fold SMax int OpConstNull 1 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SMax %110 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 45: fold UClamp 2 3 4 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -817,7 +926,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 39: fold UClamp 2 0 4 + // Test case 46: fold UClamp 2 0 4 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -825,7 +934,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 2), - // Test case 40: fold UClamp 2 0 1 + // Test case 47: fold UClamp 2 0 1 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -833,7 +942,15 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 41: fold SClamp 2 3 4 + // Test case 48: fold UClamp short 2 0 1 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %ushort %1 UClamp %ushort_2 %ushort_0 %ushort_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 49: fold SClamp 2 3 4 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -841,7 +958,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 42: fold SClamp 2 0 4 + // Test case 50: fold SClamp 2 0 4 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -849,7 +966,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 2), - // Test case 43: fold SClamp 2 0 1 + // Test case 51: fold SClamp 2 0 1 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -857,7 +974,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 44: SClamp 1 2 x + // Test case 52: SClamp 1 2 x InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -866,7 +983,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 2), - // Test case 45: SClamp 2 x 1 + // Test case 53: SClamp 2 x 1 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -875,7 +992,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 46: UClamp 1 2 x + // Test case 54: UClamp 1 2 x InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -884,7 +1001,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 2), - // Test case 47: UClamp 2 x 1 + // Test case 55: UClamp 2 x 1 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -893,7 +1010,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 48: Bit-cast int 0 to unsigned int + // Test case 56: Bit-cast int 0 to unsigned int InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -901,7 +1018,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 49: Bit-cast int -24 to unsigned int + // Test case 57: Bit-cast int -24 to unsigned int InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -909,7 +1026,15 @@ "OpReturn\n" + "OpFunctionEnd", 2, static_cast<uint32_t>(-24)), - // Test case 50: Bit-cast float 1.0f to unsigned int + // Test case 58: Bit-cast int const null to unsigned int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %110\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 59: Bit-cast float 1.0f to unsigned int InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -917,7 +1042,15 @@ "OpReturn\n" + "OpFunctionEnd", 2, static_cast<uint32_t>(0x3f800000)), - // Test case 51: Bit-cast ushort 0xBC00 to ushort + // Test case 60: Bit-cast float constant null to unsigned int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 61: Bit-cast ushort 0xBC00 to ushort InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -925,7 +1058,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00), - // Test case 52: Bit-cast short 0xBC00 to ushort + // Test case 62: Bit-cast short 0xBC00 to ushort InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -933,7 +1066,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00), - // Test case 53: Bit-cast half 1 to ushort + // Test case 63: Bit-cast half 1 to ushort InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -941,7 +1074,15 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x3C00), - // Test case 54: Bit-cast ushort 0xBC00 to short + // Test case 64: Bit-cast half const null to ushort + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %ushort %half_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x0000), + // Test case 65: Bit-cast ushort 0xBC00 to short InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -949,7 +1090,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00), - // Test case 55: Bit-cast short 0xBC00 to short + // Test case 66: Bit-cast short 0xBC00 to short InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -957,7 +1098,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00), - // Test case 56: Bit-cast half 1 to short + // Test case 67: Bit-cast half 1 to short InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -965,7 +1106,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x3C00), - // Test case 57: Bit-cast ushort 0xBC00 to half + // Test case 68: Bit-cast ushort 0xBC00 to half InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -973,7 +1114,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00), - // Test case 58: Bit-cast short 0xBC00 to half + // Test case 69: Bit-cast short 0xBC00 to half InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -981,7 +1122,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00), - // Test case 59: Bit-cast half 1 to half + // Test case 70: Bit-cast half 1 to half InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -989,7 +1130,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x3C00), - // Test case 60: Bit-cast ubyte 1 to byte + // Test case 71: Bit-cast ubyte 1 to byte InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -997,7 +1138,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 61: Bit-cast byte -1 to ubyte + // Test case 72: Bit-cast byte -1 to ubyte InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1005,7 +1146,63 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFF), - // Test case 62: Negate 2. + // Test case 73: Bit-cast v2shorts (-5,2) to uint + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %v2short_2_n5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFB0002), + // Test case 74: Bit-cast v4ubytes (0xA, 0xB, 0xC, 0xD) to int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %int %v4ubyte_a_b_c_d\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x0D0C0B0A), + // Test case 75: Bit-cast v2ushort (1, OpConstNull) to int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %int %v2ushort_1_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000001), + // Test case 76: Bit-cast v2short OpConstNull to int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %int %v2short_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 77: Bit-cast v2ubytes (0xA, 0xB) to ushort + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %int %v2ubyte_a_b\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x0B0A), + // Test case 78: Bit-cast v2short (-1, 0) to uint + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %v2short_n1_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x0000FFFF), + // Test case 79: Bit-cast v4bytes (-1, 0, 0, 0) to uint + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %v4byte_n1_0_0_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x000000FF), + // Test case 80: Negate 2. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1013,7 +1210,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, -2), - // Test case 63: Negate negative short. + // Test case 81: Negate negative short. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1021,7 +1218,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x4400 /* expected to be sign extended. */), - // Test case 64: Negate positive short. + // Test case 82: Negate positive short. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1029,7 +1226,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00 /* expected to be sign extended. */), - // Test case 65: Negate a negative short. + // Test case 83: Negate a negative short. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1037,7 +1234,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x4400 /* expected to be zero extended. */), - // Test case 66: Negate positive short. + // Test case 84: Negate positive short. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1045,7 +1242,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00 /* expected to be zero extended. */), - // Test case 67: Fold 2 + 3 (short) + // Test case 85: Fold 2 + 3 (short) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1053,7 +1250,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 5), - // Test case 68: Fold 2 + -5 (short) + // Test case 86: Fold 2 + -5 (short) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1061,7 +1258,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, -3), - // Test case 69: Fold int(3ll) + // Test case 87: Fold int(3ll) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1069,7 +1266,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 70: Fold short(-3ll) + // Test case 88: Fold short(-3ll) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1077,7 +1274,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, -3), - // Test case 71: Fold short(32768ll) - This should do a sign extend when + // Test case 89: Fold short(32768ll) - This should do a sign extend when // converting to short. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -1086,7 +1283,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, -32768), - // Test case 72: Fold short(-57344) - This should do a sign extend when + // Test case 90: Fold short(-57344) - This should do a sign extend when // converting to short making the upper bits 0. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -1095,7 +1292,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 8192), - // Test case 73: Fold int(-5(short)). The -5 should be interpreted as an unsigned value, and be zero extended to 32-bits. + // Test case 91: Fold int(-5(short)). The -5 should be interpreted as an unsigned value, and be zero extended to 32-bits. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1103,14 +1300,53 @@ "OpReturn\n" + "OpFunctionEnd", 2, 65531), - // Test case 74: Fold short(-24(int)). The upper bits should be cleared. So 0xFFFFFFE8 should become 0x0000FFE8. + // Test case 92: Fold short(-24(int)). The upper bits should be cleared. So 0xFFFFFFE8 should become 0x0000FFE8. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%2 = OpUConvert %ushort %int_n24\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 65512) + 2, 65512), + // Test case 93: Fold BitReverse of 1 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %uint %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000), + // Test case 94: Fold BitReverse of 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %uint %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 95: Fold BitReverse of uint max + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %uint %uint_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xffffffff), + // Test case 96: Fold BitReverse of 0x0000FFFF + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %uint %uint_0x0000ffff\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 0xffff0000), + // Test case 97: Fold BitReverse of OpConstNull + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %uint %111\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) )); // clang-format on @@ -1130,6 +1366,7 @@ }); } +// clang-format off INSTANTIATE_TEST_SUITE_P( TestCase, LongIntegerInstructionFoldingTest, ::testing::Values( @@ -1274,7 +1511,41 @@ Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%2 = OpUConvert %ulong %short_n5\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 65531ull))); + 2, 65531ull), + // Test case 16: fold SMin long 0 -1. + InstructionFoldingCase<uint64_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %long %1 SMin %long_0 %long_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -1ll), + // Test case 17: fold UMin ulong 9223372036854775809 max. + InstructionFoldingCase<uint64_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %ulong %1 UMin %ulong_9223372036854775809 %ulong_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 9223372036854775809ull), + // Test case 18: fold SMax long 0 -1. + InstructionFoldingCase<uint64_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %long %1 SMax %long_0 %long_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0ll), + // Test case 19: fold UMax ulong 9223372036854775809 max. + InstructionFoldingCase<uint64_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %ulong %1 UMax %ulong_9223372036854775809 %ulong_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 18446744073709551615ull) + )); +// clang-format on using UIntVectorInstructionFoldingTest = ::testing::TestWithParam<InstructionFoldingCase<std::vector<uint32_t>>>; @@ -1285,7 +1556,8 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); + CheckForExpectedVectorConstant( inst, tc.expected_result, [](const analysis::Constant* c) { return c->GetU32(); }); @@ -1298,42 +1570,59 @@ InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%n = OpVariable %_ptr_int Function\n" + - "%load = OpLoad %int %n\n" + "%2 = OpVectorShuffle %v2int %v2int_2_2 %v2int_2_3 0 3\n" + "OpReturn\n" + "OpFunctionEnd", 2, {2,3}), + // Test case 1: fold vectorshuffle of nullconstant of type vec2int InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%n = OpVariable %_ptr_int Function\n" + - "%load = OpLoad %int %n\n" + "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 3\n" + "OpReturn\n" + "OpFunctionEnd", 2, {0,3}), - // Test case 4: fold bit-cast int -24 to unsigned int + // Test case 2: fold bit-cast int -24 to unsigned int InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%n = OpVariable %_ptr_int Function\n" + - "%load = OpLoad %int %n\n" + "%2 = OpBitcast %v2uint %v2int_min_max\n" + "OpReturn\n" + "OpFunctionEnd", 2, {2147483648, 2147483647}), - // Test case 5: fold SNegate vector of uint + // Test case 3: fold bit-cast nullconstant of type vec2uint to int2 InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%n = OpVariable %_ptr_int Function\n" + - "%load = OpLoad %int %n\n" + + "%2 = OpBitcast %v2int %v2uint_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0, 0}), + // Test case 4: fold bit-cast if any component is nullconstant + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2int %v2uint_1_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1, 0}), + // Test case 5: fold bit-cast v4ushort (0, 1, 2, 3) to v2uint + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2uint %v4ushort_0_1_2_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0x00010000, 0x00030002}), + // Test case 6: fold SNegate vector of uint + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + "%2 = OpSNegate %v2uint %v2uint_0x3f800000_0xbf800000\n" + "OpReturn\n" + "OpFunctionEnd", - 2, {static_cast<uint32_t>(-0x3f800000), static_cast<uint32_t>(-0xbf800000)}), - // Test case 6: fold vector components of uint (including integer overflow) + 2, {static_cast<uint32_t>(-0x3f800000), static_cast<uint32_t>(-0xbf800000ll)}), + // Test case 7: fold vector components of uint (including integer overflow) InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1341,7 +1630,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, {0x7f000000u, 0x7f000000u}), - // Test case 6: fold vector components of uint + // Test case 8: fold vector components of uint InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1349,14 +1638,38 @@ "OpReturn\n" + "OpFunctionEnd", 2, {2,static_cast<uint32_t>(-5)}), - // Test case 6: fold vector components of uint (incuding integer overflow) + // Test case 9: fold vector components of uint (incuding integer overflow) InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%2 = OpUConvert %v2uint %v2short_2_n5\n" + "OpReturn\n" + "OpFunctionEnd", - 2, {2,65531}) + 2, {2,65531}), + // Test case 10: fold bitreverse of a vector of uint + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %v4uint %v4uint_1_0x0000ffff_uint_0_uint_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0x80000000, 0xffff0000, 0, 0xffffffff}), + // Test case 11: Fold bitreverse of ConstantNull of type v2uint + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %v2uint %v2uint_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0, 0}), + // Test case 12: Fold bitreverse if any component is NullConstant + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitReverse %v2uint %v2uint_1_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0x80000000, 0}) )); // clang-format on @@ -1369,7 +1682,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); CheckForExpectedVectorConstant( inst, tc.expected_result, @@ -1423,7 +1736,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); CheckForExpectedVectorConstant( inst, tc.expected_result, [](const analysis::Constant* c) { return c->GetU64(); }); @@ -1468,7 +1781,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); CheckForExpectedVectorConstant( inst, tc.expected_result, [](const analysis::Constant* c) { return c->GetDouble(); }); @@ -1568,14 +1881,14 @@ std::unique_ptr<IRContext> context; Instruction* inst; - std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1); + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_5); CheckForExpectedVectorConstant(inst, tc.expected_result, [](const analysis::Constant* c){ return c->GetFloat();}); } // clang-format off INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest, ::testing::Values( - // Test case 0: FMix {2.0, 2.0}, {2.0, 3.0} {0.2,0.5} + // Test case 0: FMix {2.0, 3.0}, {0.0, 0.0} {0.2,0.5} InstructionFoldingCase<std::vector<float>>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -1684,7 +1997,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); if (inst->opcode() == spv::Op::OpCopyObject) { @@ -1753,7 +2066,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); CheckForExpectedScalarConstant( inst, tc.expected_result, [](const analysis::Constant* c) { return c->AsBoolConstant()->value(); }); @@ -2345,7 +2658,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); CheckForExpectedScalarConstant(inst, tc.expected_result, [](const analysis::Constant* c) { @@ -2353,6 +2666,27 @@ }); } +using FloatBitsInstructionFoldingTest = + ::testing::TestWithParam<InstructionFoldingCase<uint32_t>>; + +TEST_P(FloatBitsInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); + + CheckForExpectedScalarConstant( + inst, tc.expected_result, [](const analysis::Constant* c) { + float f = c->AsFloatConstant()->GetFloatValue(); + uint32_t fbits{}; + static_assert(sizeof(float) == sizeof(uint32_t)); + std::memcpy(&fbits, &f, sizeof(uint32_t)); + return fbits; + }); +} + // Not testing NaNs because there are no expectations concerning NaNs according // to the "Precision and Operation of SPIR-V Instructions" section of the Vulkan // specification. @@ -2745,8 +3079,417 @@ "%2 = OpFDiv %float %float_0 %float_n0\n" + "OpReturn\n" + "OpFunctionEnd", + 2, std::numeric_limits<float>::quiet_NaN()), + // Test case 47: NMin 1.0 4.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_1 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 48: NMin 4.0 0.2 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_4 %float_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.2f), + // Test case 49: NMin NaN 4.0 + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_nan %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 50: NMin 2.0 NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_2 %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0f), + // Test case 51: NMin NaN NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_nan %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<float>::quiet_NaN()), + // Test case 52: NMax 1.0 4.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_1 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 53: NMax 1.0 0.2 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_1 %float_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 54: NMax 1.0 NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_1 %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 55: NMax NaN 0.5 + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_nan %float_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5f), + // Test case 56: NMax NaN NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_nan %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<float>::quiet_NaN()), + // Test case 57: NClamp 1.0 0.2 4.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_1 %float_0p2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 58: NClamp 0.2 2.0 4.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_0p2 %float_2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0f), + // Test case 59: NClamp 2049.0 2.0 4.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_2049 %float_2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 60: NClamp 1.0 2.0 x + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %float\n" + + "%2 = OpExtInst %float %1 NClamp %float_1 %float_2 %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 61: NClamp 1.0 x 0.5 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %float\n" + + "%2 = OpExtInst %float %1 NClamp %float_1 %undef %float_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 62: NClamp NaN 2.0 4.0 + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_nan %float_2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0f), + // Test case 63: NClamp 2049.0 NaN 4.0 + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_2049 %float_nan %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 64: NClamp 1.0 2.0 NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_1 %float_2 %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0f), + // Test case 65: NClamp 4.0 NaN NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_4 %float_nan %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 66: NClamp NaN NaN NaN + InstructionFoldingCase<float>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_nan %float_nan %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", 2, std::numeric_limits<float>::quiet_NaN()) )); + +INSTANTIATE_TEST_SUITE_P(MinMaxZeroFoldingTest, FloatBitsInstructionFoldingTest, +::testing::Values( + // Test case 0: Fold FMin 0.0 -0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMin %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 1: Fold FMin -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMin %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 2: Fold FMax 0.0 -0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMax %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 3: Fold FMax -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMax %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 4: Fold FClamp -0.0 0.0 1.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_n0 %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 5: Fold FClamp 0.0 -0.0 1.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_0 %float_n0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 6: Fold FClamp 1.0 -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_1 %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 7: Fold FClamp 0.0 -1.0 -0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_0 %float_n1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 8: Fold FClamp -0.0 -1.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_n0 %float_n1 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 9: Fold FClamp -1.0 -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_n1 %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 10: Fold NMin 0.0 -0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 11: Fold NMin -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMin %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 12: Fold NMax 0.0 -0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 13: Fold NMax -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NMax %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 14: Fold NClamp -0.0 0.0 1.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_n0 %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 15: Fold NClamp 0.0 -0.0 1.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_0 %float_n0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 16: Fold NClamp 1.0 -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_1 %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x00000000u), + // Test case 17: Fold NClamp 0.0 -1.0 -0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_0 %float_n1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 18: Fold NClamp -0.0 -1.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_n0 %float_n1 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u), + // Test case 19: Fold NClamp -1.0 -0.0 0.0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 NClamp %float_n1 %float_n0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x80000000u) +)); + +INSTANTIATE_TEST_SUITE_P(RedundantDivFloatTest, FloatInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold x / x + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFDiv %float %3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 1: Fold -x / x + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpFNegate %float %3\n" + + "%2 = OpFDiv %float %4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -1.0), + // Test case 2: Fold x / -x + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpFNegate %float %3\n" + + "%2 = OpFDiv %float %3 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -1.0) + )); + +INSTANTIATE_TEST_SUITE_P(RedundantDivIntTest, IntegerInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold x / x + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpSDiv %int %3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 1: Fold x / x + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpUDiv %uint %3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 2: Fold -x / x + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpSNegate %int %3\n" + + "%2 = OpSDiv %int %4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT32_MAX), + // Test case 3: Fold x / -x + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpSNegate %int %3\n" + + "%2 = OpSDiv %int %3 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT32_MAX) + )); + // clang-format on using DoubleInstructionFoldingTest = @@ -2758,7 +3501,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); CheckForExpectedScalarConstant( inst, tc.expected_result, [](const analysis::Constant* c) { return c->AsFloatConstant()->GetDoubleValue(); @@ -2995,6 +3738,168 @@ "%2 = OpFDiv %double %double_0 %double_n0\n" + "OpReturn\n" + "OpFunctionEnd", + 2, std::numeric_limits<double>::quiet_NaN()), + // Test case 27: NMin 1.0 4.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMin %double_1 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 28: NMin 4.0 0.2 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMin %double_4 %double_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.2), + // Test case 29: NMax 1.0 4.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMax %double_1 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 30: NMin NaN 4.0 + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMin %double_nan %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 31: NMin 2.0 NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMin %double_2 %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 32: NMin NaN NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMin %double_nan %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<double>::quiet_NaN()), + // Test case 33: NMax 1.0 0.2 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMax %double_1 %double_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 34: NMax 1.0 NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMax %double_1 %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 35: NMax NaN 0.5 + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMax %double_nan %double_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 36: NMax NaN NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NMax %double_nan %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<double>::quiet_NaN()), + // Test case 37: NClamp 1.0 0.2 4.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_1 %double_0p2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 38: NClamp 0.2 2.0 4.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_0p2 %double_2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 39: NClamp 5.0 2.0 4.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_5 %double_2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 40: NClamp 1.0 2.0 x + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 NClamp %double_1 %double_2 %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 41: NClamp 1.0 x 0.5 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 NClamp %double_1 %undef %double_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 42: NClamp NaN 2.0 4.0 + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_nan %double_2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 43: NClamp 5.0 NaN 4.0 + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_5 %double_nan %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 44: NClamp 1.0 2.0 NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_1 %double_2 %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 45: NClamp 4.0 NaN NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_4 %double_nan %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 46: NClamp NaN NaN NaN + InstructionFoldingCase<double>( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 NClamp %double_nan %double_nan %double_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", 2, std::numeric_limits<double>::quiet_NaN()) )); // clang-format on @@ -3696,7 +4601,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); EXPECT_TRUE((inst == nullptr) == (tc.expected_result == 0)); if (inst != nullptr) { @@ -4603,7 +5508,18 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 9: Fold n * 0.0 + // Test case 9: Don't fold n % 1.0 + // If `n` is not a whole number, the answer is not 0. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMod %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 10: Fold n * 0.0 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4613,7 +5529,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, FLOAT_0_ID), - // Test case 10: Fold 0.0 * n + // Test case 11: Fold 0.0 * n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4623,7 +5539,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, FLOAT_0_ID), - // Test case 11: Fold 0.0 / n + // Test case 12: Fold 0.0 / n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4633,7 +5549,17 @@ "OpReturn\n" + "OpFunctionEnd", 2, FLOAT_0_ID), - // Test case 12: Don't fold mix(a, b, 2.0) + // Test case 13: Fold 0.0 % n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMod %float %104 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, FLOAT_0_ID), + // Test case 14: Don't fold mix(a, b, 2.0) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4645,7 +5571,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 13: Fold mix(a, b, 0.0) + // Test case 15: Fold mix(a, b, 0.0) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4657,7 +5583,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 14: Fold mix(a, b, 1.0) + // Test case 16: Fold mix(a, b, 1.0) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4669,7 +5595,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 4), - // Test case 15: Fold vector fadd with null + // Test case 17: Fold vector fadd with null InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4679,7 +5605,7 @@ "OpReturn\n" + "OpFunctionEnd", 3, 2), - // Test case 16: Fold vector fadd with null + // Test case 18: Fold vector fadd with null InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4689,7 +5615,7 @@ "OpReturn\n" + "OpFunctionEnd", 3, 2), - // Test case 17: Fold vector fsub with null + // Test case 19: Fold vector fsub with null InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4699,7 +5625,7 @@ "OpReturn\n" + "OpFunctionEnd", 3, 2), - // Test case 18: Fold 0.0(half) * n + // Test case 20: Fold 0.0(half) * n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4709,7 +5635,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, HALF_0_ID), - // Test case 19: Don't fold 1.0(half) * n + // Test case 21: Don't fold 1.0(half) * n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4719,7 +5645,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 20: Don't fold 1.0 * 1.0 (half) + // Test case 22: Don't fold 1.0 * 1.0 (half) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4727,7 +5653,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 21: Don't fold (0.0, 1.0) * (0.0, 1.0) (half) + // Test case 23: Don't fold (0.0, 1.0) * (0.0, 1.0) (half) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4735,7 +5661,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 22: Don't fold (0.0, 1.0) dotp (0.0, 1.0) (half) + // Test case 24: Don't fold (0.0, 1.0) dotp (0.0, 1.0) (half) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4743,7 +5669,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 23: Don't fold 1.0(half) / 2.0(half) + // Test case 25: Don't fold 1.0(half) / 2.0(half) // We do not have to code to emulate 16-bit float operations. Just make sure we do not crash. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -4754,7 +5680,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 24: Don't fold OpFNegate for cooperative matrices. + // Test case 26: Don't fold OpFNegate for cooperative matrices. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4762,7 +5688,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 25: Don't fold OpIAdd for cooperative matrices. + // Test case 27: Don't fold OpFAdd for cooperative matrices. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4770,7 +5696,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 26: Don't fold OpISub for cooperative matrices. + // Test case 28: Don't fold OpFSub for cooperative matrices. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4778,7 +5704,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 27: Don't fold OpIMul for cooperative matrices. + // Test case 29: Don't fold OpFMul for cooperative matrices. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4786,7 +5712,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 28: Don't fold OpSDiv for cooperative matrices. + // Test case 30: Don't fold OpFDiv for cooperative matrices. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -4794,14 +5720,66 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 29: Don't fold OpMatrixTimesScalar for cooperative matrices. + // Test case 31: Don't fold OpMatrixTimesScalar for cooperative matrices. InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%2 = OpMatrixTimesScalar %float_coop_matrix %undef_float_coop_matrix %float_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 0) + 2, 0), + // Test case 32: Don't fold FMix half (1.0, 2.0, 0.5) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %half %1 FMix %half_1 %half_2 %half_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 33: Fold FMix half (3.0, 2.0, 0.0) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %half %1 FMix %112 %half_2 %108\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, HALF_3_ID), + // Test case 34: Fold FMix half (3.0, 2.0, null) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %half %1 FMix %112 %half_2 %half_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, HALF_3_ID), + // Test case 35: Don't fold FMix half (1.0, 2.0, 1.0) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %half %1 FMix %half_1 %half_2 %half_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 36: Fold n - n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFSub %float %3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, FLOAT_NULL_ID), + // Test case 37: Fold n - n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpISub %int %3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_NULL_ID) )); INSTANTIATE_TEST_SUITE_P(DoubleRedundantFoldingTest, GeneralInstructionFoldingTest, @@ -5020,7 +5998,27 @@ "OpReturn\n" + "OpFunctionEnd", 2, DVEC4_0_ID), - // Test case 2: Fold a * vec4(1.0, 1.0, 1.0, 1.0) + // Test case 2: Fold a + vec4(0.0, 0.0, 0.0, 0.0) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFAdd %v4double %3 %106\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 3: Fold a - vec4(0.0, 0.0, 0.0, 0.0) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFSub %v4double %3 %106\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 4: Fold a * vec4(1.0, 1.0, 1.0, 1.0) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5054,7 +6052,57 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 2: Fold n + 0 + // Test case 2: Fold n | 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpBitwiseOr %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 3: Fold n ^ 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpBitwiseXor %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 4: Fold n >> 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpShiftRightLogical %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 5: Fold n >> 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpShiftRightArithmetic %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 6: Fold n << 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpShiftLeftLogical %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 7: Fold n + 0 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5064,7 +6112,37 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 3: Fold 0 + n + // Test case 8: Fold n - 0 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpISub %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 9: Fold 0 | n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpBitwiseOr %uint %uint_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 10: Fold 0 ^ n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpBitwiseXor %uint %uint_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 11: Fold 0 + n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5074,7 +6152,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 4: Don't fold n + (1,0) + // Test case 12: Don't fold n + (1,0) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5084,7 +6162,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 5: Don't fold (1,0) + n + // Test case 13: Don't fold (1,0) + n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5094,7 +6172,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 6: Fold n + (0,0) + // Test case 14: Fold n + (0,0) InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5104,7 +6182,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 7: Fold (0,0) + n + // Test case 15: Fold (0,0) + n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -5114,7 +6192,117 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 8: Don't fold because of undefined value. Using 4294967295 + // Test case 16: Fold n | (0,0) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%3 = OpLoad %v2int %n\n" + + "%2 = OpBitwiseOr %v2int %3 %v2int_0_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 17: Fold (0,0) | n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%3 = OpLoad %v2int %n\n" + + "%2 = OpBitwiseOr %v2int %v2int_0_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 18: Fold 0 >> n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpShiftRightLogical %uint %109 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_0_ID), + // Test case 19: Fold 0 >> n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpShiftRightArithmetic %uint %109 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_0_ID), + // Test case 20: Fold 0 << n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpShiftLeftLogical %uint %109 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_0_ID), + // Test case 21: Fold 0 / n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpSDiv %int %100 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_0_ID), + // Test case 22: Fold 0 / n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpUDiv %uint %109 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_0_ID), + // Test case 23: Fold 0 % n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpSMod %int %int_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_0_ID), + // Test case 24: Fold 0 % n + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpUMod %uint %109 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_0_ID), + // Test case 25: Fold n % 1 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpSMod %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_NULL_ID), + // Test case 26: Fold n % 1 + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpUMod %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + // Test case 27: Don't fold because of undefined value. Using 4294967295 // means that entry is undefined. We do not expect it to ever happen, so // not worth folding. InstructionFoldingCase<uint32_t>( @@ -5126,7 +6314,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 9: Don't fold because of undefined value. Using 4294967295 + // Test case 28: Don't fold because of undefined value. Using 4294967295 // means that entry is undefined. We do not expect it to ever happen, so // not worth folding. InstructionFoldingCase<uint32_t>( @@ -5462,7 +6650,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; std::tie(context, inst) = - FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); EXPECT_TRUE((inst == nullptr) == (tc.expected_result == 0)); if (inst != nullptr) { @@ -5568,7 +6756,8 @@ std::unique_ptr<IRContext> context; Instruction* inst; - std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1); + //std::cerr << "[\n" << tc.test_body << "\n]"; + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_5); EXPECT_EQ(inst != nullptr, tc.expected_result); if (inst != nullptr) { @@ -5586,7 +6775,7 @@ "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%n = OpVariable %_ptr_int Function\n" + - "%3 = OpLoad %uint %n\n" + + "%3 = OpLoad %int %n\n" + "%2 = OpIAdd %uint %int_0 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", @@ -5598,14 +6787,59 @@ "; CHECK: %2 = OpBitcast [[int]] %3\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + - "%n = OpVariable %_ptr_int Function\n" + - "%3 = OpLoad %int %n\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + "%2 = OpIAdd %int %uint_0 %3\n" + "OpReturn\n" + "OpFunctionEnd\n", 2, true) )); +INSTANTIATE_TEST_SUITE_P(RedundantBitcastTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: uint32 x; asuint32(x) => x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: %2 = OpCopyObject [[uint]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpBitcast %uint %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 2, true), + // Test case 1: uint32 x; asuint32(asint32(x)) => x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: %2 = OpCopyObject [[uint]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%4 = OpBitcast %int %3\n" + + "%2 = OpBitcast %uint %4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 2, true), + // Test case 2: float32 x; asuint32(asint32(x)) => asuint32(x) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: %2 = OpBitcast [[uint]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpBitcast %int %3\n" + + "%2 = OpBitcast %uint %4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 2, true) + )); + INSTANTIATE_TEST_SUITE_P(MergeNegateTest, MatchingInstructionFoldingTest, ::testing::Values( // Test case 0: fold consecutive fnegate @@ -6131,6 +7365,2073 @@ 4, false) )); +INSTANTIATE_TEST_SUITE_P(ReassociateCommutiveBitwiseTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold (n ^ 248) ^ 31 = n ^ 231 + InstructionFoldingCase<bool>( + Header() + + "%uint_248 = OpConstant %uint 248\n" + + "%uint_31 = OpConstant %uint 31\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" + + "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_248\n" + + "%2 = OpBitwiseXor %uint %3 %uint_31\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 1: fold 31 ^ (n ^ 248) = n ^ 231 + InstructionFoldingCase<bool>( + Header() + + "%uint_248 = OpConstant %uint 248\n" + + "%uint_31 = OpConstant %uint 31\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" + + "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_248\n" + + "%2 = OpBitwiseXor %uint %uint_31 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 2: fold (248 ^ n) ^ 31 = n ^ 231 + InstructionFoldingCase<bool>( + Header() + + "%uint_248 = OpConstant %uint 248\n" + + "%uint_31 = OpConstant %uint 31\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" + + "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %uint_248 %4\n" + + "%2 = OpBitwiseXor %uint %3 %uint_31\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 3: fold 31 ^ (248 ^ n) = n ^ 231 + InstructionFoldingCase<bool>( + Header() + + "%uint_248 = OpConstant %uint 248\n" + + "%uint_31 = OpConstant %uint 31\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" + + "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %uint_248 %4\n" + + "%2 = OpBitwiseXor %uint %uint_31 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 4: fold 3 | (n | 193) = n | 195 + InstructionFoldingCase<bool>( + Header() + + "%uint_193 = OpConstant %uint 193\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" + + "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_193\n" + + "%2 = OpBitwiseOr %uint %uint_3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 5: fold (n | 193) | 3 = n | 195 + InstructionFoldingCase<bool>( + Header() + + "%uint_193 = OpConstant %uint 193\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" + + "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_193\n" + + "%2 = OpBitwiseOr %uint %3 %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 6: fold 3 | (193 | n) = n | 195 + InstructionFoldingCase<bool>( + Header() + + "%uint_193 = OpConstant %uint 193\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" + + "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_193 %4\n" + + "%2 = OpBitwiseOr %uint %uint_3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 7: fold (193 | n) | 3 = n | 195 + InstructionFoldingCase<bool>( + Header() + + "%uint_193 = OpConstant %uint 193\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" + + "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_193 %4\n" + + "%2 = OpBitwiseOr %uint %3 %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 8: fold 65520 & (n & 4095) = n & 4080 + InstructionFoldingCase<bool>( + Header() + + "%uint_65520 = OpConstant %uint 65520\n" + + "%uint_4095 = OpConstant %uint 4095\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseAnd %uint %4 %uint_4095\n" + + "%2 = OpBitwiseAnd %uint %uint_65520 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 9: fold (n & 4095) & 65520 = n & 4080 + InstructionFoldingCase<bool>( + Header() + + "%uint_65520 = OpConstant %uint 65520\n" + + "%uint_4095 = OpConstant %uint 4095\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseAnd %uint %4 %uint_4095\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_65520\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 10: fold 65520 & (4095 & n) = n & 4080 + InstructionFoldingCase<bool>( + Header() + + "%uint_65520 = OpConstant %uint 65520\n" + + "%uint_4095 = OpConstant %uint 4095\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseAnd %uint %uint_4095 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_65520 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 11: fold (4095 & n) & 65520 = n & 4080 + InstructionFoldingCase<bool>( + Header() + + "%uint_65520 = OpConstant %uint 65520\n" + + "%uint_4095 = OpConstant %uint 4095\n" + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseAnd %uint %uint_4095 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_65520\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(ReassociateNestedGenericInt, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // (a * 3) * (b * 8) = 24 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_24:%\\w+]] = OpConstant [[uint]] 24\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[uint_24]] [[rhs]]\n" + + "%C0 = OpConstant %uint 3\n" + + "%C1 = OpConstant %uint 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpIMul %uint %a %C0\n" + + "%rhs = OpIMul %uint %b %C1\n" + + "%4 = OpIMul %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: + // (a * 34) * (5 * b) = 170 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_170:%\\w+]] = OpConstant [[uint]] 170\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[uint_170]] [[rhs]]\n" + + "%C0 = OpConstant %uint 34\n" + + "%C1 = OpConstant %uint 5\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpIMul %uint %a %C0\n" + + "%rhs = OpIMul %uint %C1 %b\n" + + "%4 = OpIMul %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: + // (42 * a) * (b * 7) = 24 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_294:%\\w+]] = OpConstant [[uint]] 294\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[uint_294]] [[rhs]]\n" + + "%C0 = OpConstant %uint 42\n" + + "%C1 = OpConstant %uint 7\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpIMul %uint %C0 %a\n" + + "%rhs = OpIMul %uint %b %C1\n" + + "%4 = OpIMul %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: + // (5 * a) * (14 * b) = 70 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_70:%\\w+]] = OpConstant [[uint]] 70\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpIMul [[uint]] [[uint_70]] [[rhs]]\n" + + "%C0 = OpConstant %uint 5\n" + + "%C1 = OpConstant %uint 14\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpIMul %uint %C0 %a\n" + + "%rhs = OpIMul %uint %C1 %b\n" + + "%4 = OpIMul %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 4: + // (a ^ 3) ^ (b ^ 8) = 11 ^ (a ^ b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_11]] [[rhs]]\n" + + "%C0 = OpConstant %uint 3\n" + + "%C1 = OpConstant %uint 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseXor %uint %a %C0\n" + + "%rhs = OpBitwiseXor %uint %b %C1\n" + + "%4 = OpBitwiseXor %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 5: + // (a ^ 34) ^ (5 ^ b) = 39 ^ (a ^ b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_39:%\\w+]] = OpConstant [[uint]] 39\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_39]] [[rhs]]\n" + + "%C0 = OpConstant %uint 34\n" + + "%C1 = OpConstant %uint 5\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseXor %uint %a %C0\n" + + "%rhs = OpBitwiseXor %uint %C1 %b\n" + + "%4 = OpBitwiseXor %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 6: + // (42 ^ a) ^ (b ^ 7) = 45 ^ (a ^ b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_45:%\\w+]] = OpConstant [[uint]] 45\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_45]] [[rhs]]\n" + + "%C0 = OpConstant %uint 42\n" + + "%C1 = OpConstant %uint 7\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseXor %uint %C0 %a\n" + + "%rhs = OpBitwiseXor %uint %b %C1\n" + + "%4 = OpBitwiseXor %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 7: + // (5 ^ a) ^ (14 ^ b) = 11 ^ (a ^ b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_11]] [[rhs]]\n" + + "%C0 = OpConstant %uint 5\n" + + "%C1 = OpConstant %uint 14\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseXor %uint %C0 %a\n" + + "%rhs = OpBitwiseXor %uint %C1 %b\n" + + "%4 = OpBitwiseXor %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 8: + // (a | 3) | (b | 8) = 11 | (a | b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_11]] [[rhs]]\n" + + "%C0 = OpConstant %uint 3\n" + + "%C1 = OpConstant %uint 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseOr %uint %a %C0\n" + + "%rhs = OpBitwiseOr %uint %b %C1\n" + + "%4 = OpBitwiseOr %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 9: + // (a | 34) | (5 | b) = 39 | (a | b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_39:%\\w+]] = OpConstant [[uint]] 39\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_39]] [[rhs]]\n" + + "%C0 = OpConstant %uint 34\n" + + "%C1 = OpConstant %uint 5\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseOr %uint %a %C0\n" + + "%rhs = OpBitwiseOr %uint %C1 %b\n" + + "%4 = OpBitwiseOr %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 10: + // (42 | a) | (b | 7) = 47 | (a | b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_47:%\\w+]] = OpConstant [[uint]] 47\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_47]] [[rhs]]\n" + + "%C0 = OpConstant %uint 42\n" + + "%C1 = OpConstant %uint 7\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseOr %uint %C0 %a\n" + + "%rhs = OpBitwiseOr %uint %b %C1\n" + + "%4 = OpBitwiseOr %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 11: + // (5 | a) | (14 | b) = 15 | (a | b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_15:%\\w+]] = OpConstant [[uint]] 15\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_15]] [[rhs]]\n" + + "%C0 = OpConstant %uint 5\n" + + "%C1 = OpConstant %uint 14\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseOr %uint %C0 %a\n" + + "%rhs = OpBitwiseOr %uint %C1 %b\n" + + "%4 = OpBitwiseOr %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 12: + // (a & 12) & (b & 24) = 8 & (a & b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_8]] [[rhs]]\n" + + "%C0 = OpConstant %uint 12\n" + + "%C1 = OpConstant %uint 24\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseAnd %uint %a %C0\n" + + "%rhs = OpBitwiseAnd %uint %b %C1\n" + + "%4 = OpBitwiseAnd %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: + // (a & 34) & (6 & b) = 2 & (a & b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_2]] [[rhs]]\n" + + "%C0 = OpConstant %uint 34\n" + + "%C1 = OpConstant %uint 6\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseAnd %uint %a %C0\n" + + "%rhs = OpBitwiseAnd %uint %C1 %b\n" + + "%4 = OpBitwiseAnd %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 14: + // (43 & a) & (b & 15) = 11 & (a & b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_11]] [[rhs]]\n" + + "%C0 = OpConstant %uint 43\n" + + "%C1 = OpConstant %uint 15\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseAnd %uint %C0 %a\n" + + "%rhs = OpBitwiseAnd %uint %b %C1\n" + + "%4 = OpBitwiseAnd %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: + // (5 & a) & (14 & b) = 4 & (a & b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4:%\\w+]] = OpConstant [[uint]] 4\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_4]] [[rhs]]\n" + + "%C0 = OpConstant %uint 5\n" + + "%C1 = OpConstant %uint 14\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%a = OpLoad %uint %var\n" + + "%b = OpLoad %uint %var\n" + + "%lhs = OpBitwiseAnd %uint %C0 %a\n" + + "%rhs = OpBitwiseAnd %uint %C1 %b\n" + + "%4 = OpBitwiseAnd %uint %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) +)); + +INSTANTIATE_TEST_SUITE_P(ReassociateNestedMulDivFloat, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // (a * 9) * (b * 12) = 108 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_108:%\\w+]] = OpConstant [[float]] 108\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_108]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: + // (a * 24) * (b / 10) = 2.4 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2p4:%\\w+]] = OpConstant [[float]] 2.4\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_2p4]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: + // (a * 102) * (4 * b) = 408 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_408:%\\w+]] = OpConstant [[float]] 408\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_408]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: + // (a * 37) * (8 / b) = 296 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_296:%\\w+]] = OpConstant [[float]] 296\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_296]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 4: + // (a * 11) / (b * 2) = 5.5 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_5p5:%\\w+]] = OpConstant [[float]] 5.5\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_5p5]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 5: + // (a * 8) / (b / 53) = 424 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_424:%\\w+]] = OpConstant [[float]] 424\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_424]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 6: + // (a * 13) / (5 * b) = 2.6 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2p6:%\\w+]] = OpConstant [[float]] 2.6\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_2p6]] [[rhs]]\n" + + "%C0 = OpConstant %float 13\n" + + "%C1 = OpConstant %float 5\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 7: + // (a * 21) / (2 / b) = 10.5 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_10p5:%\\w+]] = OpConstant [[float]] 10.5\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_10p5]] [[rhs]]\n" + + "%C0 = OpConstant %float 21\n" + + "%C1 = OpConstant %float 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %a %C0\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 8: + // (9 * a) * (b * 12) = 108 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_108:%\\w+]] = OpConstant [[float]] 108\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_108]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 9: + // (24 * a) * (b / 4) = 6 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_6:%\\w+]] = OpConstant [[float]] 6\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_6]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 10: + // (102 * a) * (4 * b) = 408 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_408:%\\w+]] = OpConstant [[float]] 408\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_408]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 11: + // (37 * a) * (8 / b) = 296 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_296:%\\w+]] = OpConstant [[float]] 296\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_296]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 12: + // (11 * a) / (b * 8) = 1.375 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1p375:%\\w+]] = OpConstant [[float]] 1.375\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_1p375]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: + // (8 * a) / (b / 53) = 424 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_424:%\\w+]] = OpConstant [[float]] 424\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_424]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 14: + // (20 * a) / (10 * b) = 2 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_2]] [[rhs]]\n" + + "%C0 = OpConstant %float 20\n" + + "%C1 = OpConstant %float 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: + // (21 * a) / (3 / b) = 7 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_7:%\\w+]] = OpConstant [[float]] 7\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_7]] [[rhs]]\n" + + "%C0 = OpConstant %float 21\n" + + "%C1 = OpConstant %float 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFMul %float %C0 %a\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 16: + // (a / 12) * (b * 9) = 0.75 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_0p75:%\\w+]] = OpConstant [[float]] 0.75\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_0p75]] [[rhs]]\n" + + "%C0 = OpConstant %float 12\n" + + "%C1 = OpConstant %float 9\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 17: + // (a / 0.5) * (b / 0.2) = 10 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_10:%\\w+]] = OpConstant [[float]] 10\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_10]] [[rhs]]\n" + + "%C0 = OpConstant %float 0.5\n" + + "%C1 = OpConstant %float 0.2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 18: + // (a / 4) * (102 * b) = 25.5 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_25p5:%\\w+]] = OpConstant [[float]] 25.5\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_25p5]] [[rhs]]\n" + + "%C0 = OpConstant %float 4\n" + + "%C1 = OpConstant %float 102\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 19: + // (a / 8) * (37 / b) = 4.625 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_4p625:%\\w+]] = OpConstant [[float]] 4.625\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_4p625]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 37\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 20: + // (a / 10) / (b * 2) = 0.05 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_0p05:%\\w+]] = OpConstant [[float]] 0.05\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_0p05]] [[rhs]]\n" + + "%C0 = OpConstant %float 10\n" + + "%C1 = OpConstant %float 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 21: + // (a / 8) / (b / 53) = 6.625 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_6p625:%\\w+]] = OpConstant [[float]] 6.625\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_6p625]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 22: + // (a / 4) / (8 * b) = 0.03125 * (a / b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_0p03125:%\\w+]] = OpConstant [[float]] 0.03125\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_0p03125]] [[rhs]]\n" + + "%C0 = OpConstant %float 4\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 23: + // (a / 2) / (0.25 / b) = 2 * (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_2]] [[rhs]]\n" + + "%C0 = OpConstant %float 2\n" + + "%C1 = OpConstant %float 0.25\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %a %C0\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 24: + // (9 / a) * (b * 12) = 108 * (b / a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_108:%\\w+]] = OpConstant [[float]] 108\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_108]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 25: + // (24 / a) * (b / 10) = 2.4 * (b / a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2p4:%\\w+]] = OpConstant [[float]] 2.4\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_2p4]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 26: + // (102 / a) * (4 * b) = 408 * (b / a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_408:%\\w+]] = OpConstant [[float]] 408\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_408]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 27: + // (37 / a) * (8 / b) = 296 / (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_296:%\\w+]] = OpConstant [[float]] 296\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_296]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFMul %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 28: + // (11 / a) / (b * 8) = 1.375 / (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1p375:%\\w+]] = OpConstant [[float]] 1.375\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_1p375]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFMul %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 29: + // (8 / a) / (b / 53) = 424 / (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_424:%\\w+]] = OpConstant [[float]] 424\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_424]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFDiv %float %b %C1\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 30: + // (13 / a) / (10 * b) = 1.3 / (a * b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1p3:%\\w+]] = OpConstant [[float]] 1.3\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_1p3]] [[rhs]]\n" + + "%C0 = OpConstant %float 13\n" + + "%C1 = OpConstant %float 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFMul %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 31: + // (21 / a) / (2 / b) = 10.5 * (b / a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_10p5:%\\w+]] = OpConstant [[float]] 10.5\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_10p5]] [[rhs]]\n" + + "%C0 = OpConstant %float 21\n" + + "%C1 = OpConstant %float 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFDiv %float %C0 %a\n" + + "%rhs = OpFDiv %float %C1 %b\n" + + "%4 = OpFDiv %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) + )); + +INSTANTIATE_TEST_SUITE_P(ReassociateNestedAddSubTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // (a + 9) + (b + 12) = 21 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_21:%\\w+]] = OpConstant [[float]] 21\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_21]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: + // (a + 24) + (b - 11) = 13 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_13:%\\w+]] = OpConstant [[float]] 13\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_13]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 11\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: + // (a + 102) + (4 + b) = 106 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_106:%\\w+]] = OpConstant [[float]] 106\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_106]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: + // (a + 37) + (8 - b) = 45 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_45]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 4: + // (a + 11) - (b + 9) = 2 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_2]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 9\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 5: + // (a + 8) - (b - 53) = 61 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_61:%\\w+]] = OpConstant [[float]] 61\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_61]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 6: + // (a + 13) - (12 + b) = 1 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_1]] [[rhs]]\n" + + "%C0 = OpConstant %float 13\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 7: + // (a + 2) - (21 - b) = -19 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n19:%\\w+]] = OpConstant [[float]] -19\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n19]] [[rhs]]\n" + + "%C0 = OpConstant %float 2\n" + + "%C1 = OpConstant %float 21\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %a %C0\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 8: + // (9 + a) + (b + 12) = 21 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_21:%\\w+]] = OpConstant [[float]] 21\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_21]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 9: + // (24 + a) + (b - 11) = 13 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_13:%\\w+]] = OpConstant [[float]] 13\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_13]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 11\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 10: + // (102 + a) + (4 + b) = 106 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_106:%\\w+]] = OpConstant [[float]] 106\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_106]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 11: + // (37 + a) + (8 - b) = 45 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_45]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 12: + // (11 + a) - (b + 9) = 2 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_2]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 9\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: + // (8 + a) - (b - 53) = 61 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_61:%\\w+]] = OpConstant [[float]] 61\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_61]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 14: + // (13 + a) - (12 + b) = 1 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_1]] [[rhs]]\n" + + "%C0 = OpConstant %float 13\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 15: + // (2 + a) - (21 - b) = -19 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n19:%\\w+]] = OpConstant [[float]] -19\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n19]] [[rhs]]\n" + + "%C0 = OpConstant %float 2\n" + + "%C1 = OpConstant %float 21\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFAdd %float %C0 %a\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 16: + // (a - 9) + (b + 12) = 3 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_3]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 17: + // (a - 24) + (b - 11) = -35 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n35:%\\w+]] = OpConstant [[float]] -35\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n35]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 11\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 18: + // (a - 102) + (4 + b) = -98 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n98:%\\w+]] = OpConstant [[float]] -98\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n98]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 19: + // (a - 37) + (8 - b) = -29 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n29:%\\w+]] = OpConstant [[float]] -29\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n29]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 20: + // (a - 11) - (b + 10) = -21 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n21:%\\w+]] = OpConstant [[float]] -21\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n21]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 21: + // (a - 8) - (b - 53) = 45 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_45]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 22: + // (a - 13) - (12 + b) = -25 + (a - b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n25:%\\w+]] = OpConstant [[float]] -25\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n25]] [[rhs]]\n" + + "%C0 = OpConstant %float 13\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 23: + // (a - 2) - (21 - b) = -23 + (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n23:%\\w+]] = OpConstant [[float]] -23\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n23]] [[rhs]]\n" + + "%C0 = OpConstant %float 2\n" + + "%C1 = OpConstant %float 21\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %a %C0\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 24: + // (9 - a) + (b + 12) = 21 + (b - a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_21:%\\w+]] = OpConstant [[float]] 21\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_21]] [[rhs]]\n" + + "%C0 = OpConstant %float 9\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 25: + // (24 - a) + (b - 11) = 13 + (b - a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_13:%\\w+]] = OpConstant [[float]] 13\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_13]] [[rhs]]\n" + + "%C0 = OpConstant %float 24\n" + + "%C1 = OpConstant %float 11\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 26: + // (102 - a) + (4 + b) = 106 + (b - a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_106:%\\w+]] = OpConstant [[float]] 106\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_106]] [[rhs]]\n" + + "%C0 = OpConstant %float 102\n" + + "%C1 = OpConstant %float 4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 27: + // (37 - a) + (8 - b) = 45 - (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_45]] [[rhs]]\n" + + "%C0 = OpConstant %float 37\n" + + "%C1 = OpConstant %float 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFAdd %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 28: + // (11 - a) - (b + 9) = 2 - (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[rhs]]\n" + + "%C0 = OpConstant %float 11\n" + + "%C1 = OpConstant %float 9\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFAdd %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 29: + // (8 - a) - (b - 53) = 61 - (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_61:%\\w+]] = OpConstant [[float]] 61\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_61]] [[rhs]]\n" + + "%C0 = OpConstant %float 8\n" + + "%C1 = OpConstant %float 53\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFSub %float %b %C1\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 30: + // (13 - a) - (12 + b) = 1 - (a + b) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_1]] [[rhs]]\n" + + "%C0 = OpConstant %float 13\n" + + "%C1 = OpConstant %float 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFAdd %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 31: + // (2 - a) - (21 - b) = -19 + (b - a) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n19:%\\w+]] = OpConstant [[float]] -19\n" + + "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[float_n19]] [[rhs]]\n" + + "%C0 = OpConstant %float 2\n" + + "%C1 = OpConstant %float 21\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%a = OpLoad %float %var\n" + + "%b = OpLoad %float %var\n" + + "%lhs = OpFSub %float %C0 %a\n" + + "%rhs = OpFSub %float %C1 %b\n" + + "%4 = OpFSub %float %lhs %rhs\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) + )); + INSTANTIATE_TEST_SUITE_P(ReciprocalFDivTest, MatchingInstructionFoldingTest, ::testing::Values( // Test case 0: scalar reicprocal @@ -6727,6 +10028,103 @@ "%4 = OpIMul %long %3 %long_10\n" + "OpReturn\n" + "OpFunctionEnd\n", + 4, true), + // Test case 31: merge fmul with two negatives + // (-x) * (-y) = x * y + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[x]] [[y]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%x = OpLoad %float %var\n" + + "%y = OpLoad %float %var\n" + + "%nx = OpFNegate %float %x\n" + + "%ny = OpFNegate %float %y\n" + + "%4 = OpFMul %float %nx %ny\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 32: merge OpVectorTimesScalar with two negatives + // (-x) * (-y) = x * y + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float2:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[float2]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpVectorTimesScalar [[float2]] [[x]] [[y]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var_f2 = OpVariable %_ptr_v2float Function\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%x = OpLoad %v2float %var_f2\n" + + "%y = OpLoad %float %var\n" + + "%nx = OpFNegate %v2float %x\n" + + "%ny = OpFNegate %float %y\n" + + "%4 = OpVectorTimesScalar %v2float %nx %ny\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 33: merge smul with two negatives + // (-x) * (-y) = x * y + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[x]] [[y]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%x = OpLoad %long %var\n" + + "%y = OpLoad %long %var\n" + + "%nx = OpSNegate %long %x\n" + + "%ny = OpSNegate %long %y\n" + + "%4 = OpIMul %long %nx %ny\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 34: merge fdiv with two negatives + // (-x) / (-y) = x / y + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[x]] [[y]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%x = OpLoad %float %var\n" + + "%y = OpLoad %float %var\n" + + "%nx = OpFNegate %float %x\n" + + "%ny = OpFNegate %float %y\n" + + "%4 = OpFDiv %float %nx %ny\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 35: merge sdiv with two negatives + // (-x) / (-y) = x / y + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpSDiv [[long]] [[x]] [[y]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%x = OpLoad %long %var\n" + + "%y = OpLoad %long %var\n" + + "%nx = OpSNegate %long %x\n" + + "%ny = OpSNegate %long %y\n" + + "%4 = OpSDiv %long %nx %ny\n" + + "OpReturn\n" + + "OpFunctionEnd\n", 4, true) )); @@ -6994,73 +10392,1057 @@ 4, false) )); +INSTANTIATE_TEST_SUITE_P(RedundantAndOrXorTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold + // 1 & (n | 2) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 1: Fold + // 1 & (2 | n) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 2: Fold + // (n | 2) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 3: Fold + // (2 | n) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 4: Fold + // 1 & (n | 3) = 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 5: Fold + // 1 & (3 | n) = 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_3 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 6: Fold + // (n | 3) & 1 = 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 7: Fold + // (3 | n) & 1 = 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_3 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 8: Do not fold + // 3 & (n | 1) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %uint_3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 9: Do not fold + // 3 & (1 | n) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_1 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 10: Do not fold + // (n | 1) & 3 + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 11: Do not fold + // (1 | n) & 3 + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %uint_1 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 12: Fold + // 1 & (n ^ 2) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 13: Fold + // 1 & (2 ^ n) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 14: Fold + // (n ^ 2) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 15: Fold + // (2 ^ n) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 16: Do not fold + // 1 & (n ^ 3) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 17: Do not fold + // 1 & (3 ^ n) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %uint_3 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 18: Do not fold + // (n ^ 3) & 1 + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 19: Do not fold + // (3 ^ n) & 1 + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %uint_3 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 20: Fold + // 0b101 & (n | 0b10101) = 0b101 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_5:%\\w+]] = OpConstant [[uint]] 5\n" + + "; CHECK: %2 = OpCopyObject [[uint]] [[uint_5]]\n" + + "%uint_5 = OpConstant %uint 5\n" + + "%uint_21 = OpConstant %uint 21\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_21\n" + + "%2 = OpBitwiseAnd %uint %uint_5 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 21: Do not fold + // 0b101 & (n ^ 0b10101) + InstructionFoldingCase<bool>( + Header() + + "%uint_5 = OpConstant %uint 5\n" + + "%uint_21 = OpConstant %uint 21\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_21\n" + + "%2 = OpBitwiseAnd %uint %uint_5 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 22: Fold + // 0b101 & (n ^ 0b1010) = n & 0b101 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_5:%\\w+]] = OpConstant [[uint]] 5\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_5]]\n" + + "%uint_5 = OpConstant %uint 5\n" + + "%uint_10 = OpConstant %uint 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_10\n" + + "%2 = OpBitwiseAnd %uint %uint_5 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 23: Fold + // 0b101 & (n | 0b1010) = n & 0b101 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_5:%\\w+]] = OpConstant [[uint]] 5\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_5]]\n" + + "%uint_5 = OpConstant %uint 5\n" + + "%uint_10 = OpConstant %uint 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_10\n" + + "%2 = OpBitwiseAnd %uint %uint_5 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 24: Fold + // 0b11000 & (n ^ 0b00111) = n & 0b11000 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_24:%\\w+]] = OpConstant [[uint]] 24\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_24]]\n" + + "%uint_24 = OpConstant %uint 24\n" + + "%uint_7 = OpConstant %uint 7\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseXor %uint %4 %uint_7\n" + + "%2 = OpBitwiseAnd %uint %uint_24 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 25: Fold + // 0b11000 & (n | 0b00111) = n & 0b11000 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_24:%\\w+]] = OpConstant [[uint]] 24\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_24]]\n" + + "%uint_24 = OpConstant %uint 24\n" + + "%uint_7 = OpConstant %uint 7\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpBitwiseOr %uint %4 %uint_7\n" + + "%2 = OpBitwiseAnd %uint %uint_24 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + +INSTANTIATE_TEST_SUITE_P(RedundantAndAddSubTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold + // 1 & (n + 2) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 1: Fold + // 1 & (2 + n) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 2: Fold + // (n + 2) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 3: Fold + // (2 + n) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 4: Fold + // 1 & (n - 2) = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpISub %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 5: Do not fold + // 1 & (2 - n) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpISub %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 6: Fold + // (n - 2) & 1 = n & 1 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpISub %uint %4 %uint_2\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Test case 7: Do not fold + // (2 - n) & 1 + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpISub %uint %uint_2 %4\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 8: Do not fold + // 1 & (n + 1) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 9: Do not fold + // 1 & (n - 1) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpISub %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 10: Do not fold + // 2 & (n + 1) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 11: Do not fold + // 2 & (n - 1) + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpISub %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 12: Do not fold + // 0b10 & (n + 0b101) + InstructionFoldingCase<bool>( + Header() + + "%uint_5 = OpConstant %uint 5\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_5\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + + // Test case 13: Fold + // 0b10010 & (n + 0b11100000) = n & 0b10010 + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_18:%\\w+]] = OpConstant [[uint]] 18\n" + + "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_18]]\n" + + "%uint_18 = OpConstant %uint 18\n" + + "%uint_224 = OpConstant %uint 224\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_224\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_18\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + + // Testcase 14: Do not fold + // 0b10000 & (n + 0b11100001) + InstructionFoldingCase<bool>( + Header() + + "%uint_16 = OpConstant %uint 16\n" + + "%uint_225 = OpConstant %uint 225\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpIAdd %uint %4 %uint_225\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_16\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false) + )); + + INSTANTIATE_TEST_SUITE_P(RedundantAndShiftTest, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold + // 1 & (n << 1) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 1: Fold + // (n << 1) & 1 = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 2: Do not fold + // 3 & (n << 1) + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %uint_3 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 3: Do not fold + // (n << 1) & 3 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 4: Fold + // 0x80000000 & (n >> 1) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_2147483648 = OpConstant %uint 2147483648\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %uint_2147483648 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 5: Fold + // (n >> 1) & 0x80000000 = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_2147483648 = OpConstant %uint 2147483648\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_2147483648\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 6: Do not fold + // (n >> 1) & 0xc0000000 + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_3221225472 = OpConstant %uint 3221225472\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %3 %uint_3221225472\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 7: Do not fold + // 0xc0000000 & (n >> 1) + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_3221225472 = OpConstant %uint 3221225472\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_1\n" + + "%2 = OpBitwiseAnd %uint %uint_3221225472 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 8: Fold + // 0b1111 & (n << 4) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_15 = OpConstant %uint 15\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_4\n" + + "%2 = OpBitwiseAnd %uint %uint_15 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 9: Fold + // 0b1000 & (n << 4) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_8 = OpConstant %uint 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_4\n" + + "%2 = OpBitwiseAnd %uint %uint_8 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 10: Do not fold + // 0b1111 & (n << 3) + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_15 = OpConstant %uint 15\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %uint_15 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 11: Do not fold + // 0b1000 & (n << 3) + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_8 = OpConstant %uint 8\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftLeftLogical %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %uint_8 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 12: Fold + // 0xf0000000 & (n >> 4) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_4026531840 = OpConstant %uint 4026531840\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_4\n" + + "%2 = OpBitwiseAnd %uint %uint_4026531840 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UINT_NULL_ID), + + // Test case 13: Do not fold + // 0xf0000000 & (n >> 3) + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_4026531840 = OpConstant %uint 4026531840\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_3\n" + + "%2 = OpBitwiseAnd %uint %uint_4026531840 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 14: Do not fold + // 0xf0000001 & (n >> 4) + InstructionFoldingCase<uint32_t>( + Header() + + "%uint_4026531841 = OpConstant %uint 4026531841\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%4 = OpLoad %uint %n\n" + + "%3 = OpShiftRightLogical %uint %4 %uint_4\n" + + "%2 = OpBitwiseAnd %uint %uint_4026531841 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + + // Test case 15: Fold (mixed-width shift amount) + // 1u64 & (n << 1u32) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_ulong Function\n" + + "%4 = OpLoad %ulong %n\n" + + "%3 = OpShiftLeftLogical %ulong %4 %uint_1\n" + + "%2 = OpBitwiseAnd %ulong %ulong_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, ULONG_NULL_ID), + + // Test case 16: Fold (8-bit base, 32-bit shift amount) + // 1u8 & (n << 1u32) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_ubyte Function\n" + + "%4 = OpLoad %ubyte %n\n" + + "%3 = OpShiftLeftLogical %ubyte %4 %uint_1\n" + + "%2 = OpBitwiseAnd %ubyte %ubyte_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UBYTE_NULL_ID), + + // Test case 17: Fold (16-bit base, 32-bit shift amount) + // 1u16 & (n << 1u32) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_ushort Function\n" + + "%4 = OpLoad %ushort %n\n" + + "%3 = OpShiftLeftLogical %ushort %4 %uint_1\n" + + "%2 = OpBitwiseAnd %ushort %ushort_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, USHORT_NULL_ID), + + // Test case 18: Fold (vector, mixed-width shift amount) + // <1,0>u16 & (n << <1,0>u32) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2ushort Function\n" + + "%4 = OpLoad %v2ushort %n\n" + + "%3 = OpShiftLeftLogical %v2ushort %4 %v2uint_1_null\n" + + "%2 = OpBitwiseAnd %v2ushort %v2ushort_1_null %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, V2USHORT_NULL_ID), + + // Test case 19: Fold (8-bit base, 32-bit shift amount, right shift) + // 0x80u8 & (n >> 1u32) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%ubyte_128 = OpConstant %ubyte 128\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_ubyte Function\n" + + "%4 = OpLoad %ubyte %n\n" + + "%3 = OpShiftRightLogical %ubyte %4 %uint_1\n" + + "%2 = OpBitwiseAnd %ubyte %ubyte_128 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, UBYTE_NULL_ID), + + // Test case 20: Fold (vector, mixed-width shift amount, right shift) + // <0x8000,0>u16 & (n >> <1,0>u32) = 0 + InstructionFoldingCase<uint32_t>( + Header() + + "%ushort_32768 = OpConstant %ushort 32768\n" + + "%v2ushort_32768_0 = OpConstantComposite %v2ushort %ushort_32768 %ushort_0\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2ushort Function\n" + + "%4 = OpLoad %v2ushort %n\n" + + "%3 = OpShiftRightLogical %v2ushort %4 %v2uint_1_null\n" + + "%2 = OpBitwiseAnd %v2ushort %v2ushort_32768_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, V2USHORT_NULL_ID) + )); + INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, ::testing::Values( // Test case 0: merge add of negate - // (-x) + 2 = 2 - x + // (-x) + y = y - x InstructionFoldingCase<bool>( Header() + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + - "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + - "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[y]] [[x]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_float Function\n" + - "%2 = OpLoad %float %var\n" + - "%3 = OpFNegate %float %2\n" + - "%4 = OpFAdd %float %3 %float_2\n" + + "%x = OpLoad %float %var\n" + + "%y = OpLoad %float %var\n" + + "%nx = OpFNegate %float %x\n" + + "%4 = OpFAdd %float %nx %y\n" + "OpReturn\n" + "OpFunctionEnd\n", 4, true), // Test case 1: merge add of negate - // 2 + (-x) = 2 - x + // y + (-x) = y - x InstructionFoldingCase<bool>( Header() + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + - "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + - "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[y]] [[x]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_float Function\n" + - "%2 = OpLoad %float %var\n" + - "%3 = OpSNegate %float %2\n" + - "%4 = OpIAdd %float %float_2 %3\n" + + "%x = OpLoad %float %var\n" + + "%y = OpLoad %float %var\n" + + "%nx = OpFNegate %float %x\n" + + "%4 = OpFAdd %float %y %nx\n" + "OpReturn\n" + "OpFunctionEnd\n", 4, true), // Test case 2: merge add of negate - // (-x) + 2 = 2 - x + // (-x) + y = y - x InstructionFoldingCase<bool>( Header() + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + - "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + - "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[y]] [[x]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_long Function\n" + - "%2 = OpLoad %long %var\n" + - "%3 = OpSNegate %long %2\n" + - "%4 = OpIAdd %long %3 %long_2\n" + + "%x = OpLoad %long %var\n" + + "%y = OpLoad %long %var\n" + + "%nx = OpSNegate %long %x\n" + + "%4 = OpIAdd %long %nx %y\n" + "OpReturn\n" + "OpFunctionEnd\n", 4, true), // Test case 3: merge add of negate - // 2 + (-x) = 2 - x + // y + (-x) = y - x InstructionFoldingCase<bool>( Header() + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + - "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + - "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[y]] [[x]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_long Function\n" + - "%2 = OpLoad %long %var\n" + - "%3 = OpSNegate %long %2\n" + - "%4 = OpIAdd %long %long_2 %3\n" + + "%x = OpLoad %long %var\n" + + "%y = OpLoad %long %var\n" + + "%nx = OpSNegate %long %x\n" + + "%4 = OpIAdd %long %y %nx\n" + "OpReturn\n" + "OpFunctionEnd\n", 4, true), @@ -7430,6 +11812,94 @@ 9, true) )); +INSTANTIATE_TEST_SUITE_P(FactorSubMul, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: factor of sub of muls + // (a * b) - (a * c) => a * (b - c) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %6 %4\n" + + "%8 = OpFMul %float %6 %5\n" + + "%9 = OpFSub %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + // Test case 1: factor of sub of muls + // (b * a) - (a * c) => a * (b - c) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %4 %6\n" + + "%8 = OpFMul %float %6 %5\n" + + "%9 = OpFSub %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + // Test case 2: factor of sub of muls + // (a * b) - (c * a) => a * (b - c) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %6 %4\n" + + "%8 = OpFMul %float %5 %6\n" + + "%9 = OpFSub %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + // Test case 3: factor of sub of muls + // (b * a) - (c * a) => a * (b - c) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %4 %6\n" + + "%8 = OpFMul %float %5 %6\n" + + "%9 = OpFSub %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true) +)); + INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest, ::testing::Values( // Test case 0: merge sub of negate @@ -7450,19 +11920,20 @@ "OpFunctionEnd\n", 4, true), // Test case 1: merge sub of negate - // 2 - (-x) = x + 2 + // y - (-x) = y + x InstructionFoldingCase<bool>( Header() + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + - "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + - "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_2]]\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[y]] [[x]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_float Function\n" + - "%2 = OpLoad %float %var\n" + - "%3 = OpFNegate %float %2\n" + - "%4 = OpFSub %float %float_2 %3\n" + + "%x = OpLoad %float %var\n" + + "%y = OpLoad %float %var\n" + + "%nx = OpFNegate %float %x\n" + + "%4 = OpFSub %float %y %nx\n" + "OpReturn\n" + "OpFunctionEnd\n", 4, true), @@ -7484,19 +11955,20 @@ "OpFunctionEnd\n", 4, true), // Test case 3: merge sub of negate - // 2 - (-x) = x + 2 + // y - (-x) = y + x InstructionFoldingCase<bool>( Header() + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + - "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + - "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + - "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_2]]\n" + + "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[y]] [[x]]\n" + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%var = OpVariable %_ptr_long Function\n" + - "%2 = OpLoad %long %var\n" + - "%3 = OpSNegate %long %2\n" + - "%4 = OpISub %long %long_2 %3\n" + + "%x = OpLoad %long %var\n" + + "%y = OpLoad %long %var\n" + + "%nx = OpSNegate %long %x\n" + + "%4 = OpISub %long %y %nx\n" + "OpReturn\n" + "OpFunctionEnd\n", 4, true), @@ -7811,9 +12283,1845 @@ "%4 = OpSelect %v2int %v2bool_false_true %2 %3\n" + "OpReturn\n" + "OpFunctionEnd", - 4, true) + 4, true), + // Test case 7: Fold select with different type of zeros for both sides + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_null:%\\w+]] = OpConstantNull [[float]]\n" + + "; CHECK: %2 = OpCopyObject [[float]] [[float_null]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpSelect %float %load %float_null %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) )); +INSTANTIATE_TEST_SUITE_P(FoldConstantBooleanSelectTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // x ? true : false = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpSelect %bool %3 %true %false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: + // x ? false : true = !x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpSelect %bool %3 %false %true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: + // x ? true : null_false = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpSelect %bool %3 %true %bool_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: + // x ? null_false : true = !x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpSelect %bool %3 %bool_null %true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + +INSTANTIATE_TEST_SUITE_P(RedundantLogicalAndTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // x && true = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalAnd %bool %3 %true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: + // true && x = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalAnd %bool %true %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + +INSTANTIATE_TEST_SUITE_P(RedundantLogicalOrTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // x || false = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalOr %bool %3 %false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: + // false || x = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalOr %bool %false %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: + // x || null_false = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalOr %bool %3 %bool_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: + // null_false || x = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalOr %bool %bool_null %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + +INSTANTIATE_TEST_SUITE_P(RedundantLogicalNotTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // !!x = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpLogicalNot %bool %3\n" + + "%2 = OpLogicalNot %bool %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + +INSTANTIATE_TEST_SUITE_P(RedundantLogicalEqualTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // x == true = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalEqual %bool %3 %true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: + // true == x = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalEqual %bool %true %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: + // x == false = !x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalEqual %bool %3 %false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: + // false == x = !x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalEqual %bool %false %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 4: + // x != true = !x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalNotEqual %bool %3 %true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: + // true != x = !x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalNotEqual %bool %true %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 6: + // x != false = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalNotEqual %bool %3 %false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: + // false != x = x + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%2 = OpLogicalNotEqual %bool %false %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + +INSTANTIATE_TEST_SUITE_P(FoldLogicalNotComparisonTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // !(a == b) = (a != b) [OpIEqual => OpINotEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpINotEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpLoad %int %m\n" + + "%5 = OpIEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: + // !(a != b) = (a == b) [OpINotEqual => OpIEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpIEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpLoad %int %m\n" + + "%5 = OpINotEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: + // !(a == b) = (a != b) [OpFOrdEqual => OpFUnordNotEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFUnordNotEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFOrdEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: + // !(a != b) = (a == b) [OpFUnordNotEqual => OpFOrdEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFOrdEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFUnordNotEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 4: + // !(a == b) = (a != b) [OpFUnordEqual => OpFOrdNotEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFOrdNotEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFUnordEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: + // !(a != b) = (a == b) [OpFOrdNotEqual => OpFUnordEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFUnordEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFOrdNotEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 6: + // !(a == b) = (a != b) [OpLogicalEqual => OpLogicalNotEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNotEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%m = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpLoad %bool %m\n" + + "%5 = OpLogicalEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: + // !(a != b) = (a == b) [OpLogicalNotEqual => OpLogicalEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%m = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpLoad %bool %m\n" + + "%5 = OpLogicalNotEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 8: + // !(a > b) = (a <= b) [OpUGreaterThan => OpULessThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpULessThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%4 = OpLoad %uint %m\n" + + "%5 = OpUGreaterThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 9: + // !(a <= b) = (a > b) [OpULessThanEqual => OpUGreaterThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpUGreaterThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%4 = OpLoad %uint %m\n" + + "%5 = OpULessThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 10: + // !(a > b) = (a <= b) [OpSGreaterThan => OpSLessThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpSLessThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpLoad %int %m\n" + + "%5 = OpSGreaterThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 11: + // !(a <= b) = (a > b) [OpSLessThanEqual => OpSGreaterThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpSGreaterThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpLoad %int %m\n" + + "%5 = OpSLessThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: + // !(a > b) = (a <= b) [OpFOrdGreaterThan => OpFUnordLessThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFUnordLessThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFOrdGreaterThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 13: + // !(a <= b) = (a > b) [OpFUnordLessThanEqual => OpFOrdGreaterThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFOrdGreaterThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFUnordLessThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: + // !(a > b) = (a <= b) [OpFUnordGreaterThan => OpFOrdLessThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFOrdLessThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFUnordGreaterThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 15: + // !(a <= b) = (a > b) [OpFOrdLessThanEqual => OpFUnordGreaterThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFUnordGreaterThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFOrdLessThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 16: + // !(a < b) = (a >= b) [OpULessThan => OpUGreaterThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpUGreaterThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%4 = OpLoad %uint %m\n" + + "%5 = OpULessThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 17: + // !(a >= b) = (a < b) [OpUGreaterThanEqual => OpULessThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpULessThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%4 = OpLoad %uint %m\n" + + "%5 = OpUGreaterThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 18: + // !(a < b) = (a >= b) [OpSLessThan => OpSGreaterThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpSGreaterThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpLoad %int %m\n" + + "%5 = OpSLessThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 19: + // !(a >= b) = (a < b) [OpSGreaterThanEqual => OpSLessThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpSLessThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%4 = OpLoad %int %m\n" + + "%5 = OpSGreaterThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 20: + // !(a < b) = (a >= b) [OpFOrdLessThan => OpFUnordGreaterThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFUnordGreaterThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFOrdLessThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 21: + // !(a >= b) = (a < b) [OpFUnordGreaterThanEqual => OpFOrdLessThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFOrdLessThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFUnordGreaterThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 22: + // !(a < b) = (a >= b) [OpFUnordLessThan => OpFOrdGreaterThanEqual] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFOrdGreaterThanEqual [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFUnordLessThan %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 23: + // !(a >= b) = (a < b) [OpFOrdGreaterThanEqual => OpFUnordLessThan] + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpFUnordLessThan [[bool]] %3 %4\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%m = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%4 = OpLoad %float %m\n" + + "%5 = OpFOrdGreaterThanEqual %bool %3 %4\n" + + "%2 = OpLogicalNot %bool %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + + INSTANTIATE_TEST_SUITE_P(MergeBinaryComparisonSelectTest, MatchingInstructionFoldingTest, + ::testing::Values( + // Test case 0: + // [OpLogicalEqual] ((a ? [true, false] : [false, true] == [true, false])) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" + + "; CHECK: %2 = OpCopyObject [[v2bool]] %3\n" + + "%_ptr_v2bool = OpTypePointer Function %v2bool\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2bool Function\n" + + "%3 = OpLoad %v2bool %n\n" + + "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" + + "%2 = OpLogicalEqual %v2bool %4 %v2bool_true_false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: + // [OpLogicalEqual] ((a ? [true, false] : [false, true] == [false, true])) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" + + "; CHECK: %2 = OpLogicalNot [[v2bool]] %3\n" + + "%_ptr_v2bool = OpTypePointer Function %v2bool\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2bool Function\n" + + "%3 = OpLoad %v2bool %n\n" + + "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" + + "%2 = OpLogicalEqual %v2bool %4 %v2bool_false_true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: + // [OpLogicalNotEqual] ((a ? [true, false] : [false, true] != [true, false])) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" + + "; CHECK: %2 = OpLogicalNot [[v2bool]] %3\n" + + "%_ptr_v2bool = OpTypePointer Function %v2bool\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2bool Function\n" + + "%3 = OpLoad %v2bool %n\n" + + "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" + + "%2 = OpLogicalNotEqual %v2bool %4 %v2bool_true_false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: + // [OpLogicalNotEqual] ((a ? [true, false] : [false, true]) != [false, true]) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" + + "; CHECK: %2 = OpCopyObject [[v2bool]] %3\n" + + "%_ptr_v2bool = OpTypePointer Function %v2bool\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2bool Function\n" + + "%3 = OpLoad %v2bool %n\n" + + "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" + + "%2 = OpLogicalNotEqual %v2bool %4 %v2bool_false_true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 4: + // [OpIEqual] ((a ? 1 : 0) == 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_1 %uint_0\n" + + "%2 = OpIEqual %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: + // [OpIEqual] ((a ? 0 : 1) == 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_1\n" + + "%2 = OpIEqual %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 6: + // [OpINotEqual] ((a ? 1 : 0) != 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_1 %uint_0\n" + + "%2 = OpINotEqual %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: + // [OpINotEqual] ((a ? 0 : 1) != 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_1\n" + + "%2 = OpINotEqual %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 8: + // [OpUGreaterThan] ((a ? 2 : 0) > 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" + + "%2 = OpUGreaterThan %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 9: + // [OpUGreaterThan] (1 > (a ? 2 : 0)) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" + + "%2 = OpUGreaterThan %bool %uint_1 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 10: + // [OpUGreaterThan] ((a ? 0 : 2) > 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" + + "%2 = OpUGreaterThan %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 11: + // [OpUGreaterThan] (1 > (a ? 0 : 2)) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" + + "%2 = OpUGreaterThan %bool %uint_1 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: + // [OpSGreaterThan] ((a ? 2 : 0) > 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpSGreaterThan %bool %4 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 13: + // [OpSGreaterThan] ((a ? 0 : 2) > 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_0 %int_2\n" + + "%2 = OpSGreaterThan %bool %4 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: + // [OpUGreaterThanEqual] ((a ? 2 : 0) >= 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" + + "%2 = OpUGreaterThanEqual %bool %4 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 15: + // [OpUGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" + + "%2 = OpUGreaterThanEqual %bool %4 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 16: + // [OpSGreaterThanEqual] ((a ? 2 : 0) >= 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpSGreaterThanEqual %bool %4 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 17: + // [OpSGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_0 %int_2\n" + + "%2 = OpSGreaterThanEqual %bool %4 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 18: + // [OpULessThan] ((a ? 0 : 2) < 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" + + "%2 = OpULessThan %bool %4 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 19: + // [OpULessThan] (2 < (a ? 0 : 2)) = false + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: [[false:%\\w+]] = OpConstantFalse [[bool]]\n" + + "; CHECK: %2 = OpCopyObject [[bool]] [[false]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" + + "%2 = OpULessThan %bool %uint_2 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 20: + // [OpULessThan] ((a ? 2 : 0) < 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" + + "%2 = OpULessThan %bool %4 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 21: + // [OpULessThan] (2 > (a ? 2 : 0)) = false + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: [[false:%\\w+]] = OpConstantFalse [[bool]]\n" + + "; CHECK: %2 = OpCopyObject [[bool]] [[false]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" + + "%2 = OpULessThan %bool %uint_2 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 22: + // [OpSLessThan] ((a ? 0 : 2) < 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_0 %int_2\n" + + "%2 = OpSLessThan %bool %4 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 23: + // [OpSLessThan] ((a ? 2 : 0) < 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpSLessThan %bool %4 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 24: + // [OpULessThanEqual] ((a ? 0 : 2) <= 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" + + "%2 = OpULessThanEqual %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 25: + // [OpULessThanEqual] ((a ? 2 : 0) <= 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" + + "%2 = OpULessThanEqual %bool %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 26: + // [OpSLessThanEqual] ((a ? 0 : 2) <= 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_0 %int_2\n" + + "%2 = OpSLessThanEqual %bool %4 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 27: + // [OpSLessThanEqual] ((a ? 2 : 0) <= 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpSLessThanEqual %bool %4 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 28: + // [OpFUnordEqual] ((a ? 1 : 0) == 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_0\n" + + "%2 = OpFUnordEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 29: + // [OpFUnordEqual] ((a ? 0 : 1) == 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_1\n" + + "%2 = OpFUnordEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 30: + // [OpFUnordNotEqual] ((a ? 1 : 0) != 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_0\n" + + "%2 = OpFUnordNotEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 31: + // [OpFUnordNotEqual] ((a ? 0 : 1) != 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_1\n" + + "%2 = OpFUnordNotEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 32: + // [OpFOrdEqual] ((a ? 1 : 0) == 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_0\n" + + "%2 = OpFOrdEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 33: + // [OpFOrdEqual] ((a ? 0 : 1) == 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_1\n" + + "%2 = OpFOrdEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 34: + // [OpFOrdNotEqual] ((a ? 1 : 0) != 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_0\n" + + "%2 = OpFOrdNotEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 35: + // [OpFOrdNotEqual] ((a ? 0 : 1) != 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_1\n" + + "%2 = OpFOrdNotEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 36: + // [OpFOrdGreaterThan] ((a ? 2 : 0) > 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFOrdGreaterThan %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 37: + // [OpFOrdGreaterThan] ((a ? 0 : 2) > 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFOrdGreaterThan %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 38: + // [OpFUnordGreaterThan] ((a ? 2 : 0) > 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFUnordGreaterThan %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 39: + // [OpFUnordGreaterThan] ((a ? 0 : 2) > 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFUnordGreaterThan %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 40: + // [OpFOrdGreaterThanEqual] ((a ? 2 : 0) >= 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFOrdGreaterThanEqual %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 41: + // [OpFOrdGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFOrdGreaterThanEqual %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 42: + // [OpFUnordGreaterThanEqual] ((a ? 2 : 0) >= 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFUnordGreaterThanEqual %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 43: + // [OpFUnordGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFUnordGreaterThanEqual %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 44: + // [OpFUnordLessThan] ((a ? 0 : 2) < 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFUnordLessThan %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 45: + // [OpFUnordLessThan] ((a ? 2 : 0) < 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFUnordLessThan %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 46: + // [OpFOrdLessThan] ((a ? 0 : 2) < 2) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFOrdLessThan %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 47: + // [OpFOrdLessThan] ((a ? 2 : 0) < 2) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFOrdLessThan %bool %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 48: + // [OpFUnordLessThanEqual] ((a ? 0 : 2) <= 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFUnordLessThanEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 49: + // [OpFUnordLessThanEqual] ((a ? 2 : 0) <= 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFUnordLessThanEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 50: + // [OpFOrdLessThanEqual] ((a ? 0 : 2) <= 1) = a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpCopyObject [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_0 %float_2\n" + + "%2 = OpFOrdLessThanEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 51: + // [OpFOrdLessThanEqual] ((a ? 2 : 0) <= 1) = !a + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" + + "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_2 %float_0\n" + + "%2 = OpFOrdLessThanEqual %bool %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 52: + // [OpIAdd] (1 + (a ? 2 : 0)) = (a ? 3 : 1) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK: [[int_3:%\\w+]] = OpConstant [[int]] 3\n" + + "; CHECK: %2 = OpSelect [[int]] %3 [[int_3]] [[int_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpIAdd %int %int_1 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 53: + // [OpFAdd] ((a ? 1 : 2) + 1) = (a ? 2 : 3) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: %2 = OpSelect [[float]] %3 [[float_2]] [[float_3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_2\n" + + "%2 = OpFAdd %float %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 54: + // [OpISub] (1 - (a ? 2 : 0)) = (a ? -1 : 1) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK: [[int_n1:%\\w+]] = OpConstant [[int]] -1\n" + + "; CHECK: %2 = OpSelect [[int]] %3 [[int_n1]] [[int_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpISub %int %int_1 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 55: + // [OpISub] ((a ? 2 : 0) - 1) = (a ? 1 : -1) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK: [[int_n1:%\\w+]] = OpConstant [[int]] -1\n" + + "; CHECK: %2 = OpSelect [[int]] %3 [[int_1]] [[int_n1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_0\n" + + "%2 = OpISub %int %4 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 56: + // [OpFSub] (1 - (a ? 1 : 4)) = (a ? 0 : -3) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_0:%\\w+]] = OpConstant [[float]] 0\n" + + "; CHECK: [[float_n3:%\\w+]] = OpConstant [[float]] -3\n" + + "; CHECK: %2 = OpSelect [[float]] %3 [[float_0]] [[float_n3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_4\n" + + "%2 = OpFSub %float %float_1 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 57: + // [OpFSub] ((a ? 1 : 2) - 1) = (a ? 0 : 1) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_0:%\\w+]] = OpConstant [[float]] 0\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: %2 = OpSelect [[float]] %3 [[float_0]] [[float_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_2\n" + + "%2 = OpFSub %float %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 58: + // [OpIMul] ((a ? 2 : 1) * 2) = (a ? 4 : 2) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[int_4:%\\w+]] = OpConstant [[int]] 4\n" + + "; CHECK: %2 = OpSelect [[int]] %3 [[int_4]] [[int_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_2 %int_1\n" + + "%2 = OpIMul %int %4 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 59: + // [OpFMul] (2 * (a ? 1 : 2)) = (a ? 2 : 4) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" + + "; CHECK: %2 = OpSelect [[float]] %3 [[float_2]] [[float_4]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_1 %float_2\n" + + "%2 = OpFMul %float %float_2 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 60: + // [OpUDiv] ((a ? 10 : 5) / 5) = (a ? 2 : 1) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" + + "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_2]] [[uint_1]]\n" + + "%uint_5 = OpConstant %uint 5\n" + + "%uint_10 = OpConstant %uint 10\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_10 %uint_5\n" + + "%2 = OpUDiv %uint %4 %uint_5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 61: + // [OpSDiv] ((a ? 15 : 10) / 5) = (a ? 3 : 2) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[int_3:%\\w+]] = OpConstant [[int]] 3\n" + + "; CHECK: %2 = OpSelect [[int]] %3 [[int_3]] [[int_2]]\n" + + "%int_5 = OpConstant %int 5\n" + + "%int_15 = OpConstant %int 15\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %int %3 %int_15 %int_10\n" + + "%2 = OpSDiv %int %4 %int_5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 62: + // [OpFDiv] ((a ? 8 : 16) / 4) = (a ? 2 : 4) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" + + "; CHECK: %2 = OpSelect [[float]] %3 [[float_2]] [[float_4]]\n" + + "%float_8 = OpConstant %float 8\n" + + "%float_16 = OpConstant %float 16\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %float %3 %float_8 %float_16\n" + + "%2 = OpFDiv %float %4 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 63: + // [OpVectorTimesScalar] ((a ? [1,2] : [2,1]) * 4) = (a ? [4,8] : [8,4]) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" + + "; CHECK: [[float_8:%\\w+]] = OpConstant [[float]] 8\n" + + "; CHECK: [[v2float_4_8:%\\w+]] = OpConstantComposite [[v2float]] [[float_4]] [[float_8]]\n" + + "; CHECK: [[v2float_8_4:%\\w+]] = OpConstantComposite [[v2float]] [[float_8]] [[float_4]]\n" + + "; CHECK: %2 = OpSelect [[v2float]] %3 [[v2float_4_8]] [[v2float_8_4]]\n" + + "%v2float_1_2 = OpConstantComposite %v2float %float_1 %float_2\n" + + "%v2float_2_1 = OpConstantComposite %v2float %float_2 %float_1\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %v2float %3 %v2float_1_2 %v2float_2_1\n" + + "%2 = OpVectorTimesScalar %v2float %4 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 64: + // [OpShiftRightLogical] ((a ? 32 : 16) >> 2) = (a ? 8 : 4) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4:%\\w+]] = OpConstant [[uint]] 4\n" + + "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_8]] [[uint_4]]\n" + + "%uint_16 = OpConstant %uint 16\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_32 %uint_16\n" + + "%2 = OpShiftRightLogical %uint %4 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 65: + // [OpShiftRightArithmetic] ((a ? 16 : 32) >> 1) = (a ? 8 : 16) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_16:%\\w+]] = OpConstant [[uint]] 16\n" + + "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_8]] [[uint_16]]\n" + + "%uint_16 = OpConstant %uint 16\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_16 %uint_32\n" + + "%2 = OpShiftRightArithmetic %uint %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 66: + // [OpShiftLeftLogical] ((a ? 7 : 3) << 1) = (a ? 14 : 6) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_14:%\\w+]] = OpConstant [[uint]] 14\n" + + "; CHECK: [[uint_6:%\\w+]] = OpConstant [[uint]] 6\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_14]] [[uint_6]]\n" + + "%uint_7 = OpConstant %uint 7\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_7 %uint_3\n" + + "%2 = OpShiftLeftLogical %uint %4 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 67: + // [OpBitwiseXor] (19 ^ (a ? 65 : 17)) = (a ? 82 : 2) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" + + "; CHECK: [[uint_82:%\\w+]] = OpConstant [[uint]] 82\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_82]] [[uint_2]]\n" + + "%uint_65 = OpConstant %uint 65\n" + + "%uint_17 = OpConstant %uint 17\n" + + "%uint_19 = OpConstant %uint 19\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_65 %uint_17\n" + + "%2 = OpBitwiseXor %uint %uint_19 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 68: + // [OpBitwiseOr] ((a ? 12 : 13) | 20) = (a ? 28 : 29) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_28:%\\w+]] = OpConstant [[uint]] 28\n" + + "; CHECK: [[uint_29:%\\w+]] = OpConstant [[uint]] 29\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_28]] [[uint_29]]\n" + + "%uint_12 = OpConstant %uint 12\n" + + "%uint_13 = OpConstant %uint 13\n" + + "%uint_20 = OpConstant %uint 20\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_12 %uint_13\n" + + "%2 = OpBitwiseOr %uint %4 %uint_20\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 69: + // [OpBitwiseAnd] (12 & (a ? 53 : 58)) = (a ? 4 : 8) + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: [[uint_4:%\\w+]] = OpConstant [[uint]] 4\n" + + "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" + + "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_4]] [[uint_8]]\n" + + "%uint_53 = OpConstant %uint 53\n" + + "%uint_58 = OpConstant %uint 58\n" + + "%uint_12 = OpConstant %uint 12\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%3 = OpLoad %bool %n\n" + + "%4 = OpSelect %uint %3 %uint_53 %uint_58\n" + + "%2 = OpBitwiseAnd %uint %uint_12 %4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) + )); + INSTANTIATE_TEST_SUITE_P(CompositeExtractOrInsertMatchingTest, MatchingInstructionFoldingTest, ::testing::Values( // Test case 0: Extracting from result of consecutive shuffles of differing @@ -7940,7 +14248,22 @@ "OpReturn\n" + "OpFunctionEnd", 5, false), - // Test case 7: Extracting the undefined literal value from a vector + // Test case 7: Don't fold: Using fmix feeding extract with half type in + // the a position. + InstructionFoldingCase<bool>( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v4half Function\n" + + "%n = OpVariable %_ptr_v4half Function\n" + + "%2 = OpLoad %v4half %m\n" + + "%3 = OpLoad %v4half %n\n" + + "%4 = OpExtInst %v4half %1 FMix %2 %3 %v4half_0_1_0_0\n" + + "%5 = OpCompositeExtract %half %4 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, false), + // Test case 8: Extracting the undefined literal value from a vector // shuffle. InstructionFoldingCase<bool>( Header() + @@ -7955,7 +14278,7 @@ "OpReturn\n" + "OpFunctionEnd", 4, true), - // Test case 8: Inserting every element of a vector turns into a composite construct. + // Test case 9: Inserting every element of a vector turns into a composite construct. InstructionFoldingCase<bool>( Header() + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + @@ -7974,7 +14297,7 @@ "OpReturn\n" + "OpFunctionEnd", 5, true), - // Test case 9: Inserting every element of a vector turns into a composite construct in a different order. + // Test case 10: Inserting every element of a vector turns into a composite construct in a different order. InstructionFoldingCase<bool>( Header() + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + @@ -7993,7 +14316,7 @@ "OpReturn\n" + "OpFunctionEnd", 5, true), - // Test case 10: Check multiple inserts to the same position are handled correctly. + // Test case 11: Check multiple inserts to the same position are handled correctly. InstructionFoldingCase<bool>( Header() + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + @@ -8013,7 +14336,7 @@ "OpReturn\n" + "OpFunctionEnd", 6, true), - // Test case 11: The last indexes are 0 and 1, but they have different first indexes. This should not be folded. + // Test case 12: The last indexes are 0 and 1, but they have different first indexes. This should not be folded. InstructionFoldingCase<bool>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -8023,7 +14346,7 @@ "OpReturn\n" + "OpFunctionEnd", 3, false), - // Test case 12: Don't fold when there is a partial insertion. + // Test case 13: Don't fold when there is a partial insertion. InstructionFoldingCase<bool>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -8034,7 +14357,7 @@ "OpReturn\n" + "OpFunctionEnd", 4, false), - // Test case 13: Insert into a column of a matrix + // Test case 14: Insert into a column of a matrix InstructionFoldingCase<bool>( Header() + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + @@ -8053,7 +14376,7 @@ "OpReturn\n" + "OpFunctionEnd", 3, true), - // Test case 14: Insert all elements of the matrix. + // Test case 15: Insert all elements of the matrix. InstructionFoldingCase<bool>( Header() + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + @@ -8076,7 +14399,7 @@ "OpReturn\n" + "OpFunctionEnd", 5, true), - // Test case 15: Replace construct with extract when reconstructing a member + // Test case 16: Replace construct with extract when reconstructing a member // of another object. InstructionFoldingCase<bool>( Header() + @@ -8093,7 +14416,7 @@ "OpReturn\n" + "OpFunctionEnd", 5, true), - // Test case 16: Don't fold when type cannot be deduced to a constant. + // Test case 17: Don't fold when type cannot be deduced to a constant. InstructionFoldingCase<bool>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -8102,7 +14425,7 @@ "OpReturn\n" + "OpFunctionEnd", 4, false), - // Test case 17: Don't fold when index into composite is out of bounds. + // Test case 18: Don't fold when index into composite is out of bounds. InstructionFoldingCase<bool>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -8111,7 +14434,7 @@ "OpReturn\n" + "OpFunctionEnd", 4, false), - // Test case 18: Fold when every element of an array is inserted. + // Test case 19: Fold when every element of an array is inserted. InstructionFoldingCase<bool>( Header() + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + @@ -8128,7 +14451,7 @@ "OpReturn\n" + "OpFunctionEnd", 5, true), - // Test case 19: Don't fold for isomorphic structs + // Test case 20: Don't fold for isomorphic structs InstructionFoldingCase<bool>( Header() + "%structA = OpTypeStruct %ulong\n" + @@ -8141,7 +14464,226 @@ "%4 = OpCompositeConstruct %structB %3\n" + "OpReturn\n" + "OpFunctionEnd", - 4, false) + 4, false), + // Test case 21: Fold OpCopyLogical feeding extract. + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct_type1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[struct_type2:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[ld:%\w+]] = OpLoad [[struct_type1]] [[var]] +; CHECK: [[ex:%\w+]] = OpCompositeExtract [[uint]] [[ld]] 0 +; CHECK: %13 = OpCopyObject [[uint]] [[ex]] + %struct1 = OpTypeStruct %uint %uint + %struct2 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct1 = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct1 StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 + %12 = OpCopyLogical %struct2 %11 + %13 = OpCompositeExtract %uint %12 0 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 22: Fold OpCopyLogical feeding extract with struct result. + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct_type1:%\w+]] = OpTypeStruct [[uint]] +; CHECK: [[struct_type2:%\w+]] = OpTypeStruct [[uint]] +; CHECK: [[struct_type3:%\w+]] = OpTypeStruct [[struct_type1]] +; CHECK: [[struct_type4:%\w+]] = OpTypeStruct [[struct_type2]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[ld:%\w+]] = OpLoad [[struct_type3]] [[var]] +; CHECK: [[ex:%\w+]] = OpCompositeExtract [[struct_type1]] [[ld]] 0 +; CHECK: %13 = OpCopyLogical [[struct_type2]] [[ex]] + %struct1 = OpTypeStruct %uint + %struct2 = OpTypeStruct %uint + %struct3 = OpTypeStruct %struct1 + %struct4 = OpTypeStruct %struct2 +%_ptr_StorageBuffer_struct3 = OpTypePointer StorageBuffer %struct3 +%var1 = OpVariable %_ptr_StorageBuffer_struct3 StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct3 %var1 + %12 = OpCopyLogical %struct4 %11 + %13 = OpCompositeExtract %struct2 %12 0 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 23: Fold OpCopyLogical feeding extract, even if multiple uses. + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct_type1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[struct_type2:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[ld:%\w+]] = OpLoad [[struct_type1]] [[var]] +; CHECK: [[ex:%\w+]] = OpCompositeExtract [[uint]] [[ld]] 0 +; CHECK: %13 = OpCopyObject [[uint]] [[ex]] + %struct1 = OpTypeStruct %uint %uint + %struct2 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct1 = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct1 StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 + %12 = OpCopyLogical %struct2 %11 + %13 = OpCompositeExtract %uint %12 0 + %14 = OpCompositeExtract %uint %12 1 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 24: LoadFeedingExtract + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_1:%\w+]] = OpConstant [[uint]] 1 +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]] +; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_1]] +; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] +; CHECK: OpCopyObject [[uint]] [[new_ld]] +%struct1 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 + %13 = OpCompositeExtract %uint %11 1 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 25: Fold multiple extracts + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_0:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]] +; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_0]] +; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] +; CHECK: OpCopyObject [[uint]] [[new_ld]] +%struct1 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 + %13 = OpCompositeExtract %uint %11 0 + %14 = OpCompositeExtract %uint %11 1 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 26: Don't fold function scope load. + InstructionFoldingCase<bool>( + Header() + R"( +%struct1 = OpTypeStruct %uint %uint +%_ptr_Function_struct = OpTypePointer Function %struct1 + %main = OpFunction %void None %void_func + %4 = OpLabel + %var1 = OpVariable %_ptr_Function_struct Function + %11 = OpLoad %struct1 %var1 + %13 = OpCompositeExtract %uint %11 0 + OpReturn + OpFunctionEnd + )", + 13, false), + // Test case 27: Don't fold volatile load feeding extract. + InstructionFoldingCase<bool>( + Header() + R"( +%struct1 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%3 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %3 Volatile + %13 = OpCompositeExtract %uint %11 0 + OpReturn + OpFunctionEnd + )", + 13, false), + // Test case 28: Fold with Aligned memory operand. + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_1:%\w+]] = OpConstant [[uint]] 1 +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]] +; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_1]] +; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] Aligned 4 +; CHECK: OpCopyObject [[uint]] [[new_ld]] + OpDecorate %struct1 Offset 0 + OpMemberDecorate %struct1 1 Offset 4 +%struct1 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 Aligned 16 + %13 = OpCompositeExtract %uint %11 1 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 29: Fold with Aligned and Nontemporal memory operands. + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_1:%\w+]] = OpConstant [[uint]] 1 +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]] +; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_1]] +; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] Aligned|Nontemporal 4 +; CHECK: OpCopyObject [[uint]] [[new_ld]] + OpDecorate %struct1 Offset 0 + OpMemberDecorate %struct1 1 Offset 4 +%struct1 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 Aligned|Nontemporal 16 + %13 = OpCompositeExtract %uint %11 1 + OpReturn + OpFunctionEnd + )", + 13, true), + // Test case 30: Fold with MakePointerVisible memory operand and scope. + InstructionFoldingCase<bool>( + Header() + R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]] +; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[idx:%\w+]] +; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] MakePointerVisible [[idx]] +; CHECK: OpCopyObject [[uint]] [[new_ld]] + OpDecorate %struct1 Offset 0 + OpMemberDecorate %struct1 1 Offset 4 +%struct1 = OpTypeStruct %uint %uint +%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1 +%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer + %main = OpFunction %void None %void_func + %4 = OpLabel + %11 = OpLoad %struct1 %var1 MakePointerVisible %uint_1 + %13 = OpCompositeExtract %uint %11 1 + OpReturn + OpFunctionEnd + )", + 13, true) )); INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest, @@ -8426,7 +14968,7 @@ std::unique_ptr<IRContext> context; Instruction* inst; - std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1); + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_5); // Find the instruction to test. EXPECT_EQ(inst != nullptr, tc.expected_result);
diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp index 1ccaa3e..3295a12 100644 --- a/test/opt/freeze_spec_const_test.cpp +++ b/test/opt/freeze_spec_const_test.cpp
@@ -172,6 +172,52 @@ /* skip_nop = */ true); } +TEST_F(FreezeSpecConstantValueRemoveDecorationTest, ConstantData) { + std::vector<const char*> text = { + // clang-format off + "OpCapability Shader", + "OpCapability ConstantDataKHR", + "OpExtension \"SPV_KHR_constant_data\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint GLCompute %uint_1 \"main\"", + "OpExecutionMode %2 LocalSize 1 1 1", + "OpDecorate %uint_1 SpecId 1", + "OpDecorate %3 SpecId 2", + "%void = OpTypeVoid", + "%uint = OpTypeInt 32 0", + "%uint_1 = OpSpecConstant %uint 1", +"%_arr_uint_2 = OpTypeArray %uint %2", + "%3 = OpSpecConstantDataKHR %_arr_uint_2 1", + "%7 = OpTypeFunction %void", + "%8 = OpFunction %void None %7", + "%9 = OpLabel", + "OpReturn", + "OpFunctionEnd", + + // clang-format on + }; + std::string expected_disassembly = SelectiveJoin(text, [](const char* line) { + return std::string(line).find("SpecId") != std::string::npos; + }); + std::vector<std::pair<const char*, const char*>> replacement_pairs = { + {"%uint_1 = OpSpecConstant %uint 1", "%uint_1 = OpConstant %uint 1"}, + {"%3 = OpSpecConstantDataKHR %_arr_uint_2 1", + "%3 = OpConstantDataKHR %_arr_uint_2 1"}, + }; + for (auto& p : replacement_pairs) { + EXPECT_TRUE(FindAndReplace(&expected_disassembly, p.first, p.second)) + << "text:\n" + << expected_disassembly << "\n" + << "find_str:\n" + << p.first << "\n" + << "replace_str:\n" + << p.second << "\n"; + } + SinglePassRunAndCheck<FreezeSpecConstantValuePass>(JoinAllInsts(text), + expected_disassembly, + /* skip_nop = */ true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp index a1a3b7d..20fb7e3 100644 --- a/test/opt/graphics_robust_access_test.cpp +++ b/test/opt/graphics_robust_access_test.cpp
@@ -110,6 +110,7 @@ OpCapability Shader OpMemoryModel Logical Simple OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 16 1 1 )"; } @@ -228,6 +229,7 @@ // TODO(dneto): RuntimeArray TEST_F(GraphicsRobustAccessTest, ACVectorLeastInboundConstantUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( @@ -244,6 +246,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorMostInboundConstantUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( @@ -260,6 +263,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorExcessConstantClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( @@ -276,6 +280,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorNegativeConstantClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( @@ -295,6 +300,7 @@ // Like the previous test, but ensures the pass knows how to modify an index // which does not come first in the access chain. TEST_F(GraphicsRobustAccessTest, ACVectorInArrayNegativeConstantClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( @@ -315,6 +321,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorGeneralClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() << R"( @@ -335,6 +342,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorGeneralShortClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Show that signed 16 bit integers are clamped as well. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -360,6 +368,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorGeneralUShortClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Show that unsigned 16 bit integers are clamped as well. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -385,6 +394,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorGeneralLongClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Show that signed 64 bit integers are clamped as well. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -410,6 +420,7 @@ } TEST_F(GraphicsRobustAccessTest, ACVectorGeneralULongClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Show that unsigned 64 bit integers are clamped as well. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -435,6 +446,7 @@ } TEST_F(GraphicsRobustAccessTest, ACMatrixLeastInboundConstantUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -454,6 +466,7 @@ } TEST_F(GraphicsRobustAccessTest, ACMatrixMostInboundConstantUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -473,6 +486,7 @@ } TEST_F(GraphicsRobustAccessTest, ACMatrixExcessConstantClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -492,6 +506,7 @@ } TEST_F(GraphicsRobustAccessTest, ACMatrixNegativeConstantClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -511,6 +526,7 @@ } TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() @@ -534,6 +550,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayLeastInboundConstantUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -551,6 +568,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayMostInboundConstantUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -568,6 +586,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() @@ -590,6 +609,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Index is signed short, array bounds overflows the index type. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -615,6 +635,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Index is unsigned short, array bounds overflows the index type. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -640,6 +661,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Signed int index i is wider than the array bounds type. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -664,6 +686,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Unsigned int index i is wider than the array bounds type. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -688,6 +711,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Signed long index i is wider than the array bounds type. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -712,6 +736,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Unsigned long index i is wider than the array bounds type. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -737,6 +762,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << "OpCapability Int16\n" @@ -761,6 +787,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() @@ -784,6 +811,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << "OpCapability Int64\n" @@ -811,6 +839,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"spec200"}) << R"( @@ -837,6 +866,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructLeastUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -853,6 +883,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructMostUntouched) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() @@ -869,6 +900,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructSpecConstantFail) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"struct", "spec200"}) @@ -891,6 +923,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructFloatConstantFail) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"struct"}) << @@ -911,6 +944,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructNonConstantFail) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"struct", "i"}) << @@ -932,6 +966,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructExcessFail) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt() @@ -951,6 +986,7 @@ } TEST_F(GraphicsRobustAccessTest, ACStructNegativeFail) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt() @@ -970,6 +1006,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC() << "OpDecorate %rtarr ArrayStride 4 " @@ -997,6 +1034,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << "OpCapability Int16\n" @@ -1028,6 +1066,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << "OpCapability Int16\n" @@ -1059,6 +1098,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 " @@ -1087,6 +1127,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 " @@ -1115,6 +1156,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << "OpCapability Int64" << ShaderPreambleAC({"i"}) @@ -1144,6 +1186,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); for (auto* ac : AccessChains()) { std::ostringstream shaders; shaders << "OpCapability Int64" << ShaderPreambleAC({"i"}) @@ -1173,6 +1216,7 @@ } TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // The point of this test is that the access chain can have indices past the // index into the runtime array. For good measure, the index into the final // struct is out of bounds. We have to clamp that index too. @@ -1213,6 +1257,7 @@ } TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Now add an additional level of arrays around the Block-decorated struct. for (auto* ac : AccessChains()) { std::ostringstream shaders; @@ -1257,6 +1302,7 @@ } TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Split the address calculation across two access chains. Force // the transform to walk up the access chains to find the base variable. for (auto* ac : AccessChains()) { @@ -1318,6 +1364,7 @@ TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks) { + SetTargetEnv(SPV_ENV_VULKAN_1_0); // Split the address calculation across two access chains. Force // the transform to walk up the access chains to find the base variable. // This time, put the different access chains in different basic blocks.
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index ef7ac37..f4785ab 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp
@@ -3584,8 +3584,8 @@ TEST_F(InlineTest, DebugSimpleHLSLPixelShader) { const std::string text = R"( -; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main -; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 1 47 [[dbg_main]] +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 2 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main +; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 2 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 @@ -3637,9 +3637,9 @@ %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float %19 = OpExtInst %void %1 DebugTypeVector %18 4 %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %19 %19 - %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main - %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 1 20 %22 FlagIsLocal 0 - %26 = OpExtInst %void %1 DebugLexicalBlock %15 1 47 %22 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 2 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main + %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 2 20 %22 FlagIsLocal 0 + %26 = OpExtInst %void %1 DebugLexicalBlock %15 2 47 %22 %main = OpFunction %void None %27 %28 = OpLabel %param_var_color = OpVariable %_ptr_Function_v4float Function @@ -3670,8 +3670,8 @@ // 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: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_2 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1 +; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_2 %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 @@ -3733,9 +3733,9 @@ %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 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_2 %uint_1 %16 %21 %uint_3 %uint_1 + %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_2 %uint_20 %22 %uint_4 %uint_0 + %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_47 %22 %main = OpFunction %void None %27 %28 = OpLabel %param_var_color = OpVariable %_ptr_Function_v4float Function @@ -4433,10 +4433,6 @@ OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %1 "main" OpExecutionMode %1 LocalSize 8 8 1 - OpDecorate %_ptr_PhysicalStorageBuffer__struct_5 ArrayStride 8 - OpMemberDecorate %_struct_3 0 Offset 0 - OpMemberDecorate %_struct_3 1 Offset 8 - OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4 OpMemberDecorate %_struct_5 0 Offset 0 OpMemberDecorate %_struct_5 1 Offset 4 OpDecorate %6 Aliased @@ -4471,6 +4467,155 @@ SinglePassRunAndMatch<InlineExhaustivePass>(text, true); } +TEST_F(InlineTest, DebugDeclareWithAccessChain) { + const std::string text = R"( +; CHECK: [[EmptyStruct:%[\w]+]] = OpTypeStruct %float +; CHECK-DAG: [[Struct:%[\w]+]] = OpTypeStruct [[EmptyStruct]] +; CHECK-DAG: [[PtrType:%[\w]+]] = OpTypePointer Function [[Struct]] +; CHECK-DAG: [[EmptyPtrType:%[\w]+]] = OpTypePointer Function [[EmptyStruct]] + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + OpExtension "SPV_KHR_relaxed_extended_instruction" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "computeMain" + OpExecutionMode %2 LocalSize 1 1 1 + %3 = OpString "s.hlsl" + %4 = OpString "float" + %5 = OpString "source" + %6 = OpString "a" + %7 = OpString "SomeStruct" + %8 = OpString "SomeStruct.getA" + %9 = OpString "" + %10 = OpString "this" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %void = OpTypeVoid + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + %uint_5 = OpConstant %uint 5 + %uint_11 = OpConstant %uint 11 + %uint_8 = OpConstant %uint 8 + %uint_288 = OpConstant %uint 288 + %25 = OpTypeFunction %void + %_struct_26 = OpTypeStruct %float + %_struct_27 = OpTypeStruct %_struct_26 +%_ptr_Function__struct_27 = OpTypePointer Function %_struct_27 +%_ptr_Function__struct_26 = OpTypePointer Function %_struct_26 +%_ptr_Function_float = OpTypePointer Function %float + %30 = OpTypeFunction %float %_ptr_Function__struct_26 %_ptr_Function_float + %31 = OpUndef %float + %32 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0 + %33 = OpExtInst %void %1 DebugSource %3 %5 + %34 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %33 %uint_5 + %35 = OpExtInst %void %1 DebugTypeMember %6 %32 %33 %uint_1 %uint_0 %uint_0 %uint_32 %uint_3 + %36 = OpExtInstWithForwardRefsKHR %void %1 DebugTypeComposite %7 %uint_1 %33 %uint_1 %uint_0 %34 %7 %uint_32 %uint_3 %35 %37 + %38 = OpExtInst %void %1 DebugTypeFunction %uint_3 %32 %36 + %37 = OpExtInst %void %1 DebugFunction %8 %38 %33 %uint_1 %uint_5 %36 %9 %uint_3 %uint_4 + %39 = OpExtInst %void %1 DebugLocalVariable %10 %36 %33 %uint_1 %uint_5 %37 %uint_288 %uint_1 + %52 = OpExtInst %void %1 DebugLocalVariable %10 %32 %33 %uint_1 %uint_5 %37 %uint_288 %uint_1 + %40 = OpExtInst %void %1 DebugExpression +; CHECK: OpFunction %void None +; CHECK: [[Var:%[\w]+]] = OpVariable [[PtrType]] Function +; CHECK: OpExtInst %void {{%[\w+]+}} DebugDeclare {{%[\w+]+}} [[Var]] {{%[\w+]+}} %int_0 +; CHECK: OpExtInst %void {{%[\w+]+}} DebugDeclare {{%[\w+]+}} [[Var]] {{%[\w+]+}} %int_0 %int_0 + %2 = OpFunction %void None %25 + %41 = OpLabel + %42 = OpVariable %_ptr_Function__struct_27 Function + %43 = OpAccessChain %_ptr_Function__struct_26 %42 %int_0 + %49 = OpAccessChain %_ptr_Function_float %43 %int_0 + %44 = OpFunctionCall %float %45 %43 %49 + OpReturn + OpFunctionEnd +; CHECK: OpFunction %float None + %45 = OpFunction %float None %30 + %46 = OpFunctionParameter %_ptr_Function__struct_26 + %50 = OpFunctionParameter %_ptr_Function_float + %47 = OpLabel + %48 = OpExtInst %void %1 DebugDeclare %39 %46 %40 + %51 = OpExtInst %void %1 DebugDeclare %52 %50 %40 + OpReturnValue %31 + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch<InlineExhaustivePass>(text, true); +} + +TEST_F(InlineTest, DebugDeclareWithDynamicAccessChainKilled) { + // Check that a DebugDeclare whose Variable operand is remapped to an + // OpAccessChain with a non-constant index is killed, because DebugDeclare + // Indexes operands must be constant integers. + const std::string text = R"( +; CHECK: OpFunction %void None +; CHECK-NOT: DebugDeclare +; CHECK: OpReturn + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + OpExtension "SPV_KHR_relaxed_extended_instruction" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + %3 = OpString "test.hlsl" + %4 = OpString "float" + %5 = OpString "source" + %6 = OpString "foo" + %7 = OpString "" + %8 = OpString "this" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %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_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %void = OpTypeVoid + %20 = OpTypeFunction %void +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Function_arr = OpTypePointer Function %_arr_float_uint_4 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Function_int = OpTypePointer Function %int + %25 = OpTypeFunction %float %_ptr_Function_float + %26 = OpUndef %float + %27 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0 + %28 = OpExtInst %void %1 DebugSource %3 %5 + %29 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %28 %uint_5 + %30 = OpExtInst %void %1 DebugTypeFunction %uint_3 %27 %27 + %31 = OpExtInst %void %1 DebugFunction %6 %30 %28 %uint_1 %uint_5 %29 %7 %uint_3 %uint_4 + %32 = OpExtInst %void %1 DebugLocalVariable %8 %27 %28 %uint_1 %uint_5 %31 %uint_4 %uint_1 + %33 = OpExtInst %void %1 DebugExpression + %2 = OpFunction %void None %20 + %34 = OpLabel + %35 = OpVariable %_ptr_Function_arr Function + %36 = OpVariable %_ptr_Function_int Function + OpStore %36 %int_0 + %37 = OpLoad %int %36 + %38 = OpAccessChain %_ptr_Function_float %35 %37 + %39 = OpFunctionCall %float %40 %38 + OpReturn + OpFunctionEnd +; CHECK: OpFunction %float None + %40 = OpFunction %float None %25 + %41 = OpFunctionParameter %_ptr_Function_float + %42 = OpLabel + %43 = OpExtInst %void %1 DebugDeclare %32 %41 %33 + OpReturnValue %26 + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch<InlineExhaustivePass>(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // Empty modules
diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp index 67961eb..fa230c5 100644 --- a/test/opt/instruction_test.cpp +++ b/test/opt/instruction_test.cpp
@@ -1526,7 +1526,7 @@ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); } -TEST_F(DescriptorTypeTest, GetShader100DebugOpcode) { +TEST_F(DescriptorTypeTest, GetShaderDebugOpcode) { const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" @@ -1541,11 +1541,11 @@ std::unique_ptr<IRContext> context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); Instruction* debug_expression = context->get_def_use_mgr()->GetDef(5); - EXPECT_EQ(debug_expression->GetShader100DebugOpcode(), - NonSemanticShaderDebugInfo100DebugExpression); + EXPECT_EQ(debug_expression->GetShaderDebugOpcode(), + NonSemanticShaderDebugInfoDebugExpression); Instruction* debug_source = context->get_def_use_mgr()->GetDef(6); - EXPECT_EQ(debug_source->GetShader100DebugOpcode(), - NonSemanticShaderDebugInfo100DebugSource); + EXPECT_EQ(debug_source->GetShaderDebugOpcode(), + NonSemanticShaderDebugInfoDebugSource); // Test that an opcode larger than the max will return Max. This instruction // cannot be in the assembly above because the assembler expects the string @@ -1553,16 +1553,17 @@ // file could have an arbitrary number. std::unique_ptr<Instruction> past_max(debug_expression->Clone(context.get())); const uint32_t kExtInstOpcodeInIndex = 1; - uint32_t large_opcode = NonSemanticShaderDebugInfo100InstructionsMax + 2; + uint32_t large_opcode = + static_cast<uint32_t>(NonSemanticShaderDebugInfoInstructionsMax) + 2u; past_max->SetInOperand(kExtInstOpcodeInIndex, {large_opcode}); - EXPECT_EQ(past_max->GetShader100DebugOpcode(), - NonSemanticShaderDebugInfo100InstructionsMax); + EXPECT_EQ(past_max->GetShaderDebugOpcode(), + NonSemanticShaderDebugInfoInstructionsMax); // Test that an opcode without a value in the enum, but less than Max returns // the same value. - uint32_t opcode = NonSemanticShaderDebugInfo100InstructionsMax - 2; + uint32_t opcode = NonSemanticShaderDebugInfoInstructionsMax - 2; past_max->SetInOperand(kExtInstOpcodeInIndex, {opcode}); - EXPECT_EQ(past_max->GetShader100DebugOpcode(), opcode); + EXPECT_EQ(past_max->GetShaderDebugOpcode(), opcode); } } // namespace
diff --git a/test/opt/ir_builder.cpp b/test/opt/ir_builder.cpp index f0cfc18..15d31d9 100644 --- a/test/opt/ir_builder.cpp +++ b/test/opt/ir_builder.cpp
@@ -256,6 +256,38 @@ Match(text, context.get()); } +TEST_F(IRBuilderTest, AddVariable) { + // Use Private beacuse its' enun is 7 which is higher + // than the ID limit. + const std::string text = R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer Private [[uint]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Private +; CHECK: OpTypeFloat +OpCapability Kernel +OpCapability VectorComputeINTEL +OpCapability Linkage +OpExtension "SPV_INTEL_vector_compute" +OpMemoryModel Logical OpenCL +%1 = OpTypeInt 32 0 +%2 = OpTypePointer Private %1 +%3 = OpTypeFloat 32 +)"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(nullptr, context) << text; + + auto* float_ty = context->get_def_use_mgr()->GetDef(3); + InstructionBuilder builder(context.get(), float_ty); + auto* var = builder.AddVariable(2u, uint32_t(spv::StorageClass::Private)); + EXPECT_NE(nullptr, var); + + context->get_def_use_mgr()->AnalyzeInstDefUse(var); // should not assert + + Match(text, context.get()); +} + TEST_F(IRBuilderTest, AddCompositeConstruct) { const std::string text = R"( ; CHECK: [[uint:%\w+]] = OpTypeInt
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp index 769a25d..6e0aa41 100644 --- a/test/opt/ir_loader_test.cpp +++ b/test/opt/ir_loader_test.cpp
@@ -1523,6 +1523,74 @@ }); } +TEST(IrBuilder, RoundTripSimpleGraphWithBody) { + DoRoundTripCheck(R"(OpCapability Shader +OpCapability TensorsARM +OpCapability GraphARM +OpExtension "SPV_ARM_tensors" +OpExtension "SPV_ARM_graph" +%1 = OpExtInstImport "TOSA.001000.1" +OpMemoryModel Logical GLSL450 +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%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 +%float_0 = OpConstant %float 0 +%_arr_uint_uint_1 = OpTypeArray %uint %uint_1 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%16 = OpConstantComposite %_arr_uint_uint_1 %uint_1 +%17 = OpConstantComposite %_arr_uint_uint_1 %uint_2 +%18 = OpConstantComposite %_arr_uint_uint_1 %uint_4 +%19 = OpConstantComposite %_arr_uint_uint_4 %uint_1 %uint_10 %uint_10 %uint_3 +%20 = OpConstantComposite %_arr_uint_uint_4 %uint_1 %uint_5 %uint_5 %uint_3 +%21 = OpTypeTensorARM %float %uint_1 %16 +%22 = OpTypeTensorARM %uint %uint_1 %17 +%23 = OpTypeTensorARM %uint %uint_1 %18 +%24 = OpTypeTensorARM %float %uint_4 %19 +%25 = OpTypeTensorARM %float %uint_4 %20 +%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%28 = OpConstantComposite %23 %uint_3 %uint_3 +%29 = OpConstantComposite %23 %uint_2 %uint_2 +%30 = OpConstantComposite %24 %uint_0 %uint_0 %uint_0 %uint_0 +%31 = OpConstantComposite %22 %float_0 +%32 = OpTypeGraphARM 1 %24 %25 +%2 = OpVariable %_ptr_UniformConstant_24 UniformConstant +%3 = OpVariable %_ptr_UniformConstant_25 UniformConstant +OpGraphEntryPointARM %33 "main" %2 %3 +%33 = OpGraphARM %32 +%34 = OpGraphInputARM %24 %uint_0 +%35 = OpExtInst %25 %1 AVG_POOL2D %28 %29 %30 %uint_2 %34 %31 %31 +OpGraphSetOutputARM %35 %uint_0 +OpGraphEndARM +)"); +} + +TEST(IrBuilder, GraphInsideGraph) { + DoErrorMessageCheck("%2 = OpGraphARM %1\n%3 = OpGraphARM %2", + "graph inside graph", 2); +} + +TEST(IrBuilder, GraphEndOutsideOfGraph) { + DoErrorMessageCheck("OpGraphEndARM\n", + "OpGraphEndARM without corresponding OpGraphARM", 1); +} + +TEST(IrBuilder, GraphUnhandledInstruction) { + DoErrorMessageCheck( + "%2 = OpGraphARM %1\n%3 = OpGraphInputARM %4 %5\nOpNop\nOpGraphEndARM", + "unhandled instruction (opcode 0) inside graph", 3); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/legalize_multidim_array_test.cpp b/test/opt/legalize_multidim_array_test.cpp new file mode 100644 index 0000000..c7bb6b2 --- /dev/null +++ b/test/opt/legalize_multidim_array_test.cpp
@@ -0,0 +1,639 @@ +// Copyright (c) 2026 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 "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using LegalizeMultidimArrayTest = PassTest<::testing::Test>; + +TEST_F(LegalizeMultidimArrayTest, Flatten2DResourceArray) { + // HLSL: + // Texture2D g_Textures[2][3]; + // SamplerState g_Sampler; + // float4 main(float2 uv : TEXCOORD) : SV_Target { + // return g_Textures[0][1].Sample(g_Sampler, uv); + // } + const std::string text = R"( +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %_arr_type_2d_image_uint_6 = OpTypeArray %type_2d_image %uint_6 +; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_6 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_6 +; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant +; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3 +; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1 +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx]] +; CHECK: OpLoad %type_2d_image [[ptr]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpName %type_sampler "type.sampler" + OpName %g_Sampler "g_Sampler" + OpName %main "main" + OpName %src_main "src.main" + OpDecorate %in_var_TEXCOORD Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + OpDecorate %g_Sampler DescriptorSet 0 + OpDecorate %g_Sampler Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2 +%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2 +%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 + %24 = OpTypeFunction %void +%_ptr_Function_v2float = OpTypePointer Function %v2float + %31 = OpTypeFunction %v4float %_ptr_Function_v2float +%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampled_image = OpTypeSampledImage %type_2d_image + %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant + %g_Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %24 + %25 = OpLabel +%param_var_uv = OpVariable %_ptr_Function_v2float Function + %28 = OpLoad %v2float %in_var_TEXCOORD + OpStore %param_var_uv %28 + %29 = OpFunctionCall %v4float %src_main %param_var_uv + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %31 + %uv = OpFunctionParameter %_ptr_Function_v2float + %bb_entry = OpLabel + %37 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1 + %38 = OpLoad %type_2d_image %37 + %39 = OpLoad %type_sampler %g_Sampler + %40 = OpLoad %v2float %uv + %42 = OpSampledImage %type_sampled_image %38 %39 + %43 = OpImageSampleImplicitLod %v4float %42 %40 None + OpReturnValue %43 + OpFunctionEnd + )"; + + const std::string expected = R"( +)"; + + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, + /*do_validation=*/true); +} + +// Test that the pass fails when the access chain is split into multiple access +// chains. We expect CombineAccessChains to be run before this pass to avoid +// this. +TEST_F(LegalizeMultidimArrayTest, IndirectUseViaPartialAccessChain) { + // HLSL source approximation: + // Texture2D g_Textures[2][3]; + // ... + // Texture2D row[3] = g_Textures[0]; + // return row[1].Sample(...); + // + // In SPIR-V, this often looks like: + // %ptr_row = OpAccessChain %_ptr_UniformConstant_arr_type_2d_image_uint_3 + // %g_Textures %int_0 %ptr_tex = OpAccessChain + // %_ptr_UniformConstant_type_2d_image %ptr_row %int_1 OpLoad %type_2d_image + // %ptr_tex + + const std::string text = R"( + ; CHECK: Unable to legalize multidimensional array + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2 +%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %24 = OpTypeFunction %void + %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %24 + %25 = OpLabel + %37 = OpAccessChain %_ptr_UniformConstant__arr_type_2d_image_uint_3 %g_Textures %int_0 + %38 = OpAccessChain %_ptr_UniformConstant_type_2d_image %37 %int_1 + %39 = OpLoad %type_2d_image %38 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndFail<LegalizeMultidimArrayPass>(text); +} + +TEST_F(LegalizeMultidimArrayTest, Flatten3DResourceArray) { + // Texture2D g_Textures[2][3][4]; + // Access: g_Textures[0][1][2] + const std::string text = R"( +; CHECK: %uint_24 = OpConstant %uint 24 +; CHECK: %_arr_type_2d_image_uint_24 = OpTypeArray %type_2d_image %uint_24 +; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_24 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_24 +; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_24 UniformConstant +; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_12 +; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_4 +; CHECK: [[add1:%\w+]] = OpIAdd %uint [[mul1]] [[mul2]] +; CHECK: [[final_idx:%\w+]] = OpIAdd %uint [[add1]] %int_2 +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[final_idx]] +; CHECK: OpLoad %type_2d_image [[ptr]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_12 = OpConstant %uint 12 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_4 = OpTypeArray %type_2d_image %uint_4 +%_arr_arr_type_2d_image_uint_4_uint_3 = OpTypeArray %_arr_type_2d_image_uint_4 %uint_3 +%_arr_arr_arr_type_2d_image_uint_4_uint_3_uint_2 = OpTypeArray %_arr_arr_type_2d_image_uint_4_uint_3 %uint_2 +%_ptr_UniformConstant_arr_3d = OpTypePointer UniformConstant %_arr_arr_arr_type_2d_image_uint_4_uint_3_uint_2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Textures = OpVariable %_ptr_UniformConstant_arr_3d UniformConstant + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1 %int_2 + %val = OpLoad %type_2d_image %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, FlattenSamplerArray) { + // SamplerState g_Samplers[2][2]; + const std::string text = R"( +; CHECK: %uint_4 = OpConstant %uint 4 +; CHECK: %_arr_type_sampler_uint_4 = OpTypeArray %type_sampler %uint_4 +; CHECK: %_ptr_UniformConstant__arr_type_sampler_uint_4 = OpTypePointer UniformConstant %_arr_type_sampler_uint_4 +; CHECK: %g_Samplers = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_4 UniformConstant +; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_2 +; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1 +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers [[idx]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %type_sampler "type.sampler" + OpName %g_Samplers "g_Samplers" + OpDecorate %g_Samplers DescriptorSet 0 + OpDecorate %g_Samplers Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%type_sampler = OpTypeSampler +%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2 +%_arr_arr_type_sampler_uint_2_uint_2 = OpTypeArray %_arr_type_sampler_uint_2 %uint_2 +%_ptr_UniformConstant_arr_2d = OpTypePointer UniformConstant %_arr_arr_type_sampler_uint_2_uint_2 +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Samplers = OpVariable %_ptr_UniformConstant_arr_2d UniformConstant + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers %int_0 %int_1 + %val = OpLoad %type_sampler %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, FlattenStorageBufferArray) { + // struct S { float f; }; + // S buffers[2][3]; + // buffers[0][1].f + const std::string text = R"( +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %_arr_S_uint_6 = OpTypeArray %S %uint_6 +; CHECK: %_ptr_StorageBuffer__arr_S_uint_6 = OpTypePointer StorageBuffer %_arr_S_uint_6 +; CHECK: %g_Buffers = OpVariable %_ptr_StorageBuffer__arr_S_uint_6 StorageBuffer +; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3 +; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1 +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_StorageBuffer_float %g_Buffers [[idx]] %int_0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %S "S" + OpName %g_Buffers "g_Buffers" + OpDecorate %g_Buffers DescriptorSet 0 + OpDecorate %g_Buffers Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %S Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %S = OpTypeStruct %float +%_arr_S_uint_3 = OpTypeArray %S %uint_3 +%_arr__arr_S_uint_3_uint_2 = OpTypeArray %_arr_S_uint_3 %uint_2 +%_ptr_StorageBuffer_arr_2d = OpTypePointer StorageBuffer %_arr__arr_S_uint_3_uint_2 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Buffers = OpVariable %_ptr_StorageBuffer_arr_2d StorageBuffer + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr = OpAccessChain %_ptr_StorageBuffer_float %g_Buffers %int_0 %int_1 %int_0 + %val = OpLoad %float %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, FlattenUniformArray) { + // Uniform buffer array: MyBlock buffers[2][3]; + // Access: buffers[0][1].member + const std::string text = R"( +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %_arr_MyBlock_uint_6 = OpTypeArray %MyBlock %uint_6 +; CHECK: %_ptr_Uniform__arr_MyBlock_uint_6 = OpTypePointer Uniform %_arr_MyBlock_uint_6 +; CHECK: %g_Uniforms = OpVariable %_ptr_Uniform__arr_MyBlock_uint_6 Uniform +; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3 +; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1 +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Uniform_float %g_Uniforms [[idx]] %int_0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %MyBlock "MyBlock" + OpName %g_Uniforms "g_Uniforms" + OpDecorate %g_Uniforms DescriptorSet 0 + OpDecorate %g_Uniforms Binding 0 + OpMemberDecorate %MyBlock 0 Offset 0 + OpDecorate %MyBlock Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %MyBlock = OpTypeStruct %float +%_arr_MyBlock_uint_3 = OpTypeArray %MyBlock %uint_3 +%_arr__arr_MyBlock_uint_3_uint_2 = OpTypeArray %_arr_MyBlock_uint_3 %uint_2 +%_ptr_Uniform__arr__arr_MyBlock_uint_3_uint_2 = OpTypePointer Uniform %_arr__arr_MyBlock_uint_3_uint_2 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Uniforms = OpVariable %_ptr_Uniform__arr__arr_MyBlock_uint_3_uint_2 Uniform + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr = OpAccessChain %_ptr_Uniform_float %g_Uniforms %int_0 %int_1 %int_0 + %val = OpLoad %float %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, AccessChainThroughCopyObject) { + // Texture2D g_Textures[2][3]; + // %copy = OpCopyObject %ptr_type %g_Textures + // %ptr = OpAccessChain %... %copy %int_0 %int_1 + const std::string text = R"( +; CHECK: %uint_6 = OpConstant %uint 6 +; CHECK: %_arr_type_2d_image_uint_6 = OpTypeArray %type_2d_image %uint_6 +; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_6 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_6 +; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant +; CHECK: [[copy:%\w+]] = OpCopyObject %_ptr_UniformConstant__arr_type_2d_image_uint_6 %g_Textures +; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3 +; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1 +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image [[copy]] [[idx]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2 +%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant + %main = OpFunction %void None %main_func + %label = OpLabel + %copy = OpCopyObject %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 %g_Textures + %ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %copy %int_0 %int_1 + %val = OpLoad %type_2d_image %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, DynamicIndices) { + // Access with non-constant indices. + // g_Textures[var_i][var_j] + const std::string text = R"( +; CHECK: [[idx1:%\w+]] = OpLoad %int %idx_var_1 +; CHECK: [[idx2:%\w+]] = OpLoad %int %idx_var_2 +; CHECK: [[mul:%\w+]] = OpIMul %uint [[idx1]] %uint_3 +; CHECK: [[add:%\w+]] = OpIAdd %uint [[mul]] [[idx2]] +; CHECK: OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[add]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpName %idx_var_1 "idx_var_1" + OpName %idx_var_2 "idx_var_2" + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2 +%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%_ptr_Function_int = OpTypePointer Function %int + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant + %main = OpFunction %void None %main_func + %label = OpLabel + %idx_var_1 = OpVariable %_ptr_Function_int Function + %idx_var_2 = OpVariable %_ptr_Function_int Function + %i = OpLoad %int %idx_var_1 + %j = OpLoad %int %idx_var_2 + %ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %i %j + %val = OpLoad %type_2d_image %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, IgnoreFunctionScopeArray) { + // Function scope array [2][3] should NOT be legalized. + const std::string text = R"( +; CHECK: %_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2 +; CHECK: %_ptr_Function__arr__arr_float_uint_3_uint_2 = OpTypePointer Function %_arr__arr_float_uint_3_uint_2 +; CHECK: %var = OpVariable %_ptr_Function__arr__arr_float_uint_3_uint_2 Function +; CHECK: OpAccessChain %_ptr_Function_float %var %int_0 %int_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %var "var" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2 +%_ptr_Function__arr__arr_float_uint_3_uint_2 = OpTypePointer Function %_arr__arr_float_uint_3_uint_2 +%_ptr_Function_float = OpTypePointer Function %float + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %main = OpFunction %void None %main_func + %label = OpLabel + %var = OpVariable %_ptr_Function__arr__arr_float_uint_3_uint_2 Function + %ptr = OpAccessChain %_ptr_Function_float %var %int_0 %int_1 + %val = OpLoad %float %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, IgnoreWorkgroupScopeArray) { + // Workgroup scope array [2][3] should NOT be legalized. + const std::string text = R"( +; CHECK: %_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2 +; CHECK: %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_float_uint_3_uint_2 +; CHECK: %var = OpVariable %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 Workgroup +; CHECK: OpAccessChain %_ptr_Workgroup_float %var %int_0 %int_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %var "var" + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2 +%_ptr_Workgroup__arr__arr_float_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_float_uint_3_uint_2 +%_ptr_Workgroup_float = OpTypePointer Workgroup %float + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %var = OpVariable %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 Workgroup + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr = OpAccessChain %_ptr_Workgroup_float %var %int_0 %int_1 + %val = OpLoad %float %ptr + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, MultipleAccessChains) { + // Access g_Textures[0][1] and g_Textures[1][2] in the same function. + const std::string text = R"( +; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_3 +; CHECK: [[idx1:%\w+]] = OpIAdd %uint [[mul1]] %int_1 +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx1]] +; CHECK: OpLoad %type_2d_image [[ptr1]] +; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_3 +; CHECK: [[idx2:%\w+]] = OpIAdd %uint [[mul2]] %int_2 +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx2]] +; CHECK: OpLoad %type_2d_image [[ptr2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2 +%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr1 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1 + %val1 = OpLoad %type_2d_image %ptr1 + %ptr2 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_1 %int_2 + %val2 = OpLoad %type_2d_image %ptr2 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +TEST_F(LegalizeMultidimArrayTest, MultipleResources) { + // Two different resource arrays: + // Texture2D g_Textures[2][3]; + // SamplerState g_Samplers[2][2]; + const std::string text = R"( +; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant +; CHECK: %g_Samplers = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_4 UniformConstant +; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_3 +; CHECK: [[idx1:%\w+]] = OpIAdd %uint [[mul1]] %int_1 +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx1]] +; CHECK: OpLoad %type_2d_image [[ptr1]] +; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_2 +; CHECK: [[idx2:%\w+]] = OpIAdd %uint [[mul2]] %int_1 +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers [[idx2]] +; CHECK: OpLoad %type_sampler [[ptr2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %type_2d_image "type.2d.image" + OpName %g_Textures "g_Textures" + OpName %type_sampler "type.sampler" + OpName %g_Samplers "g_Samplers" + OpDecorate %g_Textures DescriptorSet 0 + OpDecorate %g_Textures Binding 0 + OpDecorate %g_Samplers DescriptorSet 0 + OpDecorate %g_Samplers Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3 +%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2 +%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2 +%_arr_arr_type_sampler_uint_2_uint_2 = OpTypeArray %_arr_type_sampler_uint_2 %uint_2 +%_ptr_UniformConstant_arr_2d_sampler = OpTypePointer UniformConstant %_arr_arr_type_sampler_uint_2_uint_2 +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %void = OpTypeVoid + %main_func = OpTypeFunction %void + %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant + %g_Samplers = OpVariable %_ptr_UniformConstant_arr_2d_sampler UniformConstant + %main = OpFunction %void None %main_func + %label = OpLabel + %ptr1 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1 + %val1 = OpLoad %type_2d_image %ptr1 + %ptr2 = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers %int_1 %int_1 + %val2 = OpLoad %type_sampler %ptr2 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools \ No newline at end of file
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp index b35f3a3..8b12314 100644 --- a/test/opt/local_access_chain_convert_test.cpp +++ b/test/opt/local_access_chain_convert_test.cpp
@@ -1344,6 +1344,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false, true); }
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp index ffe352e..8fd5c9d 100644 --- a/test/opt/local_single_store_elim_test.cpp +++ b/test/opt/local_single_store_elim_test.cpp
@@ -24,6 +24,56 @@ using LocalSingleStoreElimTest = PassTest<::testing::Test>; +TEST_F(LocalSingleStoreElimTest, DoSomethingWithExtensions) { + const std::string capabilities_and_extensions = + R"(OpCapability Shader +OpExtension "SPV_EXT_fragment_shader_interlock" +OpExtension "SPV_NV_compute_shader_derivatives" +OpExtension "SPV_KHR_ray_query" +OpExtension "SPV_NV_shader_subgroup_partitioned" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_EXT_descriptor_indexing" +)"; + + const std::string before = capabilities_and_extensions + + R"(%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%2 = OpFunction %void None %4 +%8 = OpLabel +%9 = OpVariable %_ptr_Function_float Function +OpStore %9 %float_0 +%10 = OpLoad %float %9 +OpReturn +OpFunctionEnd +)"; + const std::string after = capabilities_and_extensions + + R"(%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%2 = OpFunction %void None %4 +%8 = OpLabel +%9 = OpVariable %_ptr_Function_float Function +OpStore %9 %float_0 +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndCheck<LocalSingleStoreElimPass>(before, after, true, true); +} TEST_F(LocalSingleStoreElimTest, PositiveAndNegative) { // Single store to v is optimized. Multiple store to // f is not optimized.
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp index 45006ca..4ea0926 100644 --- a/test/opt/local_ssa_elim_test.cpp +++ b/test/opt/local_ssa_elim_test.cpp
@@ -2978,7 +2978,9 @@ ; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]] ; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugScope [[dbg_bb]] ; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_x]] %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugScope [[dbg_main]] ; 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 @@ -4223,6 +4225,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; SinglePassRunAndMatch<SSARewritePass>(text, true); } @@ -5434,6 +5437,7 @@ %1614 = OpLabel ;CHECK: %1614 = OpLabel ;CHECK-NEXT: [[phi:%\w+]] = OpPhi +;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugScope %179 ;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue %233 %2335 = OpExtInst %void %2 DebugScope %179 %1795 = OpExtInst %void %2 DebugLine %64 %uint_149 %uint_149 %uint_16 %uint_16 @@ -5456,6 +5460,50 @@ SinglePassRunAndMatch<SSARewritePass>(text, true); } +TEST_F(LocalSSAElimTest, StoreWithLoadedPtr) { + const std::string text = + R"(OpCapability Shader +OpCapability VariablePointersStorageBuffer +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "pointer_branch_buffer" %3 +OpExecutionMode %2 LocalSize 8 8 1 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%_ptr_Function__ptr_StorageBuffer_uint = OpTypePointer Function %_ptr_StorageBuffer_uint +%3 = OpVariable %_ptr_StorageBuffer_uint StorageBuffer +%11 = OpUndef %bool +%2 = OpFunction %void None %8 +%12 = OpLabel +%13 = OpVariable %_ptr_Function__ptr_StorageBuffer_uint Function +OpSelectionMerge %14 None +OpBranchConditional %11 %15 %16 +%15 = OpLabel +OpBranch %14 +%16 = OpLabel +OpStore %13 %3 +OpBranch %14 +%14 = OpLabel +; CHECK: [[phi:%\w+]] = OpPhi %_ptr_StorageBuffer_uint +; CHECK-NEXT: [[ld:%\w+]] = OpLoad %uint %20 +; CHECK-NEXT: OpIAdd %uint [[ld]] %uint_2 +%17 = OpLoad %_ptr_StorageBuffer_uint %13 +%18 = OpLoad %uint %17 +%19 = OpIAdd %uint %18 %uint_2 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SinglePassRunAndMatch<SSARewritePass>(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // No optimization in the presence of
diff --git a/test/opt/loop_optimizations/fusion_legal.cpp b/test/opt/loop_optimizations/fusion_legal.cpp index ef7daee..cda72a0 100644 --- a/test/opt/loop_optimizations/fusion_legal.cpp +++ b/test/opt/loop_optimizations/fusion_legal.cpp
@@ -3415,7 +3415,8 @@ EXPECT_FALSE(fusion.AreCompatible()); } - ld.CreatePreHeaderBlocksIfMissing(); + auto status = ld.CreatePreHeaderBlocksIfMissing(); + EXPECT_NE(status, LoopDescriptor::Status::Failure); { LoopFusion fusion(context.get(), loops[0], loops[1]); @@ -3588,7 +3589,8 @@ EXPECT_FALSE(fusion.AreCompatible()); } - ld.CreatePreHeaderBlocksIfMissing(); + auto status = ld.CreatePreHeaderBlocksIfMissing(); + EXPECT_NE(status, LoopDescriptor::Status::Failure); { LoopFusion fusion(context.get(), loops[0], loops[1]);
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp index b4fd3ea..256b487 100644 --- a/test/opt/loop_optimizations/unroll_simple.cpp +++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -3788,6 +3788,48 @@ SinglePassRunAndMatch<PartialUnrollerTestPass<2>>(text, true); } +TEST_F(PassClassTest, UnrollWithDecorationOnPhi) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 16 16 1 +; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision + OpDecorate %4 RelaxedPrecision + %float = OpTypeFloat 32 +%float_0_000122070312 = OpConstant %float 0.000122070312 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %bool = OpTypeBool + %void = OpTypeVoid + %12 = OpTypeFunction %void + %2 = OpFunction %void None %12 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + %4 = OpPhi %float %float_0_000122070312 %13 %3 %15 + %16 = OpPhi %int %int_0 %13 %17 %15 + %18 = OpSLessThan %bool %16 %int_1 + OpLoopMerge %19 %15 Unroll + OpBranchConditional %18 %15 %19 + %15 = OpLabel +; CHECK: [[v:%\w+]] = OpExtInst %float + %3 = OpExtInst %float %1 NMax %float_0_000122070312 %float_0_000122070312 + %17 = OpIAdd %int %16 %int_1 + OpBranch %14 + %19 = OpLabel +; CHECK: OpCopyObject %float [[v]] + %20 = OpCopyObject %float %4 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch<LoopUnroller>(text, true); +} + TEST_F(PassClassTest, DontUnrollInfiteLoop) { // This is an infinite loop that because the step is 0. We want to make sure // the unroller does not try to unroll it. @@ -3822,6 +3864,81 @@ SinglePassRunAndCheck<LoopUnroller>(text, text, false); } +TEST_F(PassClassTest, ApplyDecorationsToClonedInstructions) { + const std::string text = R"( + ; CHECK: OpDecorate [[ld1:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[mul1:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[add1:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[ld2:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[mul2:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[add2:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[ld3:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[mul3:%\w+]] RelaxedPrecision + ; CHECK: OpDecorate [[add3:%\w+]] RelaxedPrecision + +; CHECK: [[ld1]] = OpLoad %float +; CHECK: [[mul1]] = OpFMul %float +; CHECK: [[add1]] = OpFAdd %float +; CHECK: [[ld2]] = OpLoad %float +; CHECK: [[mul2]] = OpFMul %float +; CHECK: [[add2]] = OpFAdd %float +; CHECK: [[ld3]] = OpLoad %float +; CHECK: [[mul3]] = OpFMul %float +; CHECK: [[add3]] = OpFAdd %float + + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" %2 + OpExecutionMode %1 LocalSize 1 1 1 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_struct_4 Block + OpDecorate %5 RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_4 = OpTypeStruct %_runtimearr_float +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %16 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + %2 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %1 = OpFunction %void None %16 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %int %int_0 %19 %22 %23 + %24 = OpSLessThan %bool %21 %int_3 + OpLoopMerge %25 %23 Unroll + OpBranchConditional %24 %26 %25 + %26 = OpLabel + %27 = OpBitcast %uint %21 + %28 = OpAccessChain %_ptr_StorageBuffer_float %2 %int_0 %27 + %5 = OpLoad %float %28 + %6 = OpFMul %float %5 %5 + %7 = OpFAdd %float %5 %6 + OpStore %28 %7 + OpBranch %23 + %23 = OpLabel + %22 = OpIAdd %int %21 %int_1 + OpBranch %20 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SinglePassRunAndMatch<LoopUnroller>(text, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp index 0171c09..76457dd 100644 --- a/test/opt/optimizer_test.cpp +++ b/test/opt/optimizer_test.cpp
@@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "spirv-tools/optimizer.hpp" + +#include <sstream> #include <string> #include <vector> #include "gmock/gmock.h" #include "spirv-tools/libspirv.hpp" -#include "spirv-tools/optimizer.hpp" #include "test/opt/pass_fixture.h" namespace spvtools { @@ -388,6 +390,213 @@ << "Was expecting the result id of DebugScope to have been changed."; } +TEST(Optimizer, CheckDefaultPerformancePassesLargeStructScalarization) { + std::string start = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %46 %48 +OpSource GLSL 430 +OpName %4 "main" +OpDecorate %44 Block +OpMemberDecorate %44 0 BuiltIn Position +OpMemberDecorate %44 1 BuiltIn PointSize +OpMemberDecorate %44 2 BuiltIn ClipDistance +OpDecorate %48 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypePointer Function %7 +%9 = OpTypeStruct %7)"; + + // add 200 float members to the struct + for (int i = 0; i < 200; i++) { + start += " %6"; + } + + start += R"( +%10 = OpTypeFunction %9 %8 +%14 = OpTypeFunction %6 %9 +%18 = OpTypePointer Function %9 +%20 = OpTypeInt 32 1 +%21 = OpConstant %20 0 +%24 = OpConstant %20 1 +%25 = OpTypeInt 32 0 +%26 = OpConstant %25 1 +%27 = OpTypePointer Function %6 +%43 = OpTypeArray %6 %26 +%44 = OpTypeStruct %7 %6 %43 +%45 = OpTypePointer Output %44 +%46 = OpVariable %45 Output +%47 = OpTypePointer Input %7 +%48 = OpVariable %47 Input +%54 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%49 = OpVariable %8 Function +%50 = OpLoad %7 %48 +OpStore %49 %50 +%51 = OpFunctionCall %9 %12 %49 +%52 = OpFunctionCall %6 %16 %51 +%53 = OpCompositeConstruct %7 %52 %52 %52 %52 +%55 = OpAccessChain %54 %46 %21 +OpStore %55 %53 +OpReturn +OpFunctionEnd +%12 = OpFunction %9 None %10 +%11 = OpFunctionParameter %8 +%13 = OpLabel +%19 = OpVariable %18 Function +%22 = OpLoad %7 %11 +%23 = OpAccessChain %8 %19 %21 +OpStore %23 %22 +%28 = OpAccessChain %27 %11 %26 +%29 = OpLoad %6 %28 +%30 = OpConvertFToS %20 %29 +%31 = OpAccessChain %27 %19 %21 %30 +%32 = OpLoad %6 %31 +%33 = OpAccessChain %27 %19 %24 +OpStore %33 %32 +%34 = OpLoad %9 %19 +OpReturnValue %34 +OpFunctionEnd +%16 = OpFunction %6 None %14 +%15 = OpFunctionParameter %9 +%17 = OpLabel +%37 = OpCompositeExtract %6 %15 1 +%38 = OpConvertFToS %20 %37 +%39 = OpCompositeExtract %7 %15 0 +%40 = OpVectorExtractDynamic %6 %39 %38 +OpReturnValue %40 +OpFunctionEnd)"; + + std::vector<uint32_t> binary; + SpirvTools tools(SPV_ENV_VULKAN_1_3); + tools.Assemble(start, &binary, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::string test_disassembly; + std::string default_disassembly; + + { + Optimizer opt(SPV_ENV_VULKAN_1_3); + opt.RegisterPerformancePasses(); + + std::vector<uint32_t> optimized; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized)) + << start << "\n"; + + tools.Disassemble(optimized.data(), optimized.size(), &default_disassembly, + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } + + { + // default passes should not benefit from additional scalar replacement + Optimizer opt(SPV_ENV_VULKAN_1_3); + opt.RegisterPerformancePasses() + .RegisterPass(CreateScalarReplacementPass(201)) + .RegisterPass(CreateAggressiveDCEPass()); + + std::vector<uint32_t> optimized; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized)) + << start << "\n"; + + tools.Disassemble(optimized.data(), optimized.size(), &test_disassembly, + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } + + EXPECT_EQ(test_disassembly, default_disassembly); +} + +TEST(Optimizer, KeepDebugBuildIdentifierAfterDCE) { + // Test that DebugBuildIdentifier is not removed after DCE. + const std::string before = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 8 8 1 +%4 = OpString "8937d8f571cf7b58d86d9d66196024f5d04e3186" +%7 = OpString "" +%9 = OpString "" +OpSource Slang 1 +%19 = OpString "" +%24 = OpString "" +%25 = OpString "" +OpName %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_11 = OpConstant %uint 11 +%uint_5 = OpConstant %uint 5 +%uint_100 = OpConstant %uint 100 +%15 = OpTypeFunction %void +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%3 = OpExtInst %void %1 DebugBuildIdentifier %4 %uint_0 +%8 = OpExtInst %void %1 DebugSource %9 %7 +%13 = OpExtInst %void %1 DebugCompilationUnit %uint_100 %uint_5 %8 %uint_11 +%17 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void +%18 = OpExtInst %void %1 DebugFunction %19 %17 %8 %uint_5 %uint_6 %13 %19 %uint_0 %uint_5 +%23 = OpExtInst %void %1 DebugEntryPoint %18 %13 %24 %25 +%main = OpFunction %void None %15 +%16 = OpLabel +%21 = OpExtInst %void %1 DebugFunctionDefinition %18 %main +%32 = OpExtInst %void %1 DebugScope %18 +%26 = OpExtInst %void %1 DebugLine %8 %uint_7 %uint_7 %uint_1 %uint_2 +OpReturn +%33 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd + )"; + + std::vector<uint32_t> binary; + SpirvTools tools(SPV_ENV_VULKAN_1_3); + tools.Assemble(before, &binary, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + Optimizer opt(SPV_ENV_VULKAN_1_3); + opt.RegisterPerformancePasses().RegisterPass(CreateAggressiveDCEPass()); + + std::vector<uint32_t> optimized; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized)) + << before << "\n"; + + std::string after; + tools.Disassemble(optimized.data(), optimized.size(), &after, + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Test that the DebugBuildIdentifier is not removed after DCE. + size_t dbi_pos = after.find("DebugBuildIdentifier"); + EXPECT_NE(dbi_pos, std::string::npos) + << "Was expecting the DebugBuildIdentifier to have been kept."; + std::string string_id; + std::string flags_id; + if (dbi_pos != std::string::npos) { + std::stringstream ss(after.substr(dbi_pos)); + std::string temp; + char percent; + ss >> temp; // Consume "DebugBuildIdentifier" + ss >> percent >> string_id; + ss >> percent >> flags_id; + } + + EXPECT_FALSE(string_id.empty()) + << "Could not find string id for DebugBuildIdentifier."; + EXPECT_FALSE(flags_id.empty()) + << "Could not find flags id for DebugBuildIdentifier."; + + bool found = + (after.find("%" + string_id + " = OpString") != std::string::npos); + EXPECT_TRUE(found) + << "Was expecting the DebugBuildIdentifier string to have been kept."; + found = (after.find("%" + flags_id + " = OpConstant") != std::string::npos); + EXPECT_TRUE(found) + << "Was expecting the DebugBuildIdentifier constant to have been kept."; +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h index e520821..a578cc4 100644 --- a/test/opt/pass_fixture.h +++ b/test/opt/pass_fixture.h
@@ -35,6 +35,26 @@ namespace spvtools { namespace opt { +inline std::ostream& operator<<(std::ostream& os, + const effcee::Result::Status ers) { + switch (ers) { + case effcee::Result::Status::Ok: + return os << "effcee::Result::Status::Ok"; + case effcee::Result::Status::Fail: + return os << "effcee::Result::Status::Fail"; + case effcee::Result::Status::BadOption: + return os << "effcee::Result::Status::BadOption"; + case effcee::Result::Status::NoRules: + return os << "effcee::Result::Status::NoRules"; + case effcee::Result::Status::BadRule: + return os << "effcee::Result::Status::BadRule"; + default: + break; + } + return os << "(invalid effcee::Result::Status " << static_cast<unsigned>(ers) + << ")"; +} + // Template class for testing passes. It contains some handy utility methods for // running passes and checking results. // @@ -102,23 +122,24 @@ auto status = Pass::Status::SuccessWithoutChange; std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>( assembly, skip_nop, std::forward<Args>(args)...); - if (do_validation) { - spv_context spvContext = spvContextCreate(env_); - spv_diagnostic diagnostic = nullptr; - spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()}; - spv_result_t error = spvValidateWithOptions( - spvContext, ValidatorOptions(), &binary, &diagnostic); - EXPECT_EQ(error, 0); - if (error != 0) spvDiagnosticPrint(diagnostic); - spvDiagnosticDestroy(diagnostic); - spvContextDestroy(spvContext); - } std::string optimized_asm; SpirvTools tools(env_); EXPECT_TRUE( tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_)) << "Disassembling failed for shader:\n" << assembly << std::endl; + if (do_validation) { + spv_context spvContext = spvContextCreate(env_); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()}; + spv_result_t error = spvValidateWithOptions( + spvContext, ValidatorOptions(), &binary, &diagnostic); + EXPECT_EQ(error, 0) << "validation failed for optimized asm:\n" + << optimized_asm; + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + } return std::make_tuple(optimized_asm, status); } @@ -199,14 +220,14 @@ // messages. template <typename PassT, typename... Args> void SinglePassRunAndFail(const std::string& original, Args&&... args) { - context_ = BuildModule(env_, consumer_, original, assemble_options_); - EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n" - << original << std::endl; std::ostringstream errs; auto error_consumer = [&errs](spv_message_level_t, const char*, const spv_position_t&, const char* message) { errs << message << std::endl; }; + context_ = BuildModule(env_, error_consumer, original, assemble_options_); + EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n" + << original << std::endl; auto pass = MakeUnique<PassT>(std::forward<Args>(args)...); pass->SetMessageConsumer(error_consumer); const auto status = pass->Run(context()); @@ -257,6 +278,23 @@ } } + // Returns the disassembly of the current module. This is useful for + // debugging. + std::unique_ptr<opt::IRContext> AssembleModule(const std::string& text) { + return spvtools::BuildModule(env_, consumer_, text, assemble_options_); + } + + // Returns the disassembly of the current module. This is useful for + // debugging. + std::string Disassemble(opt::Module* m) { + std::vector<uint32_t> binary; + m->ToBinary(&binary, /* skip_nop = */ false); + std::string disassembly; + SpirvTools tools(env_); + tools.Disassemble(binary, &disassembly, disassemble_options_); + return disassembly; + } + void SetAssembleOptions(uint32_t assemble_options) { assemble_options_ = assemble_options; }
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp index 494f2e9..c005d74 100644 --- a/test/opt/pass_merge_return_test.cpp +++ b/test/opt/pass_merge_return_test.cpp
@@ -2598,6 +2598,70 @@ EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); } +TEST_F(MergeReturnPassTest, DebugFunctionDefinitionStillInEntryBlock) { + // Make sure that the DebugFunctionDefinition instruction is still in the + // entry block + const std::string text = + R"( +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK: DebugFunctionDefinition +; CHECK: OpSelectionMerge +; CHECK: OpCompositeExtract +; CHECK: OpUGreaterThan + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %entryPointParam_main %gl_GlobalInvocationID + %1 = OpString "test" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %entryPointParam_main Location 0 + %void = OpTypeVoid + %4 = OpExtInst %void %2 DebugSource %1 + %uint = OpTypeInt 32 0 + %uint_100 = OpConstant %uint 100 + %uint_5 = OpConstant %uint 5 + %uint_11 = OpConstant %uint 11 + %10 = OpExtInst %void %2 DebugCompilationUnit %uint_100 %uint_5 %4 %uint_11 + %12 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %14 = OpExtInst %void %2 DebugTypeFunction %uint_0 %void + %uint_2 = OpConstant %uint 2 + %16 = OpExtInst %void %2 DebugFunction %1 %14 %4 %uint_2 %uint_5 %10 %1 %uint_0 %uint_2 + %v3uint = OpTypeVector %uint 3 + %_ptr_Input_v3uint = OpTypePointer Input %v3uint + %bool = OpTypeBool + %uint_3 = OpConstant %uint 3 + %int = OpTypeInt 32 1 + %_ptr_Output_int = OpTypePointer Output %int + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input ; BuiltIn GlobalInvocationId + %entryPointParam_main = OpVariable %_ptr_Output_int Output ; Location 0 + + ; Function main + %main = OpFunction %void None %12 + %13 = OpLabel + %20 = OpExtInst %void %2 DebugScope %16 + %29 = OpLoad %v3uint %gl_GlobalInvocationID + %37 = OpCompositeExtract %uint %29 0 + %39 = OpUGreaterThan %bool %37 %uint_3 + %19 = OpExtInst %void %2 DebugFunctionDefinition %16 %main + OpSelectionMerge %21 None + OpBranchConditional %39 %23 %21 + %21 = OpLabel + OpStore %entryPointParam_main %int_1 + OpReturn + %23 = OpLabel + OpStore %entryPointParam_main %int_0 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<MergeReturnPass>(text, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/pass_remove_duplicates_test.cpp b/test/opt/pass_remove_duplicates_test.cpp index 131a6b4..9cf420f 100644 --- a/test/opt/pass_remove_duplicates_test.cpp +++ b/test/opt/pass_remove_duplicates_test.cpp
@@ -639,6 +639,131 @@ EXPECT_EQ(GetErrorMessage(), ""); } +TEST_F(RemoveDuplicatesTest, DuplicateExtensions) { + const std::string spirv = R"( +OpExtension "SPV_INTEL_function_variants" +OpExtension "SPV_INTEL_function_variants" +)"; + const std::string after = R"(OpExtension "SPV_INTEL_function_variants" +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, DuplicateConditionalExtensions) { + const std::string spirv = R"( +OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +)"; + const std::string after = + R"(OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, MixedConditionalExtensions) { + const std::string spirv = R"( +OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly" +OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly" +OpExtension "SPV_INTEL_function_variants" +)"; + const std::string after = + R"(OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly" +OpExtension "SPV_INTEL_function_variants" +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, MixedConditionalExtensionsReordered) { + const std::string spirv = R"( +OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly" +OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly" +OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +OpExtension "SPV_INTEL_function_variants" +%3 = OpTypeBool +%1 = OpSpecConstantTrue %3 +%2 = OpSpecConstantFalse %3 +)"; + const std::string after = + R"(OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants" +OpConditionalExtensionINTEL %1 "SPV_INTEL_inline_assembly" +OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants" +OpExtension "SPV_INTEL_function_variants" +%3 = OpTypeBool +%2 = OpSpecConstantTrue %3 +%1 = OpSpecConstantFalse %3 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, DuplicateConditionalCapabilities) { + const std::string spirv = R"( +OpConditionalCapabilityINTEL %1 Kernel +OpConditionalCapabilityINTEL %1 Kernel +)"; + const std::string after = R"(OpConditionalCapabilityINTEL %1 Kernel +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, MixedConditionalCapabilities) { + const std::string spirv = R"( +OpConditionalCapabilityINTEL %1 Kernel +OpConditionalCapabilityINTEL %2 Kernel +OpConditionalCapabilityINTEL %2 Linkage +OpConditionalCapabilityINTEL %2 Linkage +OpCapability Kernel +)"; + const std::string after = + R"(OpConditionalCapabilityINTEL %1 Kernel +OpConditionalCapabilityINTEL %2 Kernel +OpConditionalCapabilityINTEL %2 Linkage +OpCapability Kernel +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, MixedConditionalCapabilitiesReordered) { + const std::string spirv = R"( +OpConditionalCapabilityINTEL %2 Kernel +OpConditionalCapabilityINTEL %2 Linkage +OpConditionalCapabilityINTEL %2 Linkage +OpConditionalCapabilityINTEL %1 Kernel +OpCapability Kernel +%3 = OpTypeBool +%1 = OpSpecConstantTrue %3 +%2 = OpSpecConstantFalse %3 +)"; + const std::string after = + R"(OpConditionalCapabilityINTEL %1 Kernel +OpConditionalCapabilityINTEL %1 Linkage +OpConditionalCapabilityINTEL %2 Kernel +OpCapability Kernel +%3 = OpTypeBool +%2 = OpSpecConstantTrue %3 +%1 = OpSpecConstantFalse %3 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/pass_utils.h b/test/opt/pass_utils.h index 8968f8a..ddf3b9f 100644 --- a/test/opt/pass_utils.h +++ b/test/opt/pass_utils.h
@@ -17,6 +17,7 @@ #include <algorithm> #include <functional> +#include <iostream> #include <iterator> #include <string> #include <vector> @@ -24,10 +25,25 @@ #include "gtest/gtest.h" #include "include/spirv-tools/libspirv.h" #include "include/spirv-tools/libspirv.hpp" +#include "source/opt/pass.h" namespace spvtools { namespace opt { +inline std::ostream& operator<<(std::ostream& os, const Pass::Status ps) { + switch (ps) { + case Pass::Status::Failure: + return os << "Pass::Status::Failure"; + case Pass::Status::SuccessWithChange: + return os << "Pass::Status::SuccessWithChange"; + case Pass::Status::SuccessWithoutChange: + return os << "Pass::Status::SuccessWithoutChange"; + default: + break; + } + return os << "(invalid Pass::Status " << static_cast<unsigned>(ps) << ")"; +} + struct Message { spv_message_level_t level; const char* source_file;
diff --git a/test/opt/private_to_local_test.cpp b/test/opt/private_to_local_test.cpp index f7c37c9..6314d49 100644 --- a/test/opt/private_to_local_test.cpp +++ b/test/opt/private_to_local_test.cpp
@@ -173,6 +173,43 @@ SinglePassRunAndMatch<PrivateToLocalPass>(text, false); } +TEST_F(PrivateToLocalTest, IgnorePointerToPrivateVariable) { + // Should not change because the pointer to the private variable + // has been stored inside a function variable. + const std::string text = R"( + OpCapability Shader + OpCapability VariablePointersStorageBuffer + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %load_thread_local "load_thread_local" %v + OpExecutionMode %load_thread_local LocalSize 8 8 1 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %void = OpTypeVoid +%function_type__1561_ = OpTypeFunction %void +%_ptr_Private_uint = OpTypePointer Private %uint +%_ptr_Function__ptr_Private_uint = OpTypePointer Function %_ptr_Private_uint +%_ptr_Function_uint = OpTypePointer Function %uint + %v = OpVariable %_ptr_Private_uint Private %uint_0 +%load_thread_local = OpFunction %void None %function_type__1561_ + %37 = OpLabel + %31 = OpVariable %_ptr_Function__ptr_Private_uint Function + %32 = OpVariable %_ptr_Function_uint Function + %33 = OpVariable %_ptr_Function_uint Function + OpStore %31 %v + %35 = OpLoad %uint %v + OpStore %32 %35 + %36 = OpLoad %uint %v + OpStore %33 %36 + OpReturn + OpFunctionEnd + )"; + auto result = SinglePassRunAndDisassemble<PrivateToLocalPass>( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + TEST_F(PrivateToLocalTest, UsedInTwoFunctions) { // Should not change because it is used in multiple functions. const std::string text = R"(
diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp index eb78497..6af1107 100644 --- a/test/opt/redundancy_elimination_test.cpp +++ b/test/opt/redundancy_elimination_test.cpp
@@ -84,6 +84,34 @@ SinglePassRunAndMatch<RedundancyEliminationPass>(text, false); } +// Remove a redundant add with flipped arguments. +TEST_F(RedundancyEliminationTest, RemoveRedundantAddFlipped) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFMul %5 %9 %9 + %11 = OpFAdd %5 %9 %10 +; CHECK: OpFAdd +; CHECK-NEXT: OpReturn + %12 = OpFAdd %5 %10 %9 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<RedundancyEliminationPass>(text, false); +} + // Remove a redundant add going through a multiple basic blocks. TEST_F(RedundancyEliminationTest, RemoveRedundantAddDiamond) { const std::string text = R"( @@ -359,6 +387,93 @@ SinglePassRunAndCheck<RedundancyEliminationPass>(text, text, false); } +TEST_F(RedundancyEliminationTest, PreserveLoadYieldingImage) { + // This is more strict than needed. + // This is sufficient to ensure that the load of an image + // occurs in the same basic block as its use. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %load_ty "load_ty" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %load_ty = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%ptr_load_ty = OpTypePointer UniformConstant %load_ty + %var = OpVariable %ptr_load_ty UniformConstant + %main = OpFunction %void None %6 + ; CHECK: OpLoad %load_ty + ; CHECK: OpLoad %load_ty + %15 = OpLabel + %16 = OpLoad %load_ty %var + %17 = OpLoad %load_ty %var + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<RedundancyEliminationPass>(text, false); +} + +TEST_F(RedundancyEliminationTest, PreserveLoadYieldingSampler) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %load_ty "load_ty" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %load_ty = OpTypeSampler +%ptr_load_ty = OpTypePointer UniformConstant %load_ty + %var = OpVariable %ptr_load_ty UniformConstant + %main = OpFunction %void None %6 + ; CHECK: OpLoad %load_ty + ; CHECK: OpLoad %load_ty + %15 = OpLabel + %16 = OpLoad %load_ty %var + %17 = OpLoad %load_ty %var + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<RedundancyEliminationPass>(text, false); +} + +TEST_F(RedundancyEliminationTest, PreserveLoadYieldingSampledImage) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %load_ty "load_ty" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %i_ty = OpTypeImage %float 2D 0 0 0 2 Rgba32f + %load_ty = OpTypeSampledImage %i_ty +%ptr_load_ty = OpTypePointer UniformConstant %load_ty + %var = OpVariable %ptr_load_ty UniformConstant + %main = OpFunction %void None %6 + ; CHECK: OpLoad %load_ty + ; CHECK: OpLoad %load_ty + %15 = OpLabel + %16 = OpLoad %load_ty %var + %17 = OpLoad %load_ty %var + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch<RedundancyEliminationPass>(text, false); +} + } // namespace } // namespace opt -} // namespace spvtools \ No newline at end of file +} // namespace spvtools
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp index 8bb40f7..bb273e5 100644 --- a/test/opt/remove_unused_interface_variables_test.cpp +++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -179,6 +179,50 @@ true, true); } +TEST_F(RemoveUnusedInterfaceVariablesTest, + DontEliminateUntypedVariableInterfaceVulkan12) { + const std::string spirv = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpCapability Int64 + OpCapability DescriptorHeapEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" %2 +; CHECK: OpEntryPoint Fragment %1 "main" %2 + OpExecutionMode %1 OriginUpperLeft + OpDecorate %2 BuiltIn ResourceHeapEXT + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %type_untyped_pointer = OpTypeUntypedPointerKHR UniformConstant + %void = OpTypeVoid + %10 = OpTypeFunction %void + %type_buffer_image = OpTypeImage %float Buffer 2 0 0 1 Rgba32f + %type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f + %type_buffer_ext = OpTypeBufferEXT StorageBuffer +%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext + %2 = OpUntypedVariableKHR %type_untyped_pointer UniformConstant + %1 = OpFunction %void None %10 + %3 = OpLabel + %20 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %2 %uint_0 + %24 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %2 %uint_1 + %26 = OpLoad %type_buffer_image %20 + %28 = OpImageFetch %v4float %26 %uint_0 None + %29 = OpLoad %type_buffer_image_0 %24 + OpImageWrite %29 %uint_0 %28 None + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch<RemoveUnusedInterfaceVariablesPass>(spirv, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/resolve_binding_conflicts_pass_test.cpp b/test/opt/resolve_binding_conflicts_pass_test.cpp new file mode 100644 index 0000000..31378b9 --- /dev/null +++ b/test/opt/resolve_binding_conflicts_pass_test.cpp
@@ -0,0 +1,864 @@ +// Copyright (c) 2025 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 <iostream> +#include <ostream> + +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +struct ResolveBindingConflictsTest : public PassTest<::testing::Test> { + virtual void SetUp() override { + SetTargetEnv(SPV_ENV_VULKAN_1_1); // allow storage buffer storage class + // without extension. + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } +}; + +using StringList = std::vector<std::string>; +std::string EntryPointDecls(const StringList& names) { + std::ostringstream os; + for (auto& name : names) { + os << " OpEntryPoint GLCompute %" + name + " \"" + name + + "\"\n"; + } + for (auto& name : names) { + os << " OpExecutionMode %" + name + " LocalSize 1 1 1\n"; + } + for (auto& name : names) { + os << " OpName %" + name + " \"" + name + "\"\n"; + } + return os.str(); +} + +std::string Preamble(const StringList& names = {"main"}) { + return R"( OpCapability Shader + OpMemoryModel Logical GLSL450 +)" + EntryPointDecls(names) + + R"( OpName %voidfn "voidfn" + OpName %s_ty "s_ty" + OpName %i_ty "i_ty" + OpName %si_ty "si_ty" + OpName %p_s_ty "p_s_ty" + OpName %p_i_ty "p_i_ty" + OpName %p_si_ty "p_si_ty" + OpName %st_ty "st_ty" + OpName %pu_st_ty "pu_st_ty" + OpName %pb_st_ty "pb_st_ty" +)"; +} + +std::string BasicTypes() { + return R"( OpDecorate %st_ty Block + OpMemberDecorate %st_ty 0 Offset 0 + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %s_ty = OpTypeSampler + %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %si_ty = OpTypeSampledImage %i_ty + %p_i_ty = OpTypePointer UniformConstant %i_ty + %p_s_ty = OpTypePointer UniformConstant %s_ty + %p_si_ty = OpTypePointer UniformConstant %si_ty + %st_ty = OpTypeStruct %uint + %pu_st_ty = OpTypePointer Uniform %st_ty + %pb_st_ty = OpTypePointer StorageBuffer %st_ty +)"; +} +std::string NoCheck() { return "; CHECK-NOT: nothing to see"; } + +TEST_F(ResolveBindingConflictsTest, NoBindings_NoChange) { + const std::string kTest = Preamble() + BasicTypes() + + R"( %main = OpFunction %void None %voidfn + %100 = OpLabel + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest + NoCheck(), /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange); + EXPECT_EQ(kTest, disasm); +} + +TEST_F(ResolveBindingConflictsTest, NoConflict_UnusedVars_NoChange) { + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 0 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 0 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 0 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 0 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 0 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 0 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 0 + +)" + BasicTypes() + R"( + + ; Unused variables + + %100 = OpVariable %p_i_ty UniformConstant ; image + %101 = OpVariable %p_s_ty UniformConstant ; sampler + %102 = OpVariable %pu_st_ty Uniform ; UBO + %103 = OpVariable %pb_st_ty StorageBuffer ; SSBO + %104 = OpVariable %p_si_ty UniformConstant ; combined sampled image + + %main = OpFunction %void None %voidfn + %10 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, NoConflict_UsedVars_NoChange) { + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 1 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 2 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %110 DescriptorSet 1 + OpDecorate %110 Binding 0 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 0 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 1 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + ; CHECK: OpDecorate %110 DescriptorSet 1 + ; CHECK: OpDecorate %110 Binding 0 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_i_ty UniformConstant ; image + %101 = OpVariable %p_s_ty UniformConstant ; sampler + %102 = OpVariable %pu_st_ty Uniform ; UBO + %103 = OpVariable %pb_st_ty StorageBuffer ; SSBO + %110 = OpVariable %p_si_ty UniformConstant ; combined sampled image + + %main = OpFunction %void None %voidfn + %10 = OpLabel + %11 = OpCopyObject %p_i_ty %100 + %12 = OpCopyObject %p_s_ty %101 + %13 = OpCopyObject %pu_st_ty %102 + %14 = OpCopyObject %pb_st_ty %103 + %15 = OpCopyObject %p_si_ty %110 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + OneEntryPoint_SamplerFirstConflict_Resolves) { + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + + ; The sampler's binding number is incremented, even when listed first. + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; sampler listed first + %101 = OpVariable %p_i_ty UniformConstant + + %main = OpFunction %void None %voidfn + %10 = OpLabel + %11 = OpCopyObject %p_s_ty %100 + %12 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + OneEntryPoint_SamplerSecondConflict_Resolves) { + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + + ; The sampler's binding number is incremented, even when listed second. + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 0 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 1 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_i_ty UniformConstant + %101 = OpVariable %p_s_ty UniformConstant ; sampler listed second + + %main = OpFunction %void None %voidfn + %10 = OpLabel + %11 = OpCopyObject %p_i_ty %100 + %12 = OpCopyObject %p_s_ty %101 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, OneEntryPoint_Conflict_Ripples) { + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 2 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 3 + + ; The sampler's binding number is incremented, and later + ; bindings move out of the way. + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 4 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; sampler comes first + %101 = OpVariable %p_i_ty UniformConstant + %102 = OpVariable %pu_st_ty Uniform + %103 = OpVariable %pb_st_ty StorageBuffer + %104 = OpVariable %p_si_ty UniformConstant + + %main = OpFunction %void None %voidfn + %10 = OpLabel + %11 = OpCopyObject %p_s_ty %100 + %12 = OpCopyObject %p_i_ty %101 + %13 = OpCopyObject %pu_st_ty %102 + %14 = OpCopyObject %pb_st_ty %103 + %15 = OpCopyObject %p_si_ty %104 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + OneEntryPoint_Conflict_RippleStopsAtFirstHole) { + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + ; Leave a hole at (0, 2) + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 4 + + ; There was a hole at binding 2. The ripple stops there. + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 4 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; sampler comes first + %101 = OpVariable %p_i_ty UniformConstant + %102 = OpVariable %pu_st_ty Uniform + %103 = OpVariable %pb_st_ty StorageBuffer + %104 = OpVariable %p_si_ty UniformConstant + + %main = OpFunction %void None %voidfn + %10 = OpLabel + %11 = OpCopyObject %p_s_ty %100 + %12 = OpCopyObject %p_i_ty %101 + %13 = OpCopyObject %pu_st_ty %102 + %14 = OpCopyObject %pb_st_ty %103 + %15 = OpCopyObject %p_si_ty %104 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, OneEntryPoint_MultiConflict_Resolves) { + const std::string kTest = Preamble() + + R"( + ; Two conflicts: at Bindings 0, and 1 + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 1 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 4 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; sampler first + %101 = OpVariable %p_i_ty UniformConstant + %102 = OpVariable %p_i_ty UniformConstant + %103 = OpVariable %p_s_ty UniformConstant ; sampler second + %104 = OpVariable %pu_st_ty Uniform + + %main = OpFunction %void None %voidfn + %10 = OpLabel + %11 = OpCopyObject %p_s_ty %100 + %12 = OpCopyObject %p_i_ty %101 + %13 = OpCopyObject %p_i_ty %102 + %14 = OpCopyObject %p_s_ty %103 + %15 = OpCopyObject %pu_st_ty %104 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + OneEntryPoint_MultiConflict_ComplexCallGraph_Resolves) { + // Check that uses are seen even when used at various points in a complex call + // graph. + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 1 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 4 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; used in %200 + %101 = OpVariable %p_i_ty UniformConstant ; used in %300, %400 + %102 = OpVariable %p_i_ty UniformConstant ; used in %500 + %103 = OpVariable %p_s_ty UniformConstant ; used in %400 twice + %104 = OpVariable %pu_st_ty Uniform ; used in %600 + + %200 = OpFunction %void None %voidfn + %201 = OpLabel + %202 = OpCopyObject %p_s_ty %100 + OpReturn + OpFunctionEnd + + %300 = OpFunction %void None %voidfn + %301 = OpLabel + %302 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd + + %400 = OpFunction %void None %voidfn + %401 = OpLabel + %402 = OpFunctionCall %void %200 + %403 = OpCopyObject %p_s_ty %103 + %404 = OpCopyObject %p_i_ty %101 + %405 = OpCopyObject %p_s_ty %103 + %406 = OpFunctionCall %void %300 + OpReturn + OpFunctionEnd + + %500 = OpFunction %void None %voidfn + %501 = OpLabel + %502 = OpFunctionCall %void %400 + %503 = OpCopyObject %p_i_ty %102 + %504 = OpFunctionCall %void %300 + OpReturn + OpFunctionEnd + + %600 = OpFunction %void None %voidfn + %601 = OpLabel + %602 = OpFunctionCall %void %300 + %603 = OpFunctionCall %void %500 + %604 = OpCopyObject %pu_st_ty %104 + OpReturn + OpFunctionEnd + + %main = OpFunction %void None %voidfn + %1000 = OpLabel + %1001 = OpFunctionCall %void %600 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + MultiEntryPoint_DuplicatConflicts_ResolvesOnlyOnce) { + // Before: + // + // Binding: 0 1 + // Alpha: %100,%101 + // Beta: %100,%101 + // + // After: + // + // Binding: 0 1 + // Alpha: %101 %100 + // Beta: %101 %100 + const std::string kTest = Preamble({"alpha", "beta"}) + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant + %101 = OpVariable %p_i_ty UniformConstant + + %alpha = OpFunction %void None %voidfn + %1000 = OpLabel + %1001 = OpCopyObject %p_s_ty %100 + %1002 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd + + %beta = OpFunction %void None %voidfn + %2000 = OpLabel + %2001 = OpCopyObject %p_s_ty %100 + %2002 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + MultiEntryPoint_IndependentConflicts_Resolves) { + // Before: + // + // Binding: 0 1 + // Alpha: %100,%101 + // Beta: %102,%103 + // + // After: + // + // Binding: 0 1 + // Alpha: %101 %100 + // Beta: %102 %103 + const std::string kTest = Preamble({"alpha", "beta"}) + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 0 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 0 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 0 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 1 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant + %101 = OpVariable %p_i_ty UniformConstant + %102 = OpVariable %p_i_ty UniformConstant + %103 = OpVariable %p_s_ty UniformConstant + + %alpha = OpFunction %void None %voidfn + %1000 = OpLabel + %1001 = OpCopyObject %p_s_ty %100 + %1002 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd + + %beta = OpFunction %void None %voidfn + %2000 = OpLabel + %2001 = OpCopyObject %p_i_ty %102 + %2002 = OpCopyObject %p_s_ty %103 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + MultiEntryPoint_SameVarConflictsAcrossMultiEntryPoints_Resolves) { + // A sampler variable is bumped, causing potential conflicts in other shaders. + // + // Before: + // + // Binding: 0 1 2 + // Alpha: %100,%101 + // Beta: %100 %102 + // Gamma: %100 %103 + // + // After: + // + // Binding: 0 1 2 + // Alpha: %101 %100 + // Beta: %100 %102 + // Gamma: %100 %103 + // + const std::string kTest = Preamble({"alpha", "beta", "gamma"}) + + R"( + OpDecorate %100 DescriptorSet 0 ; The sampler + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 2 + + ; bumped once + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + + ; pushed back from bump of %100 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + + ; does not need to be bumped + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 2 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; used in alpha, beta, gamma + %101 = OpVariable %p_i_ty UniformConstant ; used in alpha + %102 = OpVariable %pu_st_ty Uniform ; used in beta + %103 = OpVariable %pb_st_ty StorageBuffer ; used in gamma + + %alpha = OpFunction %void None %voidfn + %1000 = OpLabel + %1001 = OpCopyObject %p_s_ty %100 + %1002 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd + + %beta = OpFunction %void None %voidfn + %2000 = OpLabel + %2001 = OpCopyObject %p_s_ty %100 + %2002 = OpCopyObject %pu_st_ty %102 + OpReturn + OpFunctionEnd + + %gamma = OpFunction %void None %voidfn + %3000 = OpLabel + %3001 = OpCopyObject %p_s_ty %100 + %3002 = OpCopyObject %pb_st_ty %103 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_ConflictCascade_Resolves) { + // Before: + // + // Binding: 0 1 2 3 + // Alpha: %100,%101 + // Beta: %100 %102 + // Gamma: %102 %103 + // Delta: %103 %104 + // + // After: + // + // Binding: 0 1 2 3 4 + // Alpha: %101 %100 + // Beta: %100 %102 + // Gamma: %102 %103 + // Delta: %103 %104 + // + const std::string kTest = Preamble({"alpha", "beta", "gamma", "delta"}) + + R"( + OpDecorate %100 DescriptorSet 0 ; The sampler + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 2 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 3 + + ; %100 is bumped once: + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 1 + + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 0 + + ; pushed back from bump of %100 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + + ; pushed back from bump of %102 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + + ; pushed back from bump of %103 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 4 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_s_ty UniformConstant ; used in alpha, beta + %101 = OpVariable %p_i_ty UniformConstant ; used in alpha + %102 = OpVariable %pu_st_ty Uniform ; used in beta, gamma + %103 = OpVariable %pb_st_ty StorageBuffer ; used in gamma, delta + %104 = OpVariable %p_si_ty UniformConstant ; used delta + + %alpha = OpFunction %void None %voidfn + %1000 = OpLabel + %1001 = OpCopyObject %p_s_ty %100 + %1002 = OpCopyObject %p_i_ty %101 + OpReturn + OpFunctionEnd + + %beta = OpFunction %void None %voidfn + %2000 = OpLabel + %2001 = OpCopyObject %p_s_ty %100 + %2002 = OpCopyObject %pu_st_ty %102 + OpReturn + OpFunctionEnd + + %gamma = OpFunction %void None %voidfn + %3000 = OpLabel + %3001 = OpCopyObject %pu_st_ty %102 + %3002 = OpCopyObject %pb_st_ty %103 + OpReturn + OpFunctionEnd + + %delta = OpFunction %void None %voidfn + %4000 = OpLabel + %4001 = OpCopyObject %pb_st_ty %103 + %4002 = OpCopyObject %p_si_ty %104 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(ResolveBindingConflictsTest, + MultiEntryPoint_ConflictCascade_RevisitEntryPoint) { + // Prove that the settling algorithm knows to revisit entry points that + // already had all their own conflicts resolved. + // + // Before: + // + // Binding: 0 1 2 3 4 + // Alpha: %100,%101 %103 %104 %105 + // Beta: %101 %102 %103 %105 + // + // After: + // + // Binding: 0 1 2 3 4 5 + // Alpha: %100 %101 %103 %104 %105 + // Beta: %101 %102 %103 %105 + const std::string kTest = Preamble({"alpha", "beta"}) + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %101 DescriptorSet 0 ; the sampler + OpDecorate %101 Binding 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 2 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 3 + OpDecorate %105 DescriptorSet 0 + OpDecorate %105 Binding 4 + + ; CHECK: OpDecorate %100 DescriptorSet 0 + ; CHECK: OpDecorate %100 Binding 0 + ; CHECK: OpDecorate %101 DescriptorSet 0 + ; CHECK: OpDecorate %101 Binding 1 + ; CHECK: OpDecorate %102 DescriptorSet 0 + ; CHECK: OpDecorate %102 Binding 2 + ; CHECK: OpDecorate %103 DescriptorSet 0 + ; CHECK: OpDecorate %103 Binding 3 + ; CHECK: OpDecorate %104 DescriptorSet 0 + ; CHECK: OpDecorate %104 Binding 4 + ; CHECK: OpDecorate %105 DescriptorSet 0 + ; CHECK: OpDecorate %105 Binding 5 + +)" + BasicTypes() + R"( + + %100 = OpVariable %p_i_ty UniformConstant + %101 = OpVariable %p_s_ty UniformConstant + %102 = OpVariable %pu_st_ty Uniform + %103 = OpVariable %pb_st_ty StorageBuffer + %104 = OpVariable %p_si_ty UniformConstant + %105 = OpVariable %p_s_ty UniformConstant + + %alpha = OpFunction %void None %voidfn + %1000 = OpLabel + %1001 = OpCopyObject %p_i_ty %100 + %1002 = OpCopyObject %p_s_ty %101 + %1003 = OpCopyObject %pb_st_ty %103 + %1004 = OpCopyObject %p_si_ty %104 + %1005 = OpCopyObject %p_s_ty %105 + OpReturn + OpFunctionEnd + + %beta = OpFunction %void None %voidfn + %2000 = OpLabel + %2001 = OpCopyObject %p_s_ty %101 + %2002 = OpCopyObject %pu_st_ty %102 + %2003 = OpCopyObject %pb_st_ty %103 + %2004 = OpCopyObject %p_s_ty %105 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp index 0ba285b..0256ac2 100644 --- a/test/opt/scalar_replacement_test.cpp +++ b/test/opt/scalar_replacement_test.cpp
@@ -27,7 +27,7 @@ TEST_F(ScalarReplacementPassName, Default) { auto srp = ScalarReplacementPass(); - EXPECT_STREQ(srp.name(), "scalar-replacement=100"); + EXPECT_STREQ(srp.name(), "scalar-replacement=0"); } TEST_F(ScalarReplacementPassName, Large) { @@ -1863,6 +1863,7 @@ OpFunctionEnd )"; + ValidatorOptions()->relax_logical_pointer = true; auto result = SinglePassRunAndDisassemble<ScalarReplacementPass>(text, true, true, 0); EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp index 10f805b..ae0ddf0 100644 --- a/test/opt/set_spec_const_default_value_test.cpp +++ b/test/opt/set_spec_const_default_value_test.cpp
@@ -70,7 +70,7 @@ {"100:1024 \n \r\t \t \v \f ", true, SpecIdToValueStrMap{{100, "1024"}}}, // 6. maximum spec id - {"4294967295:0", true, SpecIdToValueStrMap{{4294967295, "0"}}}, + {"4294967295:0", true, SpecIdToValueStrMap{{4294967295u, "0"}}}, // 7. minimum spec id {"0:100", true, SpecIdToValueStrMap{{0, "100"}}}, // 8. random content without spaces are allowed @@ -1027,6 +1027,114 @@ "%3 = OpSpecConstant %uchar 0\n" "%4 = OpSpecConstant %uchar 214\n", }, + // 23. ConstantData - no spec provided + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%uint = OpTypeInt 32 0\n" + "%2 = OpSpecConstant %uint 1\n" + "%_arr_uint_2 = OpTypeArray %uint %2\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_2 0\n", + // default values + SpecIdToValueBitPatternMap{{888, {0x01020304}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%uint = OpTypeInt 32 0\n" + "%2 = OpSpecConstant %uint 1\n" + "%_arr_uint_2 = OpTypeArray %uint %2\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_2 0\n", + }, + // 24. ConstantData - only data - uint32 + { + // code + "OpDecorate %1 SpecId 100\n" + "%uint = OpTypeInt 32 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 0\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x01020304}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%uint = OpTypeInt 32 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 16909060\n", + }, + // 25. ConstantData - only data - int8 + { + // code + "OpDecorate %1 SpecId 100\n" + "%char = OpTypeInt 8 1\n" + "%char_1 = OpConstant %char 1\n" + "%_arr_char_char_1 = OpTypeArray %char %char_1\n" + "%1 = OpSpecConstantDataKHR %_arr_char_char_1 0\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x01020304}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%char = OpTypeInt 8 1\n" + "%char_1 = OpConstant %char 1\n" + "%_arr_char_char_1 = OpTypeArray %char %char_1\n" + "%1 = OpSpecConstantDataKHR %_arr_char_char_1 16909060\n", + }, + // 26. ConstantData - only data - uint64 + { + // code + "OpDecorate %1 SpecId 100\n" + "%ulong = OpTypeInt 64 0\n" + "%ulong_1 = OpConstant %ulong 1\n" + "%_arr_ulong_ulong_1 = OpTypeArray %ulong %ulong_1\n" + "%1 = OpSpecConstantDataKHR %_arr_ulong_ulong_1 0 0\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x01020304, 0x1}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%ulong = OpTypeInt 64 0\n" + "%ulong_1 = OpConstant %ulong 1\n" + "%_arr_ulong_ulong_1 = OpTypeArray %ulong %ulong_1\n" + "%1 = OpSpecConstantDataKHR %_arr_ulong_ulong_1 16909060 1\n", + }, + // 27. ConstantData - expand length - uint32 + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%uint = OpTypeInt 32 0\n" + "%2 = OpSpecConstant %uint 1\n" + "%_arr_uint_2 = OpTypeArray %uint %2\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_2 0\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x1, 0x2, 0x3, 0x4}}, {101, {4}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%uint = OpTypeInt 32 0\n" + "%2 = OpSpecConstant %uint 4\n" + "%_arr_uint_2 = OpTypeArray %uint %2\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_2 1 2 3 4\n", + }, + // 28. ConstantData - shrink length - uint32 + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%uint = OpTypeInt 32 0\n" + "%2 = OpSpecConstant %uint 4\n" + "%_arr_uint_2 = OpTypeArray %uint %2\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_2 1 2 3 4\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x5, 0x6}}, {101, {2}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%uint = OpTypeInt 32 0\n" + "%2 = OpSpecConstant %uint 2\n" + "%_arr_uint_2 = OpTypeArray %uint %2\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_2 5 6\n", + }, })); INSTANTIATE_TEST_SUITE_P( @@ -1162,6 +1270,40 @@ "%2 = OpSpecConstant %ulong 200\n" "%3 = OpSpecConstant %double 3.141592653\n", }, + // 7. ConstantData - expand length + { + // code + "OpDecorate %1 SpecId 100\n" + "%uint = OpTypeInt 32 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 0\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x01020304, 0x0, 0x1, 0x2}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%uint = OpTypeInt 32 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 16909060 0 1 2\n", + }, + // 8. ConstantData - shrink length + { + // code + "OpDecorate %1 SpecId 100\n" + "%uint = OpTypeInt 32 0\n" + "%uint_4 = OpConstant %uint 4\n" + "%_arr_uint_uint_4 = OpTypeArray %uint %uint_4\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_uint_4 0 1 2 3\n", + // default values + SpecIdToValueBitPatternMap{{100, {4, 5}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%uint = OpTypeInt 32 0\n" + "%uint_4 = OpConstant %uint 4\n" + "%_arr_uint_uint_4 = OpTypeArray %uint %uint_4\n" + "%1 = OpSpecConstantDataKHR %_arr_uint_uint_4 4 5\n", + }, })); } // namespace
diff --git a/test/opt/split_combined_image_sampler_pass_test.cpp b/test/opt/split_combined_image_sampler_pass_test.cpp new file mode 100644 index 0000000..d6ad6bb --- /dev/null +++ b/test/opt/split_combined_image_sampler_pass_test.cpp
@@ -0,0 +1,1933 @@ +// Copyright (c) 2025 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 <array> +#include <iostream> +#include <ostream> + +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +struct SplitCombinedImageSamplerPassTest : public PassTest<::testing::Test> { + virtual void SetUp() override { + SetTargetEnv(SPV_ENV_VULKAN_1_0); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } +}; + +struct TypeCase { + const char* glsl_type; + const char* image_type_decl; +}; +std::ostream& operator<<(std::ostream& os, const TypeCase& tc) { + os << tc.glsl_type; + return os; +} + +struct SplitCombinedImageSamplerPassTypeCaseTest + : public PassTest<::testing::TestWithParam<TypeCase>> { + virtual void SetUp() override { + SetTargetEnv(SPV_ENV_VULKAN_1_0); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } +}; + +std::vector<TypeCase> ImageTypeCases() { + return std::vector<TypeCase>{ + {"sampler2D", "OpTypeImage %float 2D 0 0 0 1 Unknown"}, + {"sampler2DShadow", "OpTypeImage %float 2D 1 0 0 1 Unknown"}, + {"sampler2DArray", "OpTypeImage %float 2D 0 1 0 1 Unknown"}, + {"sampler2DArrayShadow", "OpTypeImage %float 2D 1 1 0 1 Unknown"}, + {"sampler2DMS", "OpTypeImage %float 2D 0 0 1 1 Unknown"}, + {"sampler2DMSArray", "OpTypeImage %float 2D 0 1 1 1 Unknown"}, + {"sampler3D", "OpTypeImage %float 3D 0 0 0 1 Unknown"}, + {"samplerCube", "OpTypeImage %float Cube 0 0 0 1 Unknown"}, + {"samplerCubeShadow", "OpTypeImage %float Cube 1 0 0 1 Unknown"}, + {"samplerCubeArray", "OpTypeImage %float Cube 0 1 0 1 Unknown"}, + {"samplerCubeArrayShadow", "OpTypeImage %float Cube 1 1 0 1 Unknown"}, + {"isampler2D", "OpTypeImage %int 2D 0 0 0 1 Unknown"}, + {"isampler2DShadow", "OpTypeImage %int 2D 1 0 0 1 Unknown"}, + {"isampler2DArray", "OpTypeImage %int 2D 0 1 0 1 Unknown"}, + {"isampler2DArrayShadow", "OpTypeImage %int 2D 1 1 0 1 Unknown"}, + {"isampler2DMS", "OpTypeImage %int 2D 0 0 1 1 Unknown"}, + {"isampler2DMSArray", "OpTypeImage %int 2D 0 1 1 1 Unknown"}, + {"isampler3D", "OpTypeImage %int 3D 0 0 0 1 Unknown"}, + {"isamplerCube", "OpTypeImage %int Cube 0 0 0 1 Unknown"}, + {"isamplerCubeShadow", "OpTypeImage %int Cube 1 0 0 1 Unknown"}, + {"isamplerCubeArray", "OpTypeImage %int Cube 0 1 0 1 Unknown"}, + {"isamplerCubeArrayShadow", "OpTypeImage %int Cube 1 1 0 1 Unknown"}, + {"usampler2D", "OpTypeImage %uint 2D 0 0 0 1 Unknown"}, + {"usampler2DShadow", "OpTypeImage %uint 2D 1 0 0 1 Unknown"}, + {"usampler2DArray", "OpTypeImage %uint 2D 0 1 0 1 Unknown"}, + {"usampler2DArrayShadow", "OpTypeImage %uint 2D 1 1 0 1 Unknown"}, + {"usampler2DMS", "OpTypeImage %uint 2D 0 0 1 1 Unknown"}, + {"usampler2DMSArray", "OpTypeImage %uint 2D 0 1 1 1 Unknown"}, + {"usampler3D", "OpTypeImage %uint 3D 0 0 0 1 Unknown"}, + {"usamplerCube", "OpTypeImage %uint Cube 0 0 0 1 Unknown"}, + {"usamplerCubeShadow", "OpTypeImage %uint Cube 1 0 0 1 Unknown"}, + {"usamplerCubeArray", "OpTypeImage %uint Cube 0 1 0 1 Unknown"}, + {"usamplerCubeArrayShadow", "OpTypeImage %uint Cube 1 1 0 1 Unknown"}, + }; +} + +std::string Preamble(const std::string shader_interface = "") { + return R"( OpCapability Shader + OpCapability RuntimeDescriptorArray + OpExtension "SPV_EXT_descriptor_indexing" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main")" + + shader_interface + R"( + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %main_0 "main_0" + OpName %voidfn "voidfn" +)"; +} + +std::string PreambleFragment(const std::string shader_interface = "") { + return R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main")" + + shader_interface + R"( + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %main_0 "main_0" + OpName %voidfn "voidfn" +)"; +} + +std::string BasicTypes() { + return R"( %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %float_0 = OpConstant %float 0 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %13 = OpConstantNull %v2float + %14 = OpConstantNull %v3float + %15 = OpConstantNull %v4float + %void = OpTypeVoid + %voidfn = OpTypeFunction %void +)"; +} +std::string Main() { + return R"( + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + OpReturn + OpFunctionEnd +)"; +} +std::string NoCheck() { return "; CHECK-NOT: nothing to see"; } + +TEST_F(SplitCombinedImageSamplerPassTest, SamplerOnly_NoChange) { + const std::string kTest = Preamble() + + R"( OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 +)" + BasicTypes() + R"( %10 = OpTypeSampler +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %100 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %6 = OpLoad %10 %100 + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest + NoCheck(), /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) + << "status" << kTest << "\n -> \n" + << disasm; + EXPECT_EQ(disasm, kTest) << "disasm"; +} + +TEST_F(SplitCombinedImageSamplerPassTest, ImageOnly_NoChange) { + const std::string kTest = Preamble() + + R"( OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 +)" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %100 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %6 = OpLoad %10 %100 + OpReturn + OpFunctionEnd +)"; + + SCOPED_TRACE("image only"); + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest + NoCheck(), /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange); + EXPECT_EQ(disasm, kTest); +} + +TEST_F(SplitCombinedImageSamplerPassTest, PtrSampledImageOnly_DeletesPtrType) { + const std::string kTest = Preamble() + BasicTypes() + R"( + ; CHECK: OpCapability Shader + ; CHECK-NOT: OpTypePointer UniformConstant + ; CHECK: OpFunction %void + %100 = OpTypeImage %float 2D 0 0 0 1 Unknown + %101 = OpTypeSampledImage %100 + %102 = OpTypePointer UniformConstant %101 + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest + NoCheck(), /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status"; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + PtrArraySampledImageOnly_DeletesPtrType) { + const std::string kTest = Preamble() + BasicTypes() + R"( + ; CHECK: OpCapability Shader + ; CHECK-NOT: OpTypePointer UniformConstant + ; CHECK: OpFunction %void + %100 = OpTypeImage %float 2D 0 0 0 1 Unknown + %101 = OpTypeSampledImage %100 + %103 = OpTypeArray %101 %uint_1 + %104 = OpTypePointer UniformConstant %103 + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest + NoCheck(), /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status"; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + PtrRtArraySampledImageOnly_DeletesPtrType) { + const std::string kTest = Preamble() + BasicTypes() + R"( + ; CHECK: OpCapability Shader + ; CHECK-NOT: OpTypePointer UniformConstant + ; CHECK: OpFunction %void + %100 = OpTypeImage %float 2D 0 0 0 1 Unknown + %101 = OpTypeSampledImage %100 + %103 = OpTypeRuntimeArray %101 + %104 = OpTypePointer UniformConstant %103 + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest + NoCheck(), /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status"; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + Combined_NoSampler_CreatedBeforeSampledImage) { + // No OpTypeSampler to begin with. + const std::string kTest = Preamble() + + R"( OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + + ; A sampler type is created and placed at the start of types. + ; CHECK: OpDecorate %{{\d+}} Binding 0 + ; CHECK: OpDecorate %{{\d+}} Binding 0 + ; CHECK-NOT: TypeSampledImage + ; CHECK: TypeSampler + ; CHECK: TypeSampledImage + +)" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + + %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %6 = OpLoad %11 %100 + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, Combined_SynthesizeVarNames) { + // Also tests binding info is copied to both variables. + const std::string kTest = Preamble() + + R"( + OpName %orig_var "orig_var" + OpDecorate %orig_var DescriptorSet 0 + OpDecorate %orig_var Binding 0 + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK: OpCapability + ; The original name is deleted + ; CHECK-NOT: OpName %orig_var " + ; CHECK: OpName %orig_var_image "orig_var_image" + ; CHECK: OpName %orig_var_sampler "orig_var_sampler" + ; CHECK-NOT: OpName %orig_var " + + ; CHECK: OpDecorate %orig_var_image DescriptorSet 0 + ; CHECK: OpDecorate %orig_var_sampler DescriptorSet 0 + ; CHECK: OpDecorate %orig_var_image Binding 0 + ; CHECK: OpDecorate %orig_var_sampler Binding 0 + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + + ; CHECK-NOT: %orig_var = OpVariable + ; CHECK-DAG: %orig_var_sampler = OpVariable %[[sampler_ptr_ty]] UniformConstant + ; CHECK-DAG: %orig_var_image = OpVariable %[[image_ptr_ty]] UniformConstant + ; CHECK: = OpFunction + +)" + BasicTypes() + R"( + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + + %orig_var = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %101 = OpLoad %11 %orig_var + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, Combined_RemapLoad) { + // Also tests binding info is copied to both variables. + const std::string kTest = Preamble() + + R"( + OpName %combined "combined" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + + ; CHECK: OpName + ; CHECK-NOT: OpDecorate %100 + ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[image_var]] Binding 0 + ; CHECK: OpDecorate %[[sampler_var]] Binding 0 + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK-NOT: %100 = OpVariable + ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant + ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant + ; CHECK: = OpFunction + + ; The load of the combined image+sampler is replaced by a two loads, then + ; a combination operation. + ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]] + ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]] + ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] + +)" + BasicTypes() + + " %10 = " + GetParam().image_type_decl + R"( + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + + %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %combined = OpLoad %11 %100 + + ; Uses of the combined image sampler are preserved. + ; CHECK: OpCopyObject %11 %combined + + %7 = OpCopyObject %11 %combined + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, + Combined_RemapLoad_RelaxedPrecisionOnVarCopied) { + // All decorations on the variable are copied. In this case, RelaxedPrecision + const std::string kTest = Preamble() + + R"( + OpName %combined "combined" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %100 RelaxedPrecision + + ; CHECK: OpName + ; CHECK-NOT: OpDecorate %100 + ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[image_var]] Binding 0 + ; CHECK: OpDecorate %[[sampler_var]] Binding 0 + ; CHECK: OpDecorate %[[image_var:\d+]] RelaxedPrecision + ; CHECK: OpDecorate %[[sampler_var:\d+]] RelaxedPrecision + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK-NOT: %100 = OpVariable + ; CHECK-NOT: OpVariable _ptr_UniformConstant_11 + ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant + ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant + ; CHECK: = OpFunction + + ; The load of the combined image+sampler is replaced by a two loads, then + ; a combination operation. + ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]] + ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]] + ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] + + %bool = OpTypeBool ; location marker +)" + BasicTypes() + + " %10 = " + GetParam().image_type_decl + R"( + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + + %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %combined = OpLoad %11 %100 + + ; Uses of the combined image sampler are preserved. + ; CHECK: OpCopyObject %11 %combined + + %7 = OpCopyObject %11 %combined + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, + Combined_RemapLoad_RelaxedPrecisionOnLoadCopied) { + // Copy decorations form an OpLoad that is replaced. + const std::string kTest = Preamble() + + R"( + OpName %combined "combined" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %combined RelaxedPrecision + + ; CHECK: OpName + ; CHECK-NOT: OpDecorate %100 + ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[image_var]] Binding 0 + ; CHECK: OpDecorate %[[sampler_var]] Binding 0 + + ; This is what we are checking in this test. + ; CHECK: OpDecorate %[[im:\d+]] RelaxedPrecision + ; CHECK: OpDecorate %[[s:\d+]] RelaxedPrecision + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK-NOT: %100 = OpVariable + ; CHECK-NOT: OpVariable _ptr_UniformConstant_11 + ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant + ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant + ; CHECK: = OpFunction + + ; The load of the combined image+sampler is replaced by a two loads, then + ; a combination operation. The new loads get the same decorations that the + ; original load had. + ; CHECK: %[[im]] = OpLoad %10 %[[image_var]] + ; CHECK: %[[s]] = OpLoad %[[sampler_ty]] %[[sampler_var]] + ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] + + %bool = OpTypeBool ; location marker +)" + BasicTypes() + + " %10 = " + GetParam().image_type_decl + R"( + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + + %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %combined = OpLoad %11 %100 + + ; Uses of the combined image sampler are preserved. + ; CHECK: OpCopyObject %11 %combined + + %7 = OpCopyObject %11 %combined + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, + Combined_DeletesCopyObjectOfPtr) { + // OpCopyObject is deleted, and its uses updated. + const std::string kTest = Preamble() + + R"( + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + + ; CHECK: OpName + ; CHECK-NOT: OpDecorate %100 + ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[image_var]] Binding 0 + ; CHECK: OpDecorate %[[sampler_var]] Binding 0 + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK-NOT: %100 = OpVariable + ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant + ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant + ; CHECK: = OpFunction + + +)" + BasicTypes() + + " %10 = " + GetParam().image_type_decl + R"( + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + + %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %101 = OpCopyObject %_ptr_UniformConstant_11 %100 + %102 = OpLoad %11 %101 + %103 = OpCopyObject %_ptr_UniformConstant_11 %101 + %104 = OpCopyObject %11 %102 ;; this copy survives + OpReturn + OpFunctionEnd + + ; The OpCopyObject instructions are removed. + ; The load of the combined image+sampler is replaced by a two loads, then + ; a combination operation. The only OpCopyObject that remains is the copy + ; of the copy of the sampled image value. + ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]] + ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]] + ; CHECK: %[[si:\d+]] = OpSampledImage %11 %[[im]] %[[s]] + ; CHECK-NEXT: OpCopyObject %11 %[[si]] + ; CHECK-NEXT: OpReturn +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, ArrayCombined_RemapLoad) { + const std::string kTest = Preamble() + + R"( + OpName %combined "combined" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + + ; CHECK: OpName + ; CHECK-NOT: OpDecorate %100 + ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[image_var]] Binding 0 + ; CHECK: OpDecorate %[[sampler_var]] Binding 0 + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK: %[[array_image_ty:\w+]] = OpTypeArray %10 %uint_3 + ; CHECK: %[[ptr_array_image_ty:\w+]] = OpTypePointer UniformConstant %[[array_image_ty]] + + ; CHECK: %[[array_sampler_ty:\w+]] = OpTypeArray %[[sampler_ty]] %uint_3 + ; CHECK: %[[ptr_array_sampler_ty:\w+]] = OpTypePointer UniformConstant %[[array_sampler_ty]] + + ; CHECK-NOT: %100 = OpVariable + ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[ptr_array_sampler_ty]] UniformConstant + ; CHECK-DAG: %[[image_var]] = OpVariable %[[ptr_array_image_ty]] UniformConstant + ; CHECK: = OpFunction + + ; The access chain and load is replaced by two access chains, two loads, then + ; a combine operation. + ; CHECK: %[[ptr_im:\d+]] = OpAccessChain %[[image_ptr_ty]] %[[image_var]] %uint_1 + ; CHECK: %[[ptr_s:\d+]] = OpAccessChain %[[sampler_ptr_ty]] %[[sampler_var]] %uint_1 + ; CHECK: %[[im:\d+]] = OpLoad %10 %[[ptr_im]] + ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[ptr_s]] + ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] + +)" + BasicTypes() + + " %10 = " + GetParam().image_type_decl + R"( + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %12 = OpTypeArray %11 %uint_3 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + + %100 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %ptr = OpAccessChain %_ptr_UniformConstant_11 %100 %uint_1 + %combined = OpLoad %11 %ptr + + ; Uses of the combined image sampler are preserved. + ; CHECK: OpCopyObject %11 %combined + + %7 = OpCopyObject %11 %combined + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, RtArrayCombined_RemapLoad) { + const std::string kTest = Preamble() + + R"( + OpName %combined "combined" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + + ; CHECK: OpName + ; CHECK-NOT: OpDecorate %100 + ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 + ; CHECK: OpDecorate %[[image_var]] Binding 0 + ; CHECK: OpDecorate %[[sampler_var]] Binding 0 + + ; CHECK: %10 = OpTypeImage % + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + + ; The combined image variable is replaced by an image variable and a sampler variable. + + ; CHECK: %[[array_image_ty:\w+]] = OpTypeRuntimeArray %10 + ; CHECK: %[[ptr_array_image_ty:\w+]] = OpTypePointer UniformConstant %[[array_image_ty]] + + ; CHECK: %[[array_sampler_ty:\w+]] = OpTypeRuntimeArray %[[sampler_ty]] + ; CHECK: %[[ptr_array_sampler_ty:\w+]] = OpTypePointer UniformConstant %[[array_sampler_ty]] + + ; CHECK-NOT: %100 = OpVariable + ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[ptr_array_sampler_ty]] UniformConstant + ; CHECK-DAG: %[[image_var]] = OpVariable %[[ptr_array_image_ty]] UniformConstant + ; CHECK: = OpFunction + + ; The access chain and load is replaced by two access chains, two loads, then + ; a combine operation. + ; CHECK: %[[ptr_im:\d+]] = OpAccessChain %[[image_ptr_ty]] %[[image_var]] %uint_1 + ; CHECK: %[[ptr_s:\d+]] = OpAccessChain %[[sampler_ptr_ty]] %[[sampler_var]] %uint_1 + ; CHECK: %[[im:\d+]] = OpLoad %10 %[[ptr_im]] + ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[ptr_s]] + ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] + +)" + BasicTypes() + + " %10 = " + GetParam().image_type_decl + R"( + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %12 = OpTypeRuntimeArray %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + + %100 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + %ptr = OpAccessChain %_ptr_UniformConstant_11 %100 %uint_1 + %combined = OpLoad %11 %ptr + + ; Uses of the combined image sampler are preserved. + ; CHECK: OpCopyObject %11 %combined + + %7 = OpCopyObject %11 %combined + OpReturn + OpFunctionEnd +)"; + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +INSTANTIATE_TEST_SUITE_P(AllCombinedTypes, + SplitCombinedImageSamplerPassTypeCaseTest, + ::testing::ValuesIn(ImageTypeCases())); + +// Remap entry point + +struct EntryPointRemapCase { + const spv_target_env environment = SPV_ENV_VULKAN_1_0; + const char* initial_interface = ""; + const char* expected_interface = nullptr; +}; + +std::ostream& operator<<(std::ostream& os, const EntryPointRemapCase& eprc) { + os << "(env " << spvLogStringForEnv(eprc.environment) << ", init " + << eprc.initial_interface << " -> expect " << eprc.expected_interface + << ")"; + return os; +} + +struct SplitCombinedImageSamplerPassEntryPointRemapTest + : public PassTest<::testing::TestWithParam<EntryPointRemapCase>> { + virtual void SetUp() override { + SetTargetEnv(GetParam().environment); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } +}; + +std::vector<EntryPointRemapCase> EntryPointInterfaceCases() { + return std::vector<EntryPointRemapCase>{ + {SPV_ENV_VULKAN_1_0, " %in_var %out_var", " %in_var %out_var"}, + {SPV_ENV_VULKAN_1_4, " %combined_var", + " %combined_var_image %combined_var_sampler"}, + {SPV_ENV_VULKAN_1_4, " %combined_var %in_var %out_var", + " %combined_var_image %in_var %out_var %combined_var_sampler"}, + {SPV_ENV_VULKAN_1_4, " %in_var %combined_var %out_var", + " %in_var %combined_var_image %out_var %combined_var_sampler"}, + {SPV_ENV_VULKAN_1_4, " %in_var %out_var %combined_var", + " %in_var %out_var %combined_var_image %combined_var_sampler"}, + }; +} + +TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest, + EntryPoint_Combined_UsedInShader) { + const std::string kTest = PreambleFragment(GetParam().initial_interface) + + R"( + OpName %combined "combined" + OpName %combined_var "combined_var" + OpName %in_var "in_var" + OpName %out_var "out_var" + OpDecorate %combined_var DescriptorSet 0 + OpDecorate %combined_var Binding 0 + OpDecorate %in_var BuiltIn FragCoord + OpDecorate %out_var Location 0 + +; CHECK: OpEntryPoint Fragment %main "main")" + + GetParam().expected_interface + R"( +; These clauses ensure the expected interface is the whole interface. +; CHECK-NOT: %{{\d+}} +; CHECK-NOT: %in_var +; CHECK-NOT: %out_var +; CHECK-NOT: %combined_var +; CHECK: OpExecutionMode %main OriginUpperLeft + + ; Check the var names, tracing up through the types. + ; CHECK: %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 + ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler + ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] + ; The combined image variable is replaced by an image variable and a sampler variable. + ; CHECK-DAG: %combined_var_sampler = OpVariable %[[sampler_ptr_ty]] UniformConstant + ; CHECK-DAG: %combined_var_image = OpVariable %[[image_ptr_ty]] UniformConstant + ; CHECK: = OpFunction + + %bool = OpTypeBool +)" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %in_ptr_v4f = OpTypePointer Input %v4float + %in_var = OpVariable %in_ptr_v4f Input + %out_ptr_v4f = OpTypePointer Output %v4float + %out_var = OpVariable %out_ptr_v4f Output + +%combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + ;CHECK: %main_0 = OpLabel + ;CHECK: OpLoad + + %main_0 = OpLabel + %combined = OpLoad %11 %combined_var + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest, + EntryPoint_Combined_UsedOnlyInEntryPointInstruction) { + // If the combined var is in the interface, that is enough to trigger + // its replacement. Otherwise the entry point interface is untouched + // when the combined var is not otherwise used. + const bool combined_var_in_interface = + std::string(GetParam().initial_interface).find("%combined_var") != + std::string::npos; + if (combined_var_in_interface) { + const std::string kTest = PreambleFragment(GetParam().initial_interface) + + R"( + OpName %combined_var "combined_var" + OpName %in_var "in_var" + OpName %out_var "out_var" + OpDecorate %combined_var DescriptorSet 0 + OpDecorate %combined_var Binding 0 + OpDecorate %in_var BuiltIn FragCoord + OpDecorate %out_var Location 0 + + ; CHECK: OpEntryPoint Fragment %main "main")" + + GetParam().expected_interface + R"( + ; These clauses ensure the expected interface is the whole interface. + ; CHECK-NOT: %{{\d+}} + ; CHECK-NOT: %in_var + ; CHECK-NOT: %out_var + ; CHECK-NOT: %combined_var + ; CHECK: OpExecutionMode %main OriginUpperLeft + + %bool = OpTypeBool + )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 + %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %in_ptr_v4f = OpTypePointer Input %v4float + %in_var = OpVariable %in_ptr_v4f Input + %out_ptr_v4f = OpTypePointer Output %v4float + %out_var = OpVariable %out_ptr_v4f Output + + ; %combined_var is not used! + %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + OpReturn + OpFunctionEnd + )"; + auto [disasm, status] = + SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; + } +} + +TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest, + EntryPoint_Combined_Unused) { + // If the combined var is in the interface, that is enough to trigger + // its replacement. Otherwise the entry point interface is untouched + // when the combined var is not otherwise used. + const bool combined_var_in_interface = + std::string(GetParam().initial_interface).find("%combined_var") != + std::string::npos; + if (!combined_var_in_interface) { + const std::string kTest = PreambleFragment(GetParam().initial_interface) + + R"( + ; CHECK: OpEntryPoint Fragment %main "main")" + + GetParam().initial_interface // Note this is the + // intial interface + + R"( + ; These clauses ensure the expected interface is the whole interface. + ; CHECK-NOT: %{{\d+}} + ; CHECK-NOT: %in_var + ; CHECK-NOT: %out_var + ; CHECK-NOT: %combined_var + ; CHECK: OpExecutionMode %main OriginUpperLeft + + ; The variable disappears. + ; CHECK-NOT: %combined_var = + ; CHECK: OpFunctionEnd + OpName %combined_var "combined_var" + OpName %in_var "in_var" + OpName %out_var "out_var" + OpDecorate %combined_var DescriptorSet 0 + OpDecorate %combined_var Binding 0 + OpDecorate %in_var BuiltIn FragCoord + OpDecorate %out_var Location 0 + + + %bool = OpTypeBool + )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 + %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %in_ptr_v4f = OpTypePointer Input %v4float + %in_var = OpVariable %in_ptr_v4f Input + %out_ptr_v4f = OpTypePointer Output %v4float + %out_var = OpVariable %out_ptr_v4f Output + + ; %combined_var is not used! + %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant + %main = OpFunction %void None %voidfn + %main_0 = OpLabel + OpReturn + OpFunctionEnd +)"; + auto [disasm, status] = + SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; + } +} + +INSTANTIATE_TEST_SUITE_P(EntryPointRemap, + SplitCombinedImageSamplerPassEntryPointRemapTest, + ::testing::ValuesIn(EntryPointInterfaceCases())); + +// Remap function types + +struct FunctionTypeCase { + const char* initial_type_params = ""; + const char* expected_type_params = ""; +}; + +std::ostream& operator<<(std::ostream& os, const FunctionTypeCase& ftc) { + os << "(init " << ftc.initial_type_params << " -> expect " + << ftc.expected_type_params << ")"; + return os; +} + +struct SplitCombinedImageSamplerPassFunctionTypeTest + : public PassTest<::testing::TestWithParam<FunctionTypeCase>> { + virtual void SetUp() override { + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } +}; + +std::vector<FunctionTypeCase> FunctionTypeCases() { + return std::vector<FunctionTypeCase>{ + {"", ""}, + {" %image_ty", " %image_ty"}, + {" %sampler_ty", " %sampler_ty"}, + {" %sampled_image_ty", " %image_ty %sampler_ty"}, + {" %uint %sampled_image_ty %float", + " %uint %image_ty %sampler_ty %float"}, + {" %ptr_sampled_image_ty", + " %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty"}, + {" %uint %ptr_sampled_image_ty %float", + " %uint %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty " + "%float"}, + {" %uint %ptr_sampled_image_ty %ptr_sampled_image_ty %float", + " %uint %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty " + "%_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty " + "%float"}, + }; +} + +TEST_P(SplitCombinedImageSamplerPassFunctionTypeTest, + ReplaceCombinedImageSamplersOnly) { + const std::string kTest = Preamble() + +R"( + OpName %f_ty "f_ty" + OpName %sampler_ty "sampler_ty" + OpName %image_ty "image_ty" + OpName %sampled_image_ty "sampled_image_ty" + OpName %ptr_sampled_image_ty "sampled_image_ty" + + )" + BasicTypes() + R"( + + %sampler_ty = OpTypeSampler + %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %sampled_image_ty = OpTypeSampledImage %image_ty + %ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty + + %f_ty = OpTypeFunction %float)" + + GetParam().initial_type_params + R"( + + ; CHECK: %f_ty = OpTypeFunction %float)" + + GetParam().expected_type_params + R"( +)" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_P(SplitCombinedImageSamplerPassFunctionTypeTest, AvoidDuplicateType) { + // SPIR-V does not allow duplicate non-aggregate types. That includes function + // types. Test that when replacing function type parameters would cause a + // collision, that the original function type is replaced with the new one. + const std::string initial_params(GetParam().initial_type_params); + const std::string expected_params(GetParam().expected_type_params); + const std::string kTest = Preamble() + +R"( + OpName %sampler_ty "sampler_ty" + OpName %image_ty "image_ty" + OpName %sampled_image_ty "sampled_image_ty" + OpName %_ptr_UniformConstant_sampler_ty "_ptr_UniformConstant_sampler_ty" + OpName %_ptr_UniformConstant_image_ty "_ptr_UniformConstant_image_ty" + OpName %ptr_sampled_image_ty "sampled_image_ty" + OpName %dest_ty "dest_ty" + + )" + BasicTypes() + R"( + + %sampler_ty = OpTypeSampler + %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %sampled_image_ty = OpTypeSampledImage %image_ty + %ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty + %_ptr_UniformConstant_image_ty = OpTypePointer UniformConstant %image_ty + %_ptr_UniformConstant_sampler_ty = OpTypePointer UniformConstant %sampler_ty + + %100 = OpTypeFunction %float)" + + initial_params + R"( + %dest_ty = OpTypeFunction %float)" + + expected_params + R"( + + ; CHECK: OpTypeSampler + ; CHECK-NOT: %100 = + ; CHECK: %dest_ty = OpTypeFunction %float)" + + expected_params + R"( + ; CHECK-NOT: %100 = + ; CHECK: %main = OpFunction +)" + Main(); + // The original source is invalid if initial and expected params are the same, + // because the type is already duplicated. + // Only test when they are different. + if (initial_params != expected_params) { + auto [disasm, status] = + SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; + } +} + +INSTANTIATE_TEST_SUITE_P(FunctionTypeRemap, + SplitCombinedImageSamplerPassFunctionTypeTest, + ::testing::ValuesIn(FunctionTypeCases())); + +// Test array and runtime-array cases for function type replacement. + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceSampledImageArg) { + // The original module has a sampled image type, used only as a function + // parameter. We still want to replace it. But no other sampled-image types + // exist. This proves that the pass needs a sampled_image_used_as_param_ + // state variable. + const std::string kTest = Preamble() + +R"( + OpName %f_ty "f_ty" + OpName %sampler_ty "sampler_ty" + OpName %image_ty "image_ty" + OpName %sampled_image_ty "sampled_image_ty" + + )" + BasicTypes() + R"( + + %sampler_ty = OpTypeSampler + %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %sampled_image_ty = OpTypeSampledImage %image_ty + + %f_ty = OpTypeFunction %float %sampled_image_ty %float + ; CHECK: %f_ty = OpTypeFunction %float %image_ty %sampler_ty %float +)" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceArrayArg) { + const std::string kTest = Preamble() + +R"( + OpName %f_ty "f_ty" + OpName %sampler_ty "sampler_ty" + OpName %image_ty "image_ty" + OpName %sampled_image_ty "sampled_image_ty" + OpName %ptr_array_si_ty "ptr_array_si_ty" + + )" + BasicTypes() + R"( + + %sampler_ty = OpTypeSampler + %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %sampled_image_ty = OpTypeSampledImage %image_ty + %array_si_ty = OpTypeArray %sampled_image_ty %uint_3 + %ptr_array_si_ty = OpTypePointer UniformConstant %array_si_ty + + ; CHECK: %[[array_i_ty:\w+]] = OpTypeArray %image_ty %uint_3 + ; CHECK: %[[ptr_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] + ; CHECK: %[[array_s_ty:\w+]] = OpTypeArray %sampler_ty %uint_3 + ; CHECK: %[[ptr_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] + + %f_ty = OpTypeFunction %float %uint %ptr_array_si_ty %float + ; CHECK: %f_ty = OpTypeFunction %float %uint %[[ptr_array_i_ty]] %[[ptr_array_s_ty]] %float +)" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceRtArrayArg) { + const std::string kTest = Preamble() + +R"( + OpName %f_ty "f_ty" + OpName %sampler_ty "sampler_ty" + OpName %image_ty "image_ty" + OpName %sampled_image_ty "sampled_image_ty" + OpName %ptr_array_si_ty "ptr_array_si_ty" + + )" + BasicTypes() + R"( + + %sampler_ty = OpTypeSampler + %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %sampled_image_ty = OpTypeSampledImage %image_ty + %array_si_ty = OpTypeRuntimeArray %sampled_image_ty + %ptr_array_si_ty = OpTypePointer UniformConstant %array_si_ty + + ; CHECK: %[[array_i_ty:\w+]] = OpTypeRuntimeArray %image_ty + ; CHECK: %[[ptr_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] + ; CHECK: %[[array_s_ty:\w+]] = OpTypeRuntimeArray %sampler_ty + ; CHECK: %[[ptr_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] + + %f_ty = OpTypeFunction %float %uint %ptr_array_si_ty %float + ; CHECK: %f_ty = OpTypeFunction %float %uint %[[ptr_array_i_ty]] %[[ptr_array_s_ty]] %float +)" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +// Remap function bodies + +std::string NamedITypes() { + return R"( + OpName %f "f" + OpName %f_ty "f_ty" + OpName %i_ty "i_ty" + OpName %s_ty "s_ty" + OpName %p_i_ty "p_i_ty" + OpName %p_s_ty "p_s_ty" +)"; +} + +std::string NamedCombinedTypes() { + return R"( + OpName %si_ty "si_ty" + OpName %p_si_ty "p_si_ty" + OpName %array_si_ty "array_si_ty" + OpName %rtarray_si_ty "rtarray_si_ty" + OpName %p_array_si_ty "p_array_si_ty" + OpName %p_rtarray_si_ty "p_rtarray_si_ty" +)"; +} + +std::string NamedCaller() { + return R"( + OpName %caller_ty "caller_ty" + OpName %caller "caller" + OpName %caller_entry "caller_entry" + OpName %caller_call "caller_call" + OpName %caller_arg "caller_arg" +)"; +} + +std::string ITypes() { + return R"( + %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %s_ty = OpTypeSampler + %p_i_ty = OpTypePointer UniformConstant %i_ty + %p_s_ty = OpTypePointer UniformConstant %s_ty +)"; +} + +std::string CombinedTypes() { + return R"( + %si_ty = OpTypeSampledImage %i_ty + %p_si_ty = OpTypePointer UniformConstant %si_ty + %array_si_ty = OpTypeArray %si_ty %uint_3 + %p_array_si_ty = OpTypePointer UniformConstant %array_si_ty + %rtarray_si_ty = OpTypeRuntimeArray %si_ty + %p_rtarray_si_ty = OpTypePointer UniformConstant %rtarray_si_ty +)"; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_ScalarNoChange) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + BasicTypes() + ITypes() + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %float %i_ty %s_ty %p_i_ty %p_s_ty + %f_ty = OpTypeFunction %float %i_ty %s_ty %p_i_ty %p_s_ty + + ; CHECK: %f = OpFunction %float None %f_ty + ; CHECK-NEXT: OpFunctionParameter %i_ty + ; CHECK-NEXT: OpFunctionParameter %s_ty + ; CHECK-NEXT: OpFunctionParameter %p_i_ty + ; CHECK-NEXT: OpFunctionParameter %p_s_ty + ; CHECK-NEXT: OpLabel + %f = OpFunction %float None %f_ty + %100 = OpFunctionParameter %i_ty + %101 = OpFunctionParameter %s_ty + %102 = OpFunctionParameter %p_i_ty + %103 = OpFunctionParameter %p_s_ty + %110 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionBody_SampledImage_OpImageSample) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + BasicTypes() + ITypes() + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %v4float %uint %i_ty %s_ty %float + %f_ty = OpTypeFunction %v4float %uint %si_ty %float + + ; CHECK: %f = OpFunction %v4float None %f_ty + ; CHECK: OpFunctionParameter %uint + ; CHECK-NEXT: %[[i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: OpFunctionParameter %float + ; CHECK-NEXT: OpLabel + ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]] + ; CHECK-NEXT: %200 = OpImageSampleExplicitLod %v4float %[[si]] %13 Lod %float_0 + ; CHECK-NEXT: OpReturnValue %200 + + %f = OpFunction %v4float None %f_ty + %100 = OpFunctionParameter %uint + %101 = OpFunctionParameter %si_ty ; replace this + %110 = OpFunctionParameter %float + %120 = OpLabel + %200 = OpImageSampleExplicitLod %v4float %101 %13 Lod %float_0 + OpReturnValue %200 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_SampledImage_OpImage) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + BasicTypes() + ITypes() + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %uint %i_ty %s_ty %float + %f_ty = OpTypeFunction %void %uint %si_ty %float + + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK: OpFunctionParameter %uint + ; CHECK-NEXT: %[[i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: OpFunctionParameter %float + ; CHECK-NEXT: OpLabel + ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]] + ; CHECK-NEXT: %200 = OpImage %i_ty %[[si]] + ; CHECK-NEXT: OpReturn + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %uint + %101 = OpFunctionParameter %si_ty ; replace this + %110 = OpFunctionParameter %float + %120 = OpLabel + %200 = OpImage %i_ty %101 + OpReturn + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_PtrSampledImage) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + BasicTypes() + ITypes() + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %v4float %uint %p_i_ty %p_s_ty %float + %f_ty = OpTypeFunction %v4float %uint %p_si_ty %float + + ; CHECK: %f = OpFunction %v4float None %f_ty + ; CHECK-NEXT: OpFunctionParameter %uint + ; CHECK-NEXT: %[[pi:\w+]] = OpFunctionParameter %p_i_ty + ; CHECK-NEXT: %[[ps:\w+]] = OpFunctionParameter %p_s_ty + ; CHECK-NEXT: OpFunctionParameter %float + ; CHECK-NEXT: OpLabel + ; CHECK-NEXT: %[[i:\w+]] = OpLoad %i_ty %[[pi]] + ; CHECK-NEXT: %[[s:\w+]] = OpLoad %s_ty %[[ps]] + ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]] + ; CHECK-NEXT: %200 = OpImageSampleExplicitLod %v4float %[[si]] %13 Lod %float_0 + ; CHECK-NEXT: OpReturnValue %200 + + %f = OpFunction %v4float None %f_ty + %100 = OpFunctionParameter %uint + %101 = OpFunctionParameter %p_si_ty ; replace this + %110 = OpFunctionParameter %float + %120 = OpLabel + %121 = OpLoad %si_ty %101 + %200 = OpImageSampleExplicitLod %v4float %121 %13 Lod %float_0 + OpReturnValue %200 + OpFunctionEnd + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_NoImageOrSampler_NoChange) { + const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + + BasicTypes() + ITypes() + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %uint %float + %f_ty = OpTypeFunction %void %uint %float + %caller_ty = OpTypeFunction %float ; make it return non-void otherwise it's just like main + + ; The called function does not change + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: = OpFunctionParameter %uint + ; CHECK-NEXT: = OpFunctionParameter %float + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %uint + %101 = OpFunctionParameter %float + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; The caller does not change + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_arg = OpCopyObject %uint %uint_0 + ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg %float_0 + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty +%caller_entry = OpLabel + %caller_arg = OpCopyObject %uint %uint_0 + %caller_call = OpFunctionCall %void %f %caller_arg %float_0 + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + // We still get a success-with-change result because the boilerplate included + // combined types, which were removed. + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_Image_NoChange) { + const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + + BasicTypes() + ITypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %i_ty + %f_ty = OpTypeFunction %void %i_ty + %caller_ty = OpTypeFunction %float %i_ty + + ; The called function does not change + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: = OpFunctionParameter %i_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %i_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; The caller does not change + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %caller_arg = OpFunctionParameter %i_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %i_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_Sampler_NoChange) { + const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + + BasicTypes() + ITypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %s_ty + %f_ty = OpTypeFunction %void %s_ty + %caller_ty = OpTypeFunction %float %s_ty + + ; The called function does not change + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: = OpFunctionParameter %s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %s_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; The caller does not change + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %caller_arg = OpFunctionParameter %s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %s_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrImage_NoChange) { + const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + + BasicTypes() + ITypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %p_i_ty + %f_ty = OpTypeFunction %void %p_i_ty + %caller_ty = OpTypeFunction %float %p_i_ty + + ; The called function does not change + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: = OpFunctionParameter %p_i_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %p_i_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; The caller does not change + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %caller_arg = OpFunctionParameter %p_i_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %p_i_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrSampler_NoChange) { + const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + + BasicTypes() + ITypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %p_s_ty + %f_ty = OpTypeFunction %void %p_s_ty + %caller_ty = OpTypeFunction %float %p_s_ty + + ; The called function does not change + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: = OpFunctionParameter %p_s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %p_s_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; The caller does not change + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %caller_arg = OpFunctionParameter %p_s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %p_s_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_SampledImage_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty + %f_ty = OpTypeFunction %void %si_ty + %caller_ty = OpTypeFunction %float %si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %si_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_SampledImageDuplicatedArg_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty + %f_ty = OpTypeFunction %void %si_ty %si_ty + %caller_ty = OpTypeFunction %float %si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %si_ty + %101 = OpFunctionParameter %si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] %[[caller_i]] %[[caller_s]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %si_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_SampledImageTwoDistinct_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty + %f_ty = OpTypeFunction %void %si_ty %si_ty + %caller_ty = OpTypeFunction %float %si_ty %si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %si_ty + %101 = OpFunctionParameter %si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i_0:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[caller_s_0:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %[[caller_i_1:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[caller_s_1:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i_0]] %[[caller_s_0]] %[[caller_i_1]] %[[caller_s_1]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %si_ty + %201 = OpFunctionParameter %si_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg %201 + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_SampledImageAndCopy_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty + %f_ty = OpTypeFunction %void %si_ty %si_ty + ; CHECK: %caller_ty = OpTypeFunction %float %i_ty %s_ty + %caller_ty = OpTypeFunction %float %si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %si_ty + %101 = OpFunctionParameter %si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] %[[caller_i]] %[[caller_s]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %si_ty +%caller_entry = OpLabel + %copy = OpCopyObject %si_ty %caller_arg + %caller_call = OpFunctionCall %void %f %caller_arg %copy + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_SampledImageSurrounded_Split) { + // Test indexing by surrounding the sampled image parameter with other + // arguments that should not be touched. + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %float %i_ty %s_ty %uint + %f_ty = OpTypeFunction %void %float %si_ty %uint + ; CHECK: %caller_ty = OpTypeFunction %float %uint %i_ty %s_ty %float + %caller_ty = OpTypeFunction %float %uint %si_ty %float + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_f:\w+]] = OpFunctionParameter %float + ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %[[callee_u:\w+]] = OpFunctionParameter %uint + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %99 = OpFunctionParameter %float + %100 = OpFunctionParameter %si_ty + %101 = OpFunctionParameter %uint + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[u_param:\w+]] = OpFunctionParameter %uint + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty + ; CHECK-NEXT: %[[f_param:\w+]] = OpFunctionParameter %float + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[f_param]] %[[caller_i]] %[[caller_s]] %[[u_param]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %200 = OpFunctionParameter %uint + %caller_arg = OpFunctionParameter %si_ty + %201 = OpFunctionParameter %float +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %201 %caller_arg %200 + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrSampledImage_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %f_ty = OpTypeFunction %void %p_i_ty %p_s_ty + %f_ty = OpTypeFunction %void %p_si_ty + %caller_ty = OpTypeFunction %float %p_si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %p_i_ty + ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %p_s_ty + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %p_si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %p_i_ty + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %p_s_ty + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %p_si_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_PtrArraySampledImage_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %[[array_i_ty:\w+]] = OpTypeArray %i_ty %uint_3 + ; CHECK: %[[p_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] + ; CHECK: %[[array_s_ty:\w+]] = OpTypeArray %s_ty %uint_3 + ; CHECK: %[[p_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] + + ; CHECK: %f_ty = OpTypeFunction %void %[[p_array_i_ty]] %[[p_array_s_ty]] + %f_ty = OpTypeFunction %void %p_array_si_ty + %caller_ty = OpTypeFunction %float %p_array_si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] + ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %p_array_si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %p_array_si_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +TEST_F(SplitCombinedImageSamplerPassTest, + FunctionCall_PtrRtArraySampledImage_Split) { + const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + + NamedCaller() + BasicTypes() + ITypes() + + CombinedTypes() + R"( + + ; CHECK: %[[array_i_ty:\w+]] = OpTypeRuntimeArray %i_ty + ; CHECK: %[[p_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] + ; CHECK: %[[array_s_ty:\w+]] = OpTypeRuntimeArray + ; CHECK: %[[p_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] + + ; CHECK: %f_ty = OpTypeFunction %void %[[p_array_i_ty]] %[[p_array_s_ty]] + %f_ty = OpTypeFunction %void %p_rtarray_si_ty + %caller_ty = OpTypeFunction %float %p_rtarray_si_ty + + ; Call function arg is split. We've checked these details in other tests. + ; CHECK: %f = OpFunction %void None %f_ty + ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] + ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] + ; CHECK-NEXT: = OpLabel + ; CHECK-NEXT: OpReturn + ; CHECK-NEXT: OpFunctionEnd + + %f = OpFunction %void None %f_ty + %100 = OpFunctionParameter %p_rtarray_si_ty + %110 = OpLabel + OpReturn + OpFunctionEnd + + ; CHECK: %caller = OpFunction %float None %caller_ty + ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] + ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] + ; CHECK-NEXT: %caller_entry = OpLabel + ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] + ; CHECK-NEXT: OpReturnValue %float_0 + ; CHECK-NEXT: OpFunctionEnd + + %caller = OpFunction %float None %caller_ty + %caller_arg = OpFunctionParameter %p_rtarray_si_ty +%caller_entry = OpLabel + %caller_call = OpFunctionCall %void %f %caller_arg + OpReturnValue %float_0 + OpFunctionEnd + + )" + Main(); + + auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( + kTest, /* do_validation= */ true); + EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; +} + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/strip_nonsemantic_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp index 3aacffa..5c3909c 100644 --- a/test/opt/strip_nonsemantic_info_test.cpp +++ b/test/opt/strip_nonsemantic_info_test.cpp
@@ -224,6 +224,70 @@ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); } +TEST_F(StripNonSemanticInfoTest, StripNonSemanticScopes) { + std::string text = R"( +;CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info +;CHECK-NOT: OpExtInstImport "NonSemantic.Shader.DebugInfo.100 +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugInfoNon +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugSource +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugCompilationUnit +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugTypeFunction +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugFunction +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugScope +;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugNoScope + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + OpExtension "SPV_KHR_storage_buffer_storage_class" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 + OpExecutionMode %2 LocalSize 1 1 1 + %4 = OpString "" + OpDecorate %5 ArrayStride 4 + OpDecorate %6 ArrayStride 4 + OpDecorate %7 Block + OpMemberDecorate %7 0 Offset 0 + OpDecorate %3 Binding 0 + OpDecorate %3 DescriptorSet 0 + %8 = OpTypeVoid + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 100 + %11 = OpTypeFunction %8 + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypeFloat 32 + %5 = OpTypePointer StorageBuffer %14 + %6 = OpTypeRuntimeArray %14 + %7 = OpTypeStruct %6 + %15 = OpTypePointer StorageBuffer %7 + %16 = OpConstant %14 0 + %3 = OpVariable %15 StorageBuffer + %17 = OpExtInst %8 %1 DebugInfoNone + %18 = OpExtInst %8 %1 DebugSource %4 %4 + %19 = OpExtInst %8 %1 DebugCompilationUnit %10 %10 %18 %10 + %20 = OpExtInst %8 %1 DebugTypeFunction %10 %8 + %21 = OpExtInst %8 %1 DebugFunction %4 %20 %18 %10 %10 %19 %4 %10 %10 + %22 = OpExtInst %8 %1 DebugEntryPoint %21 %19 %4 %4 + %23 = OpExtInst %8 %1 DebugTypeBasic %4 %10 %10 %10 + %24 = OpExtInst %8 %1 DebugTypeArray %23 %10 + %25 = OpExtInst %8 %1 DebugTypeMember %4 %24 %18 %10 %10 %10 %10 %10 + %26 = OpExtInst %8 %1 DebugTypeComposite %4 %10 %18 %10 %10 %19 %4 %10 %10 %25 + %27 = OpExtInst %8 %1 DebugGlobalVariable %4 %26 %18 %10 %10 %19 %4 %3 %10 + %2 = OpFunction %8 None %11 + %28 = OpLabel + %29 = OpExtInst %8 %1 DebugFunctionDefinition %21 %2 + %30 = OpExtInst %8 %1 DebugScope %21 + %31 = OpExtInst %8 %1 DebugLine %18 %10 %10 %10 %10 + %32 = OpAccessChain %5 %3 %13 %13 + OpStore %32 %16 + OpReturn + %33 = OpExtInst %8 %1 DebugNoScope + OpFunctionEnd +)"; + + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, false); +} + // Make sure that strip reflect does not remove the debug info (OpString and // OpLine). TEST_F(StripNonSemanticInfoTest, DontStripDebug) {
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp index 1569ad7..43271da 100644 --- a/test/opt/trim_capabilities_pass_test.cpp +++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -1401,6 +1401,244 @@ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); } +TEST_F(TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_RemainsSimplePointer_Vulkan1_0) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + +; CHECK: OpCapability StorageBuffer16BitAccess +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct Block + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer StorageBuffer %struct + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_0); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_RemainsSimplePointer_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + +; CHECK: OpCapability StorageBuffer16BitAccess +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct Block + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer StorageBuffer %struct + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_1); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F( + TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_RemainsSimplePointerUshortPhysicalStorage_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability StorageBuffer16BitAccess + OpCapability PhysicalStorageBufferAddresses + +; CHECK: OpCapability StorageBuffer16BitAccess +; CHECK: OpCapability PhysicalStorageBufferAddresses +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" +; CHECK-NOT: OpCapability Int16 + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct Block + %void = OpTypeVoid + %ushort = OpTypeInt 16 0 + %struct = OpTypeStruct %ushort + %ptr = OpTypePointer PhysicalStorageBuffer %struct + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_1); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F( + TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_RemainsSimplePointerUshortStorageBuffer_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability StorageBuffer16BitAccess + +; CHECK: OpCapability StorageBuffer16BitAccess +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" +; CHECK-NOT: OpCapability Int16 + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct Block + %void = OpTypeVoid + %ushort = OpTypeInt 16 0 + %struct = OpTypeStruct %ushort + %ptr = OpTypePointer StorageBuffer %struct + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_1); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F( + TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_RemainsSimplePointerUshortRecordBuffer_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability RayTracingKHR + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_ray_tracing" + +; CHECK-NOT: OpCapability Int16 +; CHECK: OpCapability RayTracingKHR +; CHECK: OpCapability StorageBuffer16BitAccess +; CHECK: OpExtension "SPV_KHR_ray_tracing" +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct Block + %void = OpTypeVoid + %ushort = OpTypeInt 16 0 + %struct = OpTypeStruct %ushort + %ptr = OpTypePointer ShaderRecordBufferKHR %struct + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_1); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_TrimRecordBuffer_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability RayTracingKHR + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_ray_tracing" + +; CHECK-NOT: OpCapability RayTracingKHR +; CHECK-NOT: OpCapability StorageBuffer16BitAccess +; CHECK-NOT: OpExtension "SPV_KHR_ray_tracing" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct Block + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_1); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageBuffer16BitAccess_RemovedSimplePointer) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + +; CHECK-NOT: OpCapability StorageBuffer16BitAccess +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Function %struct + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + TEST_F(TrimCapabilitiesPassTest, FragmentShaderInterlock_RemovedIfNotUsed) { const std::string kTest = R"( OpCapability Shader @@ -2762,8 +3000,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -2792,8 +3030,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK: OpCapability GroupNonUniform OpCapability Shader @@ -2828,8 +3066,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -2864,8 +3102,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -2900,8 +3138,8 @@ ; CHECK: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -2937,8 +3175,8 @@ ; CHECK: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -2974,8 +3212,8 @@ ; CHECK: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -3011,8 +3249,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -3068,10 +3306,10 @@ // clang-format on }; -using TrimCapabilitiesPassTestSubgroupNV_Unsigned = PassTest< +using TrimCapabilitiesPassTestSubgroupEXT_Unsigned = PassTest< ::testing::TestWithParam<std::tuple<SubgroupTestCase, std::string>>>; -TEST_P(TrimCapabilitiesPassTestSubgroupNV_Unsigned, - GroupNonUniformPartitionedNV_Remains) { +TEST_P(TrimCapabilitiesPassTestSubgroupEXT_Unsigned, + GroupNonUniformPartitionedEXT_Remains) { SubgroupTestCase test_case = std::get<0>(GetParam()); const std::string operation = std::get<1>(GetParam()); @@ -3083,8 +3321,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -3120,14 +3358,14 @@ } INSTANTIATE_TEST_SUITE_P( - TrimCapabilitiesPassTestSubgroupNV_Unsigned_I, - TrimCapabilitiesPassTestSubgroupNV_Unsigned, + TrimCapabilitiesPassTestSubgroupEXT_Unsigned_I, + TrimCapabilitiesPassTestSubgroupEXT_Unsigned, ::testing::Combine(::testing::ValuesIn(kSubgroupTestCases), - ::testing::Values("PartitionedReduceNV", - "PartitionedInclusiveScanNV", - "PartitionedExclusiveScanNV")), + ::testing::Values("PartitionedReduceEXT", + "PartitionedInclusiveScanEXT", + "PartitionedExclusiveScanEXT")), [](const ::testing::TestParamInfo< - TrimCapabilitiesPassTestSubgroupNV_Unsigned::ParamType>& info) { + TrimCapabilitiesPassTestSubgroupEXT_Unsigned::ParamType>& info) { return std::get<0>(info.param).opcode + "_" + std::get<1>(info.param); }); @@ -3146,8 +3384,8 @@ ; CHECK: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK-NOT: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -3207,8 +3445,8 @@ ; CHECK-NOT: OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered ; CHECK: OpCapability GroupNonUniformClustered - OpCapability GroupNonUniformPartitionedNV -; CHECK-NOT: OpCapability GroupNonUniformPartitionedNV + OpCapability GroupNonUniformPartitionedEXT +; CHECK-NOT: OpCapability GroupNonUniformPartitionedEXT OpCapability GroupNonUniform ; CHECK-NOT: OpCapability GroupNonUniform OpExtension "SPV_NV_shader_subgroup_partitioned" @@ -3305,6 +3543,259 @@ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); } +TEST_F(TrimCapabilitiesPassTest, QuadControlKHR_RemoveIfNotUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability QuadControlKHR +; CHECK-NOT: OpCapability QuadControlKHR + OpExtension "SPV_KHR_quad_control" +; CHECK-NOT: OpExtension "SPV_KHR_quad_control" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 660 + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %7 = OpTypeFunction %void +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %7 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, QuadControlKHR_RemainsWithQuadAny) { + const std::string kTest = R"( + OpCapability Shader + OpCapability QuadControlKHR +; CHECK: OpCapability QuadControlKHR + OpExtension "SPV_KHR_quad_control" +; CHECK: OpExtension "SPV_KHR_quad_control" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 660 + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %7 = OpTypeFunction %void +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %7 + %8 = OpLabel + %true = OpConstantTrue %bool + %10 = OpGroupNonUniformQuadAnyKHR %bool %true + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, PhysicalStorageBuffer_RecursiveTypes) { + const std::string kTest = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability PhysicalStorageBufferAddresses + OpExtension "SPV_KHR_physical_storage_buffer" +; CHECK: OpExtension "SPV_KHR_physical_storage_buffer" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + OpSource HLSL 600 + OpMemberDecorate %_struct_2 0 Offset 0 + OpMemberDecorate %_struct_2 1 Offset 16 + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer__struct_2 PhysicalStorageBuffer + %int = OpTypeInt 32 1 + %_struct_2 = OpTypeStruct %int %_ptr_PhysicalStorageBuffer__struct_2 +%_ptr_PhysicalStorageBuffer__struct_2 = OpTypePointer PhysicalStorageBuffer %_struct_2 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, Geometry_Remains) { + const std::string kTest = R"( + OpCapability Geometry +; CHECK: OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %gs_main "gs_main" %gl_Position + OpExecutionMode %gs_main OutputVertices 3 + OpExecutionMode %gs_main Invocations 1 + OpExecutionMode %gs_main Triangles + OpExecutionMode %gs_main OutputTriangleStrip + OpSource HLSL 660 + OpName %gs_main "gs_main" + OpDecorate %gl_Position BuiltIn Position + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %7 = OpTypeFunction %void +%gl_Position = OpVariable %_ptr_Output_v4float Output + %gs_main = OpFunction %void None %7 + %8 = OpLabel + OpEmitVertex + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +// FIXME(6277): enable once spirv-opt supports SPV_INTEL_function_variants +#if 0 +TEST_F(TrimCapabilitiesPassTest, Geometry_RemainsIntel) { + const std::string kTest = R"( + OpCapability Geometry + ; CHECK: OpCapability Geometry + OpCapability SpecConditionalINTEL + OpExtension "SPV_INTEL_function_variants" + OpMemoryModel Logical GLSL450 + OpConditionalEntryPointINTEL %false Geometry %gs_main "gs_main" + OpExecutionMode %gs_main OutputVertices 3 + OpExecutionMode %gs_main Invocations 1 + OpExecutionMode %gs_main Triangles + OpExecutionMode %gs_main OutputTriangleStrip + OpSource HLSL 660 + OpName %gs_main "gs_main" + OpDecorate %gl_Position BuiltIn Position + %bool = OpTypeBool + %false = OpSpecConstantFalse %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %7 = OpTypeFunction %void + %gl_Position = OpVariable %_ptr_Output_v4float Output + %gs_main = OpFunction %void None %7 + %8 = OpLabel + OpEmitVertex + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} +#endif + +TEST_F(TrimCapabilitiesPassTest, Geometry_Removed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Geometry +; CHECK-NOT: OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %ps_main "ps_main" %in_var_POSITION %out_var_SV_Target + OpExecutionMode %ps_main OriginUpperLeft + OpSource HLSL 660 + OpName %in_var_POSITION "in.var.POSITION" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %ps_main "ps_main" + OpDecorate %in_var_POSITION Location 0 + OpDecorate %out_var_SV_Target Location 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %9 = OpTypeFunction %void +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %ps_main = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %v4float %in_var_POSITION + OpStore %out_var_SV_Target %11 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +// FIXME(6277): enable once spirv-opt supports SPV_INTEL_function_variants +#if 0 +TEST_F(TrimCapabilitiesPassTest, Geometry_RemovedIntel) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Geometry +; CHECK-NOT: OpCapability Geometry + OpCapability SpecConditionalINTEL + OpExtension "SPV_INTEL_function_variants" + OpMemoryModel Logical GLSL450 + OpConditionalEntryPointINTEL %false Fragment %ps_main "ps_main" %in_var_POSITION %out_var_SV_Target + OpExecutionMode %ps_main OriginUpperLeft + OpSource HLSL 660 + OpName %in_var_POSITION "in.var.POSITION" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %ps_main "ps_main" + OpDecorate %in_var_POSITION Location 0 + OpDecorate %out_var_SV_Target Location 0 + %bool = OpTypeBool + %false = OpSpecConstantFalse %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %9 = OpTypeFunction %void +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %ps_main = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %v4float %in_var_POSITION + OpStore %out_var_SV_Target %11 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} +#endif + +TEST_F(TrimCapabilitiesPassTest, CheckNop) { + const std::string kTest = R"( + OpCapability Shader +; CHECK: OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpNop + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + INSTANTIATE_TEST_SUITE_P( TrimCapabilitiesPassTestSubgroupClustered_Unsigned_I, TrimCapabilitiesPassTestSubgroupClustered_Unsigned,
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp index 865bfbb..29feaa0 100644 --- a/test/opt/type_manager_test.cpp +++ b/test/opt/type_manager_test.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -147,6 +149,7 @@ types.emplace_back(new Pointer(f32, spv::StorageClass::Input)); types.emplace_back(new Pointer(sts32f32, spv::StorageClass::Function)); types.emplace_back(new Pointer(a42f32, spv::StorageClass::Function)); + types.emplace_back(new Pointer(nullptr, spv::StorageClass::Uniform)); // Function types.emplace_back(new Function(voidt, {})); @@ -174,6 +177,27 @@ types.emplace_back(new CooperativeMatrixKHR(f32, 8, 8, 8, 1002)); types.emplace_back(new RayQueryKHR()); types.emplace_back(new HitObjectNV()); + types.emplace_back(new HitObjectEXT()); + types.emplace_back(new CooperativeVectorNV(f32, 16)); + + // SPV_AMDX_shader_enqueue + types.emplace_back(new NodePayloadArrayAMDX(sts32f32)); + + // Tensors + types.emplace_back(new TensorARM(f32)); + auto* tensor_f32 = types.back().get(); + types.emplace_back(new TensorARM(f32, 4)); + auto* tensor_f32_ranked = types.back().get(); + types.emplace_back(new TensorARM(f32, 4, 44)); + auto* tensor_f32_shaped = types.back().get(); + + // BufferEXT (SPV_EXT_descriptor_heap) + types.emplace_back(new BufferEXT(spv::StorageClass::StorageBuffer)); + + // Graph + types.emplace_back(new GraphARM(0, {tensor_f32})); + types.emplace_back(new GraphARM(1, {tensor_f32_ranked, tensor_f32_ranked})); + types.emplace_back(new GraphARM(1, {tensor_f32_shaped, tensor_f32_shaped})); types.emplace_back(new TensorLayoutNV(1002, 1000)); types.emplace_back(new TensorViewNV(1002, 1003, {1000, 1001})); @@ -243,6 +267,15 @@ %cm = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4 %id2 = OpConstant %u32 2 %cmkhr = OpTypeCooperativeMatrixKHR %f64 %id4 %id4 %id4 %id2 + %untyped = OpTypeUntypedPointerKHR Uniform + ; ID 43 + %ts_shape = OpConstantComposite %a5u32 %id4 %id4 %id4 %id4 + %ts = OpTypeTensorARM %u32 + %tsr = OpTypeTensorARM %u32 %id4 + %tss = OpTypeTensorARM %u32 %id4 %ts_shape + %g_noin = OpTypeGraphARM 0 %ts + %g_onein = OpTypeGraphARM 1 %tsr %tsr + %g_shaped = OpTypeGraphARM 1 %tss %tss )"; std::vector<std::pair<uint32_t, std::string>> type_id_strs = { @@ -282,10 +315,20 @@ {38, "[sint32, id(34), words(2,34)]"}, {39, "<float64, 6, 6, 6>"}, {41, "<float64, 6, 6, 6, 40>"}, + {42, "untyped_ptr 2*"}, // Include storage class number + // Id 43 is OpConstantComposite %a5u32 %id4 %id4 %id4 %id4 + {44, "tensor<uint32, id(0), id(0)>"}, + {45, "tensor<uint32, id(6), id(0)>"}, + {46, "tensor<uint32, id(6), id(43)>"}, + {47, "graph<0,tensor<uint32, id(0), id(0)>>"}, + {48, + "graph<1,tensor<uint32, id(6), id(0)>,tensor<uint32, id(6), id(0)>>"}, + {49, + "graph<1,tensor<uint32, id(6), id(43)>,tensor<uint32, id(6), id(43)>>"}, }; std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, text); ASSERT_NE(nullptr, context.get()); // It assembled TypeManager manager(nullptr, context.get()); @@ -1040,8 +1083,11 @@ ; CHECK: [[uniform_ptr:%\w+]] = OpTypePointer Uniform [[uint]] ; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2 ; CHECK: [[uint8:%\w+]] = OpConstant [[uint]] 8 +; CHECK: [[uint4:%\w+]] = OpConstant [[uint]] 4 +; CHECK: [[uint_arr4:%\w+]] = OpTypeArray [[uint]] [[uint4]] ; CHECK: [[uint24:%\w+]] = OpConstant [[uint]] 24 ; CHECK: [[uint42:%\w+]] = OpConstant [[uint]] 42 +; CHECK: [[uint_arr4_44:%\w+]] = OpConstantComposite [[uint_arr4]] [[uint4]] [[uint4]] [[uint4]] [[uint4]] ; CHECK: [[uint100:%\w+]] = OpConstant [[uint]] 100 ; CHECK: [[void:%\w+]] = OpTypeVoid ; CHECK: [[bool:%\w+]] = OpTypeBool @@ -1098,6 +1144,12 @@ ; CHECK: OpTypeCooperativeMatrixKHR [[f32]] [[uint8]] [[uint8]] [[uint8]] [[uint2]] ; CHECK: OpTypeRayQueryKHR ; CHECK: OpTypeHitObjectNV +; CHECK: [[tensor_f32:%\w+]] = OpTypeTensorARM [[f32]] +; CHECK: [[tensor_f32_ranked:%\w+]] = OpTypeTensorARM [[f32]] [[uint4]] +; CHECK: [[tensor_f32_shaped:%\w+]] = OpTypeTensorARM [[f32]] [[uint4]] [[uint_arr4_44]] +; CHECK: OpTypeGraphARM 0 [[tensor_f32]] +; CHECK: OpTypeGraphARM 1 [[tensor_f32_ranked]] [[tensor_f32_ranked]] +; CHECK: OpTypeGraphARM 1 [[tensor_f32_shaped]] [[tensor_f32_shaped]] OpCapability Shader OpCapability Int64 OpCapability Linkage @@ -1109,8 +1161,11 @@ %1001 = OpConstant %uint 1 %1002 = OpConstant %uint 2 %8 = OpConstant %uint 8 +%4 = OpConstant %uint 4 +%5 = OpTypeArray %uint %4 %24 = OpConstant %uint 24 %42 = OpConstant %uint 42 +%44 = OpConstantComposite %5 %4 %4 %4 %4 %100 = OpConstant %uint 100 %1003 = OpConstantFalse %bool )"; @@ -1239,6 +1294,35 @@ EXPECT_EQ(id, 1201); } +TEST(TypeManager, AttachLinkageDecoration) { + const std::string text = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %1000 LinkageAttributes "_1000" Export + %800 = OpTypeInt 32 0 + %1000 = OpTypeStruct %800 + %1200 = OpTypeStruct %800 + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_5, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + + constexpr uint32_t source_id = 1000u; + constexpr uint32_t target_id = 1200u; + std::vector<Instruction*> decorations = + context->get_decoration_mgr()->GetDecorationsFor(source_id, true); + Type* type = context->get_type_mgr()->GetType(target_id); + for (auto dec : decorations) { + manager.AttachDecoration(*dec, type); + } + EXPECT_FALSE(type->decoration_empty()); + EXPECT_TRUE( + type->HasSameDecorations(context->get_type_mgr()->GetType(source_id))); +} + } // namespace } // namespace analysis } // namespace opt
diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp index 4ceeb14..82ddccf 100644 --- a/test/opt/types_test.cpp +++ b/test/opt/types_test.cpp
@@ -226,6 +226,7 @@ types.emplace_back(new Pointer(sts32f32, spv::StorageClass::Function)); types.emplace_back(new Pointer(a42f32, spv::StorageClass::Function)); types.emplace_back(new Pointer(voidt, spv::StorageClass::Function)); + types.emplace_back(new Pointer(nullptr, spv::StorageClass::Uniform)); // Function types.emplace_back(new Function(voidt, {})); @@ -331,6 +332,50 @@ EXPECT_EQ(struct_100xf32.NumberOfComponents(), 100); } +TEST(Types, GetByteOffset) { + Integer i32(32, true); + Float f64(64); + Vector v4i32(&i32, 4); + + // Struct: { i32, v4i32, f64 } + Struct s1({&i32, &v4i32, &f64}); + s1.AddMemberDecoration(0, {uint32_t(spv::Decoration::Offset), 0}); + s1.AddMemberDecoration(1, {uint32_t(spv::Decoration::Offset), 16}); + s1.AddMemberDecoration(2, {uint32_t(spv::Decoration::Offset), 32}); + + EXPECT_EQ(s1.GetByteOffset({0}).value(), 0); + EXPECT_EQ(s1.GetByteOffset({1}).value(), 16); + EXPECT_EQ(s1.GetByteOffset({2}).value(), 32); + + // Into the vector + EXPECT_EQ(s1.GetByteOffset({1, 0}).value(), 16); + EXPECT_EQ(s1.GetByteOffset({1, 1}).value(), 20); + EXPECT_EQ(s1.GetByteOffset({1, 3}).value(), 28); + + // Array of struct: { i32, v4i32, f64 }[10] + Array::LengthInfo len_info{1, {Array::LengthInfo::kConstant, 10}}; + Array arr(&s1, len_info); + arr.AddDecoration({uint32_t(spv::Decoration::ArrayStride), 48}); + + // arr[2].v4i32[3] + EXPECT_EQ(arr.GetByteOffset({2, 1, 3}).value(), 48 * 2 + 16 + 12); + + // Matrix: 4x4 of f64 + Vector v4f64(&f64, 4); + Matrix m(&v4f64, 4); + m.AddDecoration({uint32_t(spv::Decoration::MatrixStride), 32}); + + // m[1][2] + EXPECT_EQ(m.GetByteOffset({1, 2}).value(), 32 * 1 + 8 * 2); + + // Missing decorations -> returns nullopt + Struct s_no_deco({&i32, &f64}); + EXPECT_FALSE(s_no_deco.GetByteOffset({1}).has_value()); + + Array arr_no_deco(&i32, len_info); + EXPECT_FALSE(arr_no_deco.GetByteOffset({2}).has_value()); +} + TEST(Types, IntSignedness) { std::vector<bool> signednesses = {true, false, false, true}; std::vector<std::unique_ptr<Integer>> types; @@ -364,6 +409,20 @@ } } +TEST(Types, FloatFPEncoding) { + std::vector<spv::FPEncoding> encodings = { + spv::FPEncoding::BFloat16KHR, + spv::FPEncoding::Max, + }; + std::vector<std::unique_ptr<Float>> types; + for (spv::FPEncoding encoding : encodings) { + types.emplace_back(new Float(16, encoding)); + } + for (size_t i = 0; i < encodings.size(); i++) { + EXPECT_EQ(encodings[i], types[i]->encoding()); + } +} + TEST(Types, VectorElementCount) { auto s32 = MakeUnique<Integer>(32, true); for (uint32_t c : {2, 3, 4}) { @@ -443,6 +502,15 @@ } } +TEST(Types, UntypedPointer) { + std::unique_ptr<Type> type(new Pointer(nullptr, spv::StorageClass::Uniform)); + const auto untyped = type->AsPointer(); + EXPECT_NE(untyped, nullptr); + EXPECT_TRUE(untyped->is_untyped()); + EXPECT_EQ(untyped->pointee_type(), nullptr); + EXPECT_EQ(untyped->storage_class(), spv::StorageClass::Uniform); +} + } // namespace } // namespace analysis } // namespace opt
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp index d213b8b..9c92104 100644 --- a/test/opt/upgrade_memory_model_test.cpp +++ b/test/opt/upgrade_memory_model_test.cpp
@@ -434,6 +434,394 @@ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); } +TEST_F(UpgradeMemoryModelTest, VariablePointerStoreToFunctionAndPrivate) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK-NOT: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer {{%\w+}} +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %void %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_Private__ptr_StorageBuffer__struct_4 = OpTypePointer Private %_ptr_StorageBuffer__struct_4 +%var_bufPtr_pri = OpVariable %_ptr_Private__ptr_StorageBuffer__struct_4 Private +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %void None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%bb_entry = OpLabel +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +OpStore %param_buf %buf +OpStore %var_bufPtr_pri %buf +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectLoad) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[buffer:%\w+]] = OpLoad {{%\w+}} {{%\w+}} +; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %uint None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +%offset = OpLoad %uint %param_offset +%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset +%data = OpLoad %uint %ld_gep +OpReturnValue %data +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectLoadWithPrivateAS) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer:%\w+]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%_ptr_Private__ptr_StorageBuffer__struct_4 = OpTypePointer Private %_ptr_StorageBuffer__struct_4 +%var_bufPtr_pri = OpVariable %_ptr_Private__ptr_StorageBuffer__struct_4 Private +%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %uint None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf0 = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +OpStore %var_bufPtr_pri %buf0 +%offset = OpLoad %uint %param_offset +%buf1 = OpLoad %_ptr_StorageBuffer__struct_4 %var_bufPtr_pri +%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf1 %uint_0 %offset +%data = OpLoad %uint %ld_gep +OpReturnValue %data +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectStore) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[st_gep:%\w+]] = OpAccessChain +; CHECK: OpStore [[st_gep]] {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%void = OpTypeVoid +%uint_0 = OpConstant %uint 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %void %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %void None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +%offset = OpLoad %uint %param_offset +%st_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset +OpStore %st_gep %uint_0 +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectStoreWithPrivateAS) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[st_gep:%\w+]] = OpAccessChain +; CHECK: OpStore [[st_gep]] {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%void = OpTypeVoid +%uint_0 = OpConstant %uint 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%_ptr_Private__ptr_StorageBuffer__struct_4 = OpTypePointer Private %_ptr_StorageBuffer__struct_4 +%var_bufPtr_pri = OpVariable %_ptr_Private__ptr_StorageBuffer__struct_4 Private +%functy = OpTypeFunction %void %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %void None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf0 = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +OpStore %var_bufPtr_pri %buf0 +%offset = OpLoad %uint %param_offset +%buf1 = OpLoad %_ptr_StorageBuffer__struct_4 %var_bufPtr_pri +%st_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf1 %uint_0 %offset +OpStore %st_gep %uint_0 +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectSelect) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[buffer:%\w+]] = OpSelect {{%\w+}} {{%\w+}} +; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%null = OpConstantNull %_ptr_StorageBuffer__struct_4 +%func = OpFunction %uint None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +%select = OpSelect %_ptr_StorageBuffer__struct_4 %true %buf %null +%offset = OpLoad %uint %param_offset +%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %select %uint_0 %offset +%data = OpLoad %uint %ld_gep +OpReturnValue %data +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectCopied) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[buffer:%\w+]] = OpLoad {{%\w+}} {{%\w+}} +; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %uint None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%copy = OpCopyObject %_ptr_Function__ptr_StorageBuffer__struct_4 %param_buf +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %copy +%offset = OpLoad %uint %param_offset +%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset +%data = OpLoad %uint %ld_gep +OpReturnValue %data +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectPhi) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[buffer:%\w+]] = OpPhi {{%\w+}} {{%\w+}} +; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpMemberDecorate %_struct_4 0 Coherent +OpDecorate %_struct_4 Block +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%null = OpConstantNull %_ptr_StorageBuffer__struct_4 +%func = OpFunction %uint None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +OpSelectionMerge %bb_end None +OpBranchConditional %true %bb_then %bb_else +%bb_then = OpLabel +OpBranch %bb_end +%bb_else = OpLabel +OpBranch %bb_end +%bb_end = OpLabel +%phi_buf_ptr = OpPhi %_ptr_StorageBuffer__struct_4 %buf %bb_then %null %bb_else +%offset = OpLoad %uint %param_offset +%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %phi_buf_ptr %uint_0 %offset +%data = OpLoad %uint %ld_gep +OpReturnValue %data +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectFuncCall) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[buffer:%\w+]] = OpLoad {{%\w+}} {{%\w+}} +; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: [[buffer2:%\w+]] = OpLoad {{%\w+}} {{%\w+}} Volatile +; CHECK: [[ld_gep2:%\w+]] = OpAccessChain {{%\w+}} [[buffer2]] {{%\w+}} {{%\w+}} +; CHECK: OpLoad {{%\w+}} [[ld_gep2]] Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpMemberDecorate %_struct_4 0 Offset 0 +OpDecorate %_struct_4 Block +OpDecorate %param_buf Coherent +OpDecorate %param_buf_1 Coherent +OpDecorate %param_buf_1 Volatile +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1024 = OpConstant %uint 1024 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_4 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4 +%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint +%local_functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%func = OpFunction %uint None %functy +%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%param_offset = OpFunctionParameter %_ptr_Function_uint +%bb_entry = OpLabel +%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf +%offset = OpLoad %uint %param_offset +%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset +%data = OpLoad %uint %ld_gep +%data_2 = OpFunctionCall %uint %local_func %param_buf +%ret = OpIAdd %uint %data %data_2 +OpReturnValue %ret +OpFunctionEnd +%local_func = OpFunction %uint None %local_functy +%param_buf_1 = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4 +%bb_entry_1 = OpLabel +%buf_1 = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf_1 +%ld_gep_1 = OpAccessChain %_ptr_StorageBuffer_uint %buf_1 %uint_0 %uint_1024 +%data_1 = OpLoad %uint %ld_gep_1 +OpReturnValue %data_1 +OpFunctionEnd +)"; + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + TEST_F(UpgradeMemoryModelTest, CoherentStructElement) { const std::string text = R"( ; CHECK-NOT: OpMemberDecorate @@ -1174,9 +1562,9 @@ TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierNoChange) { const std::string text = R"( -; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0 -; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 -; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]] +; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0{{\s*$}} +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]]{{\s*$}} OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %func "func" @@ -1195,11 +1583,11 @@ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); } -TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutput) { +TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierRelaxedNoChange) { const std::string text = R"( -; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 -; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096 -; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]] +; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0{{\s*$}} +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]]{{\s*$}} OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %func "func" %var @@ -1222,25 +1610,52 @@ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); } -TEST_F(UpgradeMemoryModelTest, TessellationMemoryBarrierNoChange) { +TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutput) { const std::string text = R"( -; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0 -; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 -; CHECK: OpMemoryBarrier [[workgroup]] [[none]] +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: [[acqrel_workgroup_output:%\w+]] = OpConstant {{%\w+}} 4360{{\s*$}} +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[acqrel_workgroup_output]]{{\s*$}} OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %func "func" %var %void = OpTypeVoid %int = OpTypeInt 32 0 -%none = OpConstant %int 0 %workgroup = OpConstant %int 2 +%acqrel_workgroup = OpConstant %int 264 %ptr_int_Output = OpTypePointer Output %int %var = OpVariable %ptr_int_Output Output %func_ty = OpTypeFunction %void %func = OpFunction %void None %func_ty %1 = OpLabel %ld = OpLoad %int %var -OpMemoryBarrier %workgroup %none +OpControlBarrier %workgroup %workgroup %acqrel_workgroup +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true); +} + +TEST_F(UpgradeMemoryModelTest, TessellationMemoryBarrierNoChange) { + const std::string text = R"( +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: [[acqrel_workgroup:%\w+]] = OpConstant {{%\w+}} 264{{\s*$}} +; CHECK: OpMemoryBarrier [[workgroup]] [[acqrel_workgroup]]{{\s*$}} +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %func "func" %var +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%workgroup = OpConstant %int 2 +%acqrel_workgroup = OpConstant %int 264 +%ptr_int_Output = OpTypePointer Output %int +%var = OpVariable %ptr_int_Output Output +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +OpMemoryBarrier %workgroup %acqrel_workgroup OpStore %var %ld OpReturn OpFunctionEnd @@ -1251,16 +1666,16 @@ TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutputSubFunction) { const std::string text = R"( -; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 -; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096 -; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]] +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: [[acqrel_workgroup_output:%\w+]] = OpConstant {{%\w+}} 4360{{\s*$}} +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[acqrel_workgroup_output]]{{\s*$}} OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %func "func" %var %void = OpTypeVoid %int = OpTypeInt 32 0 -%none = OpConstant %int 0 %workgroup = OpConstant %int 2 +%acqrel_workgroup = OpConstant %int 264 %ptr_int_Output = OpTypePointer Output %int %var = OpVariable %ptr_int_Output Output %func_ty = OpTypeFunction %void @@ -1272,7 +1687,7 @@ %sub_func = OpFunction %void None %func_ty %2 = OpLabel %ld = OpLoad %int %var -OpControlBarrier %workgroup %workgroup %none +OpControlBarrier %workgroup %workgroup %acqrel_workgroup OpStore %var %ld OpReturn OpFunctionEnd @@ -1284,16 +1699,16 @@ TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutputDifferentFunctions) { const std::string text = R"( -; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 -; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096 -; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]] +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: [[acqrel_workgroup_output:%\w+]] = OpConstant {{%\w+}} 4360{{\s*$}} +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[acqrel_workgroup_output]]{{\s*$}} OpCapability Tessellation OpMemoryModel Logical GLSL450 OpEntryPoint TessellationControl %func "func" %var %void = OpTypeVoid %int = OpTypeInt 32 0 -%none = OpConstant %int 0 %workgroup = OpConstant %int 2 +%acqrel_workgroup = OpConstant %int 264 %ptr_int_Output = OpTypePointer Output %int %var = OpVariable %ptr_int_Output Output %func_ty = OpTypeFunction %void @@ -1313,7 +1728,7 @@ OpFunctionEnd %barrier_func = OpFunction %void None %func_ty %3 = OpLabel -OpControlBarrier %workgroup %workgroup %none +OpControlBarrier %workgroup %workgroup %acqrel_workgroup OpReturn OpFunctionEnd %st_func = OpFunction %void None %st_func_ty @@ -1329,21 +1744,22 @@ TEST_F(UpgradeMemoryModelTest, ChangeControlBarrierMemoryScope) { std::string text = R"( -; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 -; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 -; CHECK: OpControlBarrier [[workgroup]] [[queuefamily]] +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}} +; CHECK: [[acqrel_workgroup:%\w+]] = OpConstant {{%\w+}} 264{{\s*$}} +; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5{{\s*$}} +; CHECK: OpControlBarrier [[workgroup]] [[queuefamily]] [[acqrel_workgroup]]{{\s*$}} OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %func "func" %void = OpTypeVoid %int = OpTypeInt 32 0 -%none = OpConstant %int 0 %device = OpConstant %int 1 %workgroup = OpConstant %int 2 +%acqrel_workgroup = OpConstant %int 264 %func_ty = OpTypeFunction %void %func = OpFunction %void None %func_ty %1 = OpLabel -OpControlBarrier %workgroup %device %none +OpControlBarrier %workgroup %device %acqrel_workgroup OpReturn OpFunctionEnd )"; @@ -1353,19 +1769,20 @@ TEST_F(UpgradeMemoryModelTest, ChangeMemoryBarrierMemoryScope) { std::string text = R"( -; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 -; CHECK: OpMemoryBarrier [[queuefamily]] +; CHECK: [[acqrel_workgroup:%\w+]] = OpConstant {{%\w+}} 264{{\s*$}} +; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5{{\s*$}} +; CHECK: OpMemoryBarrier [[queuefamily]] [[acqrel_workgroup]]{{\s*$}} OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %func "func" %void = OpTypeVoid %int = OpTypeInt 32 0 -%none = OpConstant %int 0 %device = OpConstant %int 1 +%acqrel_workgroup = OpConstant %int 264 %func_ty = OpTypeFunction %void %func = OpFunction %void None %func_ty %1 = OpLabel -OpMemoryBarrier %device %none +OpMemoryBarrier %device %acqrel_workgroup OpReturn OpFunctionEnd )";
diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp index 3d7aaad..c3be991 100644 --- a/test/opt/value_table_test.cpp +++ b/test/opt/value_table_test.cpp
@@ -25,7 +25,16 @@ using ::testing::HasSubstr; using ::testing::MatchesRegex; -using ValueTableTest = PassTest<::testing::Test>; + +struct ValueTableTest : public PassTest<::testing::Test> { + virtual void SetUp() override { + SetTargetEnv(SPV_ENV_UNIVERSAL_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + } +}; TEST_F(ValueTableTest, SameInstructionSameValue) { const std::string text = R"( @@ -724,9 +733,147 @@ ValueNumberTable vtable(context.get()); Instruction* load1 = context->get_def_use_mgr()->GetDef(17); Instruction* load2 = context->get_def_use_mgr()->GetDef(18); + // Considered the same because the underlying memory is read-only EXPECT_EQ(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2)); } +TEST_F(ValueTableTest, ImageRead_ConsideredDifferent) { + // Image reads are considered different because they can be read-write storage + // image. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %var "s" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %v2zero = OpConstantNull %v2int + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %i_ty = OpTypeImage %float 2D 0 0 0 2 Rgba32f + %ptr_s_ty = OpTypePointer UniformConstant %i_ty + %var = OpVariable %ptr_s_ty UniformConstant + %main = OpFunction %void None %6 + %15 = OpLabel + %16 = OpLoad %i_ty %var + %100 = OpImageRead %v4float %16 %v2zero + %101 = OpImageRead %v4float %16 %v2zero + OpReturn + OpFunctionEnd + )"; + auto context = AssembleModule(text); + ValueNumberTable vtable(context.get()); + Instruction* read1 = context->get_def_use_mgr()->GetDef(100); + Instruction* read2 = context->get_def_use_mgr()->GetDef(101); + ASSERT_NE(read1, nullptr); + ASSERT_NE(read2, nullptr); + EXPECT_NE(vtable.GetValueNumber(read1), vtable.GetValueNumber(read2)); +} + +TEST_F(ValueTableTest, LoadSampler_ConsideredDifferent) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %var "var" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %s_ty = OpTypeSampler + %ptr_s_ty = OpTypePointer UniformConstant %s_ty + %var = OpVariable %ptr_s_ty UniformConstant + %main = OpFunction %void None %6 + %15 = OpLabel + %100 = OpLoad %s_ty %var + %101 = OpLoad %s_ty %var + OpReturn + OpFunctionEnd + )"; + auto context = AssembleModule(text); + ValueNumberTable vtable(context.get()); + Instruction* load1 = context->get_def_use_mgr()->GetDef(100); + Instruction* load2 = context->get_def_use_mgr()->GetDef(101); + ASSERT_NE(load1, nullptr) << Disassemble(context->module()); + ASSERT_NE(load2, nullptr); + EXPECT_NE(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2)); +} + +TEST_F(ValueTableTest, LoadImage_ConsideredDifferent) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %var "var" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %ptr_i_ty = OpTypePointer UniformConstant %i_ty + %var = OpVariable %ptr_i_ty UniformConstant + %main = OpFunction %void None %6 + %15 = OpLabel + %100 = OpLoad %i_ty %var + %101 = OpLoad %i_ty %var + OpReturn + OpFunctionEnd + )"; + auto context = AssembleModule(text); + ValueNumberTable vtable(context.get()); + Instruction* load1 = context->get_def_use_mgr()->GetDef(100); + Instruction* load2 = context->get_def_use_mgr()->GetDef(101); + ASSERT_NE(load1, nullptr); + ASSERT_NE(load2, nullptr); + EXPECT_NE(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2)); +} + +TEST_F(ValueTableTest, LoadSampledImage_ConsideredDifferent) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpName %main "main" + OpName %var "var" + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %s_ty = OpTypeSampler + %float = OpTypeFloat 32 + %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown + %si_ty = OpTypeSampledImage %i_ty + %ptr_si_ty = OpTypePointer UniformConstant %si_ty + %var = OpVariable %ptr_si_ty UniformConstant + %main = OpFunction %void None %6 + %15 = OpLabel + %100 = OpLoad %si_ty %var + %101 = OpLoad %si_ty %var + OpReturn + OpFunctionEnd + )"; + auto context = AssembleModule(text); + ValueNumberTable vtable(context.get()); + Instruction* load1 = context->get_def_use_mgr()->GetDef(100); + Instruction* load2 = context->get_def_use_mgr()->GetDef(101); + ASSERT_NE(load1, nullptr); + ASSERT_NE(load2, nullptr); + EXPECT_NE(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2)); +} + TEST_F(ValueTableTest, DifferentDebugLocalVariableSameValue) { const std::string text = R"( OpCapability Shader
diff --git a/test/parse_number_test.cpp b/test/parse_number_test.cpp index c99205c..40bdd02 100644 --- a/test/parse_number_test.cpp +++ b/test/parse_number_test.cpp
@@ -898,6 +898,120 @@ EXPECT_EQ("Invalid 16-bit float literal: -1e400", err_msg); } +TEST(ParseAndEncodeFloat8_E4M3, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {8, SPV_NUMBER_FLOATING, SPV_FP_ENCODING_FLOAT8_E4M3}; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x80u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "1.0", type, [](uint32_t word) { EXPECT_EQ(0x38u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "2.5", type, [](uint32_t word) { EXPECT_EQ(0x42u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.625", type, [](uint32_t word) { EXPECT_EQ(0xB2u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Overflow + rc = + ParseAndEncodeFloatingPointNumber("1e38", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: 1e38", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e38", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: -1e38", err_msg); + rc = + ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: 1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: -1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E4M3 float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeFloat8_E5M2, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {8, SPV_NUMBER_FLOATING, SPV_FP_ENCODING_FLOAT8_E5M2}; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x80u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "1.0", type, [](uint32_t word) { EXPECT_EQ(0x3cu, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "2.5", type, [](uint32_t word) { EXPECT_EQ(0x41u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.625", type, [](uint32_t word) { EXPECT_EQ(0xB9u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Overflow + rc = + ParseAndEncodeFloatingPointNumber("1e38", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: 1e38", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e38", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: -1e38", err_msg); + rc = + ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: 1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: -1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid E5M2 float literal: -1e400", err_msg); +} + TEST(ParseAndEncodeFloatingPointNumber, TypeNone) { EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; std::string err_msg;
diff --git a/test/string_utils_test.cpp b/test/string_utils_test.cpp index 5851415..b4ce29f 100644 --- a/test/string_utils_test.cpp +++ b/test/string_utils_test.cpp
@@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <string> - -#include "gtest/gtest.h" #include "source/util/string_utils.h" + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/util/small_vector.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -186,6 +189,50 @@ EXPECT_EQ("1225th", CardinalToOrdinal(1225)); } +using MakeVectorMakeStringRoundTripTest = ::testing::TestWithParam<std::string>; + +TEST_P(MakeVectorMakeStringRoundTripTest, ViaStdVector) { + const std::string str = GetParam(); + const auto& vec = MakeVector<std::vector<uint32_t>>(str); + // Allow for the terminating null byte that must be present. + const auto expected_vec_len = (str.size() + 4) / 4; + EXPECT_EQ(vec.size(), expected_vec_len); + const std::string back_to_string = MakeString(vec); + EXPECT_EQ(str, back_to_string); +} + +TEST_P(MakeVectorMakeStringRoundTripTest, ViaSmallVector1) { + const std::string str = GetParam(); + // Allow for the terminating null byte that must be present. + const auto expected_vec_len = (str.size() + 4) / 4; + const auto& vec = MakeVector<spvtools::utils::SmallVector<uint32_t, 1>>(str); + EXPECT_EQ(vec.size(), expected_vec_len); + const std::string back_to_string = MakeString(vec); + EXPECT_EQ(str, back_to_string); +} + +TEST_P(MakeVectorMakeStringRoundTripTest, ViaSmallVector100) { + const std::string str = GetParam(); + // Allow for the terminating null byte that must be present. + const auto expected_vec_len = (str.size() + 4) / 4; + const auto& vec = + MakeVector<spvtools::utils::SmallVector<uint32_t, 100>>(str); + EXPECT_EQ(vec.size(), expected_vec_len); + const std::string back_to_string = MakeString(vec); + EXPECT_EQ(str, back_to_string); +} + +INSTANTIATE_TEST_SUITE_P(Examples, MakeVectorMakeStringRoundTripTest, + testing::ValuesIn(std::vector<std::string>{ + "", + "a", + "bc", + "def", + "ghij", + "klmno", + "dustclouds disappear without a trace", + })); + } // namespace } // namespace utils } // namespace spvtools
diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index faffa8a..05898df 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp
@@ -79,9 +79,16 @@ {"spv1.1", true, SPV_ENV_UNIVERSAL_1_1}, {"spv1.2", true, SPV_ENV_UNIVERSAL_1_2}, {"spv1.3", true, SPV_ENV_UNIVERSAL_1_3}, + {"spv1.4", true, SPV_ENV_UNIVERSAL_1_4}, + {"spv1.5", true, SPV_ENV_UNIVERSAL_1_5}, + {"spv1.6", true, SPV_ENV_UNIVERSAL_1_6}, + {"spv1.7", false, SPV_ENV_UNIVERSAL_1_3}, {"vulkan1.0", true, SPV_ENV_VULKAN_1_0}, {"vulkan1.1", true, SPV_ENV_VULKAN_1_1}, {"vulkan1.2", true, SPV_ENV_VULKAN_1_2}, + {"vulkan1.3", true, SPV_ENV_VULKAN_1_3}, + {"vulkan1.4", true, SPV_ENV_VULKAN_1_4}, + {"vulkan1.5", false, SPV_ENV_UNIVERSAL_1_0}, {"opencl2.1", true, SPV_ENV_OPENCL_2_1}, {"opencl2.2", true, SPV_ENV_OPENCL_2_2}, {"opengl4.0", true, SPV_ENV_OPENGL_4_0}, @@ -221,6 +228,7 @@ {" \t ; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1}, // Previous lines {"; SPIR-V\n; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1}, + {"; -\n; SPIR-V\n; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1}, // After a non-header line {"OpCapability Shader\n; Version: 1.1", false, kSentinelEnv}}));
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 59f2af9..f57d323 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp
@@ -31,12 +31,12 @@ using spvtest::Concatenate; using spvtest::MakeInstruction; -using utils::MakeVector; using spvtest::TextToBinaryTest; using ::testing::Combine; using ::testing::Eq; using ::testing::Values; using ::testing::ValuesIn; +using utils::MakeVector; // Returns a generator of common Vulkan environment values to be tested. std::vector<spv_target_env> CommonVulkanEnvs() { @@ -70,9 +70,15 @@ Eq("Import Id is being defined a second time")); } -TEST_F(TextToBinaryTest, TooManyArguments) { +TEST_F(TextToBinaryTest, TooManyArgumentsIdEqualQuote) { const std::string input = R"(%opencl = OpExtInstImport "OpenCL.std" - %2 = OpExtInst %float %opencl cos %x %oops")"; + %2 = OpExtInst %float %opencl cos %x %oops=")"; + EXPECT_THAT(CompileFailure(input), Eq("Expected '=', found end of stream.")); +} + +TEST_F(TextToBinaryTest, TooManyArgumentsIdEqual) { + const std::string input = R"(%opencl = OpExtInstImport "OpenCL.std" + %2 = OpExtInst %float %opencl cos %x %oops=)"; EXPECT_THAT(CompileFailure(input), Eq("Expected '=', found end of stream.")); } @@ -852,41 +858,42 @@ {"OpExtension \"SPV_NV_shader_subgroup_partitioned\"\n", MakeInstruction(spv::Op::OpExtension, MakeVector("SPV_NV_shader_subgroup_partitioned"))}, - {"OpCapability GroupNonUniformPartitionedNV\n", + {"OpCapability GroupNonUniformPartitionedEXT\n", MakeInstruction( spv::Op::OpCapability, - {(uint32_t)spv::Capability::GroupNonUniformPartitionedNV})}, + {(uint32_t)spv::Capability::GroupNonUniformPartitionedEXT})}, // Check the new capability's token number - {"OpCapability GroupNonUniformPartitionedNV\n", + {"OpCapability GroupNonUniformPartitionedEXT\n", MakeInstruction(spv::Op::OpCapability, {5297})}, - {"%2 = OpGroupNonUniformPartitionNV %1 %3\n", - MakeInstruction(spv::Op::OpGroupNonUniformPartitionNV, {1, 2, 3})}, + {"%2 = OpGroupNonUniformPartitionEXT %1 %3\n", + MakeInstruction(spv::Op::OpGroupNonUniformPartitionEXT, + {1, 2, 3})}, // Check the new instruction's token number - {"%2 = OpGroupNonUniformPartitionNV %1 %3\n", + {"%2 = OpGroupNonUniformPartitionEXT %1 %3\n", MakeInstruction(static_cast<spv::Op>(5296), {1, 2, 3})}, // Check the new group operations - {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n", + {"%2 = OpGroupIAdd %1 %3 PartitionedReduceEXT %4\n", MakeInstruction( spv::Op::OpGroupIAdd, - {1, 2, 3, (uint32_t)spv::GroupOperation::PartitionedReduceNV, + {1, 2, 3, (uint32_t)spv::GroupOperation::PartitionedReduceEXT, 4})}, - {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n", + {"%2 = OpGroupIAdd %1 %3 PartitionedReduceEXT %4\n", MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 6, 4})}, - {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n", + {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanEXT %4\n", MakeInstruction( spv::Op::OpGroupIAdd, {1, 2, 3, (uint32_t)spv::GroupOperation::PartitionedInclusiveScanNV, 4})}, - {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n", + {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanEXT %4\n", MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 7, 4})}, - {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n", + {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanEXT %4\n", MakeInstruction( spv::Op::OpGroupIAdd, {1, 2, 3, - (uint32_t)spv::GroupOperation::PartitionedExclusiveScanNV, + (uint32_t)spv::GroupOperation::PartitionedExclusiveScanEXT, 4})}, - {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n", + {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanEXT %4\n", MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 8, 4})}, }))); @@ -1376,5 +1383,302 @@ {1, 2, 3, 4, 5, 6, 7})}, }))); +// SPV_ARM_tensors +INSTANTIATE_TEST_SUITE_P( + SPV_ARM_tensors, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3, + SPV_ENV_OPENCL_2_1), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_ARM_tensors\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_ARM_tensors"))}, + {"OpCapability TensorsARM\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability::TensorsARM})}, + {"OpCapability StorageTensorArrayDynamicIndexingARM\n", + MakeInstruction( + spv::Op::OpCapability, + {(uint32_t) + spv::Capability::StorageTensorArrayDynamicIndexingARM})}, + {"OpCapability StorageTensorArrayNonUniformIndexingARM\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability:: + StorageTensorArrayNonUniformIndexingARM})}, + {"%1 = OpTypeTensorARM %2\n", + MakeInstruction(spv::Op::OpTypeTensorARM, {1, 2})}, + {"%1 = OpTypeTensorARM %2 %3\n", + MakeInstruction(spv::Op::OpTypeTensorARM, {1, 2, 3})}, + {"%1 = OpTypeTensorARM %2 %3 %4\n", + MakeInstruction(spv::Op::OpTypeTensorARM, {1, 2, 3, 4})}, + {"%2 = OpTensorReadARM %1 %3 %4\n", + MakeInstruction(spv::Op::OpTensorReadARM, {1, 2, 3, 4})}, + {"%2 = OpTensorReadARM %1 %3 %4 NoneARM\n", + MakeInstruction(spv::Op::OpTensorReadARM, + {1, 2, 3, 4, + (uint32_t)spv::TensorOperandsMask::MaskNone})}, + {"%2 = OpTensorReadARM %1 %3 %4 NontemporalARM\n", + MakeInstruction( + spv::Op::OpTensorReadARM, + {1, 2, 3, 4, + (uint32_t)spv::TensorOperandsMask::NontemporalARM})}, + {"%2 = OpTensorReadARM %1 %3 %4 OutOfBoundsValueARM %5\n", + MakeInstruction( + spv::Op::OpTensorReadARM, + {1, 2, 3, 4, + (uint32_t)spv::TensorOperandsMask::OutOfBoundsValueARM, 5})}, + {"%2 = OpTensorReadARM %1 %3 %4 MakeElementVisibleARM %5\n", + MakeInstruction( + spv::Op::OpTensorReadARM, + {1, 2, 3, 4, + (uint32_t)spv::TensorOperandsMask::MakeElementVisibleARM, + 5})}, + {"%2 = OpTensorReadARM %1 %3 %4 NonPrivateElementARM\n", + MakeInstruction( + spv::Op::OpTensorReadARM, + {1, 2, 3, 4, + (uint32_t)spv::TensorOperandsMask::NonPrivateElementARM})}, + {"OpTensorWriteARM %1 %2 %3\n", + MakeInstruction(spv::Op::OpTensorWriteARM, {1, 2, 3})}, + {"OpTensorWriteARM %1 %2 %3 NoneARM\n", + MakeInstruction(spv::Op::OpTensorWriteARM, + {1, 2, 3, + (uint32_t)spv::TensorOperandsMask::MaskNone})}, + {"OpTensorWriteARM %1 %2 %3 NontemporalARM\n", + MakeInstruction( + spv::Op::OpTensorWriteARM, + {1, 2, 3, (uint32_t)spv::TensorOperandsMask::NontemporalARM})}, + {"OpTensorWriteARM %1 %2 %3 MakeElementAvailableARM %4\n", + MakeInstruction( + spv::Op::OpTensorWriteARM, + {1, 2, 3, + (uint32_t)spv::TensorOperandsMask::MakeElementAvailableARM, + 4})}, + {"OpTensorWriteARM %1 %2 %3 NonPrivateElementARM\n", + MakeInstruction( + spv::Op::OpTensorWriteARM, + {1, 2, 3, + (uint32_t)spv::TensorOperandsMask::NonPrivateElementARM})}, + {"%2 = OpTensorQuerySizeARM %1 %3 %4\n", + MakeInstruction(spv::Op::OpTensorQuerySizeARM, {1, 2, 3, 4})}, + }))); + +// SPV_EXT_float8 +INSTANTIATE_TEST_SUITE_P( + SPV_EXT_float8, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3, + SPV_ENV_OPENCL_2_1), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_EXT_float8\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_EXT_float8"))}, + {"OpCapability Float8EXT\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability::Float8EXT})}, + {"OpCapability Float8CooperativeMatrixEXT\n", + MakeInstruction( + spv::Op::OpCapability, + {(uint32_t)spv::Capability::Float8CooperativeMatrixEXT})}, + {"%1 = OpTypeFloat 8 Float8E4M3EXT\n", + MakeInstruction(spv::Op::OpTypeFloat, + {1, 8, (uint32_t)spv::FPEncoding::Float8E4M3EXT})}, + {"%1 = OpTypeFloat 8 Float8E5M2EXT\n", + MakeInstruction(spv::Op::OpTypeFloat, + {1, 8, (uint32_t)spv::FPEncoding::Float8E5M2EXT})}, + {"OpDecorate %1 SaturatedToLargestFloat8NormalConversionEXT\n", + MakeInstruction( + spv::Op::OpDecorate, + {1, + uint32_t(spv::Decoration:: + SaturatedToLargestFloat8NormalConversionEXT)})}, + }))); + +// SPV_INTEL_function_variants +// https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_function_variants.asciidoc +INSTANTIATE_TEST_SUITE_P( + SPV_INTEL_function_variants, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_INTEL_function_variants\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_INTEL_function_variants"))}, + {"OpCapability SpecConditionalINTEL\n", + MakeInstruction( + spv::Op::OpCapability, + {(uint32_t)spv::Capability::SpecConditionalINTEL})}, + {"OpCapability FunctionVariantsINTEL\n", + MakeInstruction( + spv::Op::OpCapability, + {(uint32_t)spv::Capability::FunctionVariantsINTEL})}, + {"OpDecorate %1 ConditionalINTEL %2\n", + MakeInstruction(spv::Op::OpDecorate, + {1, (uint32_t)spv::Decoration::ConditionalINTEL, + 2})}, + + {"OpConditionalExtensionINTEL %1 \"foo\"\n", + MakeInstruction(spv::Op::OpConditionalExtensionINTEL, {1}, + MakeVector("foo"))}, + + {"OpConditionalEntryPointINTEL %1 Kernel %2 \"foo\"\n", + MakeInstruction(spv::Op::OpConditionalEntryPointINTEL, + {1, (uint32_t)spv::ExecutionModel::Kernel, 2}, + MakeVector("foo"))}, + + {"OpConditionalCapabilityINTEL %1 Kernel\n", + MakeInstruction(spv::Op::OpConditionalCapabilityINTEL, + {1, (uint32_t)spv::ExecutionModel::Kernel})}, + + {"%2 = OpSpecConstantTargetINTEL %1 42\n", + MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, {1, 2, 42})}, + + {"%2 = OpSpecConstantTargetINTEL %1 42 99\n", + MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, + {1, 2, 42, 99})}, + + {"%2 = OpSpecConstantTargetINTEL %1 42 99 108\n", + MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, + {1, 2, 42, 99, 108})}, + + {"%2 = OpSpecConstantArchitectureINTEL %1 42 99 108 72\n", + MakeInstruction(spv::Op::OpSpecConstantArchitectureINTEL, + {1, 2, 42, 99, 108, 72})}, + + {"%2 = OpSpecConstantCapabilitiesINTEL %1\n", + MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, {1, 2})}, + + {"%2 = OpSpecConstantCapabilitiesINTEL %1 Kernel\n", + MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, + {1, 2, (uint32_t)spv::Capability::Kernel})}, + + {"%2 = OpSpecConstantCapabilitiesINTEL %1 Kernel Shader\n", + MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, + {1, 2, (uint32_t)spv::Capability::Kernel, + (uint32_t)spv::Capability::Shader})}, + + {"%2 = OpConditionalCopyObjectINTEL %1 %3 %4\n", + MakeInstruction(spv::Op::OpConditionalCopyObjectINTEL, + {1, 2, 3, 4})}, + + {"%2 = OpConditionalCopyObjectINTEL %1 %3 %4 %5 %6\n", + MakeInstruction(spv::Op::OpConditionalCopyObjectINTEL, + {1, 2, 3, 4, 5, 6})}, + + }))); + +// SPV_ARM_graph +INSTANTIATE_TEST_SUITE_P( + SPV_ARM_graph, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, + SPV_ENV_VULKAN_1_3, SPV_ENV_OPENCL_2_1), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_ARM_graph\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_ARM_graph"))}, + {"OpCapability GraphARM\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability::GraphARM})}, + {"%1 = OpTypeGraphARM 1 %2 %3\n", + MakeInstruction(spv::Op::OpTypeGraphARM, {1, 1, 2, 3})}, + {"%2 = OpGraphConstantARM %1 42\n", + MakeInstruction(spv::Op::OpGraphConstantARM, {1, 2, 42})}, + {"%2 = OpGraphARM %1\n", + MakeInstruction(spv::Op::OpGraphARM, {1, 2})}, + {"OpGraphEndARM\n", + MakeInstruction(spv::Op::OpGraphEndARM, {})}, + {"%2 = OpGraphInputARM %1 %3\n", + MakeInstruction(spv::Op::OpGraphInputARM, {1, 2, 3})}, + {"%2 = OpGraphInputARM %1 %3 %4\n", + MakeInstruction(spv::Op::OpGraphInputARM, {1, 2, 3, 4})}, + {"OpGraphSetOutputARM %1 %2\n", + MakeInstruction(spv::Op::OpGraphSetOutputARM, {1, 2})}, + {"OpGraphSetOutputARM %1 %2 %3\n", + MakeInstruction(spv::Op::OpGraphSetOutputARM, {1, 2, 3})}, + }))); + +// SPV_KHR_abort +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_abort, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, + SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_abort\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_KHR_abort"))}, + {"OpCapability AbortKHR\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability::AbortKHR})}, + {"OpAbortKHR %1 %2\n", + MakeInstruction(spv::Op::OpAbortKHR, {1, 2})}, + }))); + +// SPV_KHR_constant_data +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_constant_data, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_constant_data\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_KHR_constant_data"))}, + {"OpCapability ConstantDataKHR\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability::ConstantDataKHR})}, + {"%2 = OpConstantDataKHR %1 1718578944\n", + MakeInstruction(spv::Op::OpConstantDataKHR, {1, 2, 0x666F6F00})}, + {"%2 = OpSpecConstantDataKHR %1 1718578944\n", + MakeInstruction(spv::Op::OpSpecConstantDataKHR, + {1, 2, 0x666F6F00})}, + {"OpDecorate %1 UTFEncodedKHR\n", + MakeInstruction(spv::Op::OpDecorate, + {1, (uint32_t)spv::Decoration::UTFEncodedKHR})}, + + }))); + +TEST_F(TextToBinaryTest, ConstantDataNonUTF) { + const std::string source = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int8 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %char = OpTypeInt 8 1 + %uint_20 = OpConstant %uint 20 + %char_array = OpTypeArray %char %uint_20 + ; "abcd" where "a" is 0x61 ascii + %data = OpConstantDataKHR %char_array 0x10203040 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + spv_binary binary = nullptr; + EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, source.c_str(), source.size(), + &binary, nullptr)); + + // Opcode for OpConstantDataKHR + EXPECT_EQ((binary->code[50] & 0x0ffffu), 5147); + EXPECT_EQ(((binary->code[53] & 0xff000000u) >> 24), 0x10); + EXPECT_EQ(((binary->code[53] & 0x00ff0000u) >> 16), 0x20); + EXPECT_EQ(((binary->code[53] & 0x0000ff00u) >> 8), 0x30); + EXPECT_EQ(((binary->code[53] & 0x000000ffu)), 0x40); + + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + } // namespace } // namespace spvtools
diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp index 770f298..ff9f654 100644 --- a/test/text_to_binary.type_declaration_test.cpp +++ b/test/text_to_binary.type_declaration_test.cpp
@@ -278,6 +278,63 @@ Eq("Expected id to start with %.")); } +struct FloatEncodingWidthCase { + std::string input; + bool expect_pass; +}; + +using FloatEncodingWidthTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam<FloatEncodingWidthCase>>; + +TEST_P(FloatEncodingWidthTest, Samples) { + const auto& param = GetParam(); + if (param.expect_pass) { + CompileSuccessfully(param.input); + } else { + auto err = CompileFailure(param.input); + EXPECT_THAT(err, testing::HasSubstr("Invalid bit width")); + EXPECT_THAT(err, testing::HasSubstr("for floating point encoding")); + } +} + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryFloatWidth, FloatEncodingWidthTest, + ::testing::ValuesIn(std::vector<FloatEncodingWidthCase>{ + {"%1 = OpTypeFloat 32", true}, + {"%1 = OpTypeFloat 64", true}, + {"%1 = OpTypeFloat 16", true}, + {"%1 = OpTypeFloat 8", true}, + // bfloat16 + {"%1 = OpTypeFloat 0 BFloat16KHR", false}, + {"%1 = OpTypeFloat 1 BFloat16KHR", false}, + {"%1 = OpTypeFloat 15 BFloat16KHR", false}, + {"%1 = OpTypeFloat 16 BFloat16KHR", true}, + {"%1 = OpTypeFloat 17 BFloat16KHR", false}, + {"%1 = OpTypeFloat 32 BFloat16KHR", false}, + {"%1 = OpTypeFloat 64 BFloat16KHR", false}, + {"%1 = OpTypeFloat 100 BFloat16KHR", false}, + // fp8 E5M2 + {"%1 = OpTypeFloat 0 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 1 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 7 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 8 Float8E5M2EXT", true}, + {"%1 = OpTypeFloat 9 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 16 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 32 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 64 Float8E5M2EXT", false}, + {"%1 = OpTypeFloat 100 Float8E4M3EXT", false}, + // fp8 E4M3 + {"%1 = OpTypeFloat 0 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 1 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 7 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 8 Float8E4M3EXT", true}, + {"%1 = OpTypeFloat 9 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 16 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 32 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 64 Float8E4M3EXT", false}, + {"%1 = OpTypeFloat 100 Float8E4M3EXT", false}, + })); + // TODO(dneto): OpTypeVoid // TODO(dneto): OpTypeBool // TODO(dneto): OpTypeInt
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index 52a43c5..2c7d7ca 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py
@@ -113,7 +113,7 @@ 'remove-duplicates', 'replace-invalid-opcode', 'ssa-rewrite', - 'scalar-replacement=100', + 'scalar-replacement=0', 'scalar-replacement=42', 'strength-reduction', 'strip-debug', @@ -148,7 +148,7 @@ 'eliminate-local-single-block', 'eliminate-local-single-store', 'eliminate-dead-code-aggressive', - 'scalar-replacement=100', + 'scalar-replacement=0', 'convert-local-access-chains', 'eliminate-local-single-block', 'eliminate-local-single-store', @@ -162,7 +162,7 @@ 'redundancy-elimination', 'combine-access-chains', 'simplify-instructions', - 'scalar-replacement=100', + 'scalar-replacement=0', 'convert-local-access-chains', 'eliminate-local-single-block', 'eliminate-local-single-store', @@ -259,7 +259,9 @@ 'eliminate-local-single-store', 'eliminate-dead-code-aggressive', 'ssa-rewrite', + 'combine-access-chains', 'eliminate-dead-code-aggressive', + 'legalize-multidim-array', 'ccp', 'loop-unroll', 'eliminate-dead-branches',
diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt index 20038f7..c5cbc17 100644 --- a/test/util/CMakeLists.txt +++ b/test/util/CMakeLists.txt
@@ -17,6 +17,8 @@ bit_vector_test.cpp bitutils_test.cpp hash_combine_test.cpp + index_range_test.cpp small_vector_test.cpp + span_test.cpp LIBS SPIRV-Tools-opt )
diff --git a/test/util/index_range_test.cpp b/test/util/index_range_test.cpp new file mode 100644 index 0000000..0ae3954 --- /dev/null +++ b/test/util/index_range_test.cpp
@@ -0,0 +1,79 @@ +// Copyright (c) 2025 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/util/index_range.h" + +#include "gmock/gmock.h" + +namespace spvtools { +namespace utils { +namespace { + +using IndexRangeTest = ::testing::Test; + +using ushort = unsigned short; + +TEST(IndexRangeTest, Initialize_Default) { + double sentinel_a = 0.0; + double sentinel_b = 1.0; + const IndexRange<double, unsigned, ushort> ir; + + EXPECT_EQ(ir.first(), unsigned(0)); + EXPECT_EQ(ir.count(), ushort(0)); + EXPECT_TRUE(ir.empty()); + + auto span_null = ir.apply(nullptr); + EXPECT_EQ(span_null.data(), nullptr); + EXPECT_EQ(span_null.size(), 0); + EXPECT_TRUE(span_null.empty()); + + auto span_a = ir.apply(&sentinel_a); + EXPECT_EQ(span_a.data(), &sentinel_a); + EXPECT_EQ(span_a.size(), 0); + EXPECT_TRUE(span_a.empty()); + + auto span_b = ir.apply(&sentinel_b); + EXPECT_EQ(span_b.data(), &sentinel_b); + EXPECT_EQ(span_b.size(), 0); + EXPECT_TRUE(span_b.empty()); +} + +TEST(IndexRangeTest, Initialize_NonEmpty) { + const IndexRange<double, unsigned, ushort> ir(1, 2); + + EXPECT_EQ(ir.first(), unsigned(1)); + EXPECT_EQ(ir.count(), ushort(2)); + EXPECT_FALSE(ir.empty()); + + auto span_null = ir.apply(nullptr); + EXPECT_EQ(span_null.data(), nullptr); + EXPECT_EQ(span_null.size(), 0); + EXPECT_TRUE(span_null.empty()); + + double arr[] = {0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0}; + + auto span_a = ir.apply(arr); + EXPECT_EQ(span_a.begin(), arr + 1); + EXPECT_EQ(span_a.end(), arr + 3); + EXPECT_FALSE(span_a.empty()); + + auto span_b = ir.apply(arr + 3); + EXPECT_EQ(span_b.begin(), arr + 4); + EXPECT_EQ(span_b.end(), arr + 6); + EXPECT_FALSE(span_a.empty()); +} + +} // namespace +} // namespace utils +} // namespace spvtools
diff --git a/test/util/span_test.cpp b/test/util/span_test.cpp new file mode 100644 index 0000000..cfffa7a --- /dev/null +++ b/test/util/span_test.cpp
@@ -0,0 +1,223 @@ +// Copyright (c) 2025 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/util/span.h" + +#include "gmock/gmock.h" + +namespace spvtools { +namespace utils { +namespace { + +using SpanTest = ::testing::Test; + +TEST(SpanTest, Initialize_Default) { + const Span<int> s; + + EXPECT_EQ(s.begin(), nullptr); + EXPECT_EQ(s.end(), nullptr); + EXPECT_EQ(s.cbegin(), nullptr); + EXPECT_EQ(s.cend(), nullptr); + + EXPECT_EQ(s.size(), std::size_t(0)); + EXPECT_EQ(s.size_bytes(), std::size_t(0)); + EXPECT_TRUE(s.empty()); + + EXPECT_EQ(s.data(), nullptr); +} + +TEST(SpanTest, Initialize_EmptySpan) { + int ints[3]; + int* first = ints + 1; + const Span<int> s(first, 0); + + EXPECT_EQ(s.begin(), first); + EXPECT_EQ(s.end(), first); + EXPECT_EQ(s.begin(), s.end()); + EXPECT_EQ(s.cbegin(), first); + EXPECT_EQ(s.cend(), first); + EXPECT_EQ(s.cbegin(), s.cend()); + + EXPECT_EQ(s.size(), std::size_t(0)); + EXPECT_EQ(s.size_bytes(), std::size_t(0)); + EXPECT_TRUE(s.empty()); + + EXPECT_EQ(s.data(), first); +} + +TEST(SpanTest, Initialize_NonemptySpan) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 3); + + EXPECT_EQ(s.begin(), first); + EXPECT_EQ(s.end(), first + 3); + EXPECT_NE(s.begin(), s.end()); + EXPECT_EQ(s.cbegin(), first); + EXPECT_EQ(s.cend(), first + 3); + EXPECT_NE(s.cbegin(), s.cend()); + + EXPECT_EQ(s.size(), std::size_t(3)); + EXPECT_EQ(s.size_bytes(), 3 * sizeof(int)); + EXPECT_FALSE(s.empty()); + + EXPECT_EQ(&(s.front()), first); + EXPECT_EQ(s.front(), 20); + EXPECT_EQ(&(s.back()), first + 2); + EXPECT_EQ(s.back(), 40); + + EXPECT_EQ(s.data(), first); + EXPECT_EQ(&s[0], first); + EXPECT_EQ(s[0], 20); + EXPECT_EQ(&s[1], first + 1); + EXPECT_EQ(s[1], 30); + EXPECT_EQ(&s[2], first + 2); + EXPECT_EQ(s[2], 40); + EXPECT_EQ(&s[3], first + 3); + EXPECT_EQ(&s[3], s.end()); +} + +TEST(SpanTest, Initialize_NonemptySpan_Iterator_PostIncrement) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 3); + + auto iter = s.begin(); + EXPECT_NE(iter, s.end()); + EXPECT_EQ(*iter++, 20); + + EXPECT_NE(iter, s.end()); + EXPECT_EQ(*iter++, 30); + + EXPECT_NE(iter, s.end()); + EXPECT_EQ(*iter++, 40); + + EXPECT_EQ(iter, s.end()); +} + +TEST(SpanTest, Initialize_NonemptySpan_Iterator_PreIncrement) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 3); + + auto iter = s.begin(); + EXPECT_EQ(*++iter, 30); + + EXPECT_NE(iter, s.end()); + EXPECT_EQ(*++iter, 40); + + EXPECT_EQ(++iter, s.end()); +} + +TEST(SpanTest, Initialize_NonemptySpan_Iterator_PostDecrement) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 3); + + auto iter = s.end(); + EXPECT_EQ(iter--, s.end()); + EXPECT_EQ(*iter--, 40); + + EXPECT_NE(iter, s.begin()); + EXPECT_EQ(*iter--, 30); + + EXPECT_EQ(iter, s.begin()); + EXPECT_EQ(*iter--, 20); + + EXPECT_NE(iter, s.begin()); +} + +TEST(SpanTest, Initialize_NonemptySpan_Iterator_PreDecrement) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 3); + + auto iter = s.end(); + EXPECT_EQ(*--iter, 40); + + EXPECT_NE(iter, s.begin()); + EXPECT_EQ(*--iter, 30); + + EXPECT_NE(iter, s.begin()); + EXPECT_EQ(*--iter, 20); + + EXPECT_EQ(iter, s.begin()); +} + +TEST(SpanTest, Subspan_FromNil) { + const Span<int> snil(nullptr, 0); + + const auto s0 = snil.subspan(0); + const auto s2 = snil.subspan(2); + + EXPECT_EQ(s0.begin(), nullptr); + EXPECT_EQ(s0.end(), nullptr); + EXPECT_EQ(s0.size(), 0u); + EXPECT_TRUE(s0.empty()); + + EXPECT_EQ(s2.begin(), nullptr); + EXPECT_EQ(s2.end(), nullptr); + EXPECT_EQ(s2.size(), 0u); + EXPECT_TRUE(s2.empty()); +} + +TEST(SpanTest, Subspan_FromEmpty) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 0); + + const auto s0 = s.subspan(0); + const auto s2 = s.subspan(2); + + EXPECT_EQ(s0.begin(), nullptr); + EXPECT_EQ(s0.end(), nullptr); + EXPECT_EQ(s0.size(), 0u); + EXPECT_TRUE(s0.empty()); + + EXPECT_EQ(s2.begin(), nullptr); + EXPECT_EQ(s2.end(), nullptr); + EXPECT_EQ(s2.size(), 0u); + EXPECT_TRUE(s2.empty()); +} + +TEST(SpanTest, Subspan_FromNonEmpty) { + int ints[10] = {0, 10, 20, 30, 40, 50, 60}; + int* first = ints + 2; + const Span<int> s(first, 3); + + const auto s0 = s.subspan(0); + const auto s2 = s.subspan(2); + const auto s3 = s.subspan(3); + const auto s4 = s.subspan(4); + + EXPECT_EQ(s0.begin(), s.begin()); + EXPECT_EQ(s0.end(), s.end()); + EXPECT_EQ(s0.size(), 3u); + + EXPECT_EQ(s2.begin(), s.begin() + 2); + EXPECT_EQ(s2.end(), s.end()); + EXPECT_EQ(s2.size(), 1u); + + EXPECT_EQ(s3.begin(), nullptr); + EXPECT_EQ(s3.end(), nullptr); + EXPECT_EQ(s3.size(), 0u); + + EXPECT_EQ(s4.begin(), nullptr); + EXPECT_EQ(s4.end(), nullptr); + EXPECT_EQ(s4.size(), 0u); +} + +} // namespace +} // namespace utils +} // namespace spvtools
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 9d6f6ea..550a5b2 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt
@@ -36,9 +36,12 @@ val_data_test.cpp val_decoration_test.cpp val_derivatives_test.cpp + val_dot_product_test.cpp val_entry_point_test.cpp val_explicit_reserved_test.cpp + val_invalid_type_test.cpp val_extensions_test.cpp + val_extension_spv_khr_abort_test.cpp val_extension_spv_khr_expect_assume_test.cpp val_extension_spv_khr_linkonce_odr_test.cpp val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp @@ -47,6 +50,10 @@ val_extension_spv_khr_terminate_invocation_test.cpp val_extension_spv_khr_subgroup_rotate_test.cpp val_extension_spv_nv_raw_access_chains.cpp + val_extension_spv_intel_arbitrary_precision_integers_test.cpp + val_extension_spv_intel_function_variants.cpp + val_extension_spv_intel_inline_assembly.cpp + val_extension_spv_ext_descriptor_heap.cpp val_ext_inst_test.cpp val_ext_inst_debug_test.cpp ${VAL_TEST_COMMON_SRCS} @@ -71,19 +78,24 @@ add_spvtools_unittest(TARGET val_fghijklmnop SRCS val_function_test.cpp + val_graph_test.cpp + val_group_test.cpp val_id_test.cpp val_image_test.cpp val_interfaces_test.cpp val_layout_test.cpp val_literals_test.cpp + val_logical_pointers_test.cpp val_logicals_test.cpp val_memory_test.cpp + val_memory_semantics_test.cpp val_mesh_shading_test.cpp val_misc_test.cpp val_modes_test.cpp val_non_semantic_test.cpp val_non_uniform_test.cpp val_opencl_test.cpp + val_pipe_test.cpp val_primitives_test.cpp ${VAL_TEST_COMMON_SRCS} LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} @@ -99,6 +111,7 @@ val_ssa_test.cpp val_state_test.cpp val_storage_test.cpp + val_tensor_test.cpp val_type_unique_test.cpp val_validation_state_test.cpp val_version_test.cpp
diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp index 8b2a8d0..445c579 100644 --- a/test/val/val_arithmetics_test.cpp +++ b/test/val/val_arithmetics_test.cpp
@@ -1166,6 +1166,243 @@ "vector size of the right operand: OuterProduct")); } +std::string GenerateBFloatCode(const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability BFloat16TypeKHR +OpCapability BFloat16DotProductKHR +OpCapability BFloat16CooperativeMatrixKHR +OpExtension "SPV_KHR_bfloat16" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bfloat16 = OpTypeFloat 16 BFloat16KHR +%_ptr_Function_bfloat16 = OpTypePointer Function %bfloat16 +%v2bfloat16 = OpTypeVector %bfloat16 2 +%_ptr_Function_v2bfloat16 = OpTypePointer Function %v2bfloat16 +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + main_body + suffix; +} + +TEST_F(ValidateArithmetics, DotBfloat16) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_v2bfloat16 Function +%v2 = OpVariable %_ptr_Function_v2bfloat16 Function +%12 = OpLoad %v2bfloat16 %v1 +%14 = OpLoad %v2bfloat16 %v2 +%15 = OpDot %bfloat16 %12 %14 +)"; + + CompileSuccessfully(GenerateBFloatCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +std::string GenerateFmaCode(const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float16 +OpCapability Float64 +OpCapability FMAKHR +OpExtension "SPV_KHR_fma" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%f64 = OpTypeFloat 64 +%f16vec2 = OpTypeVector %f16 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%f16vec3 = OpTypeVector %f16 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%f16vec4 = OpTypeVector %f16 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 + +%f16_1 = OpConstant %f16 1 +%f16_2 = OpConstant %f16 2 +%f16_4 = OpConstant %f16 4 + +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_4 = OpConstant %f32 4 + +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_4 = OpConstant %f64 4 + +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_4 = OpConstant %u32 4 + +%f16vec2_12 = OpConstantComposite %f16vec2 %f16_1 %f16_2 +%f16vec2_24 = OpConstantComposite %f16vec2 %f16_2 %f16_4 +%f16vec3_124 = OpConstantComposite %f16vec3 %f16_1 %f16_2 %f16_4 +%f16vec4_1241 = OpConstantComposite %f16vec4 %f16_1 %f16_2 %f16_4 %f16_1 + +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec2_24 = OpConstantComposite %f32vec2 %f32_2 %f32_4 +%f32vec3_124 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_4 +%f32vec4_1241 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_4 %f32_1 + +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec2_24 = OpConstantComposite %f64vec2 %f64_2 %f64_4 +%f64vec3_124 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_4 +%f64vec4_1241 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_4 %f64_1 + +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec2_24 = OpConstantComposite %u32vec2 %u32_2 %u32_4 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + main_body + suffix; +} + +TEST_F(ValidateArithmetics, FmaSuccess) { + const std::string body = R"( +%val1 = OpFmaKHR %f16 %f16_1 %f16_2 %f16_4 +%val2 = OpFmaKHR %f32 %f32_1 %f32_2 %f32_4 +%val3 = OpFmaKHR %f64 %f64_1 %f64_2 %f64_4 +%val4 = OpFmaKHR %f16vec2 %f16vec2_12 %f16vec2_24 %f16vec2_12 +%val5 = OpFmaKHR %f32vec2 %f32vec2_12 %f32vec2_24 %f32vec2_12 +%val6 = OpFmaKHR %f64vec2 %f64vec2_12 %f64vec2_24 %f64vec2_12 +%val7 = OpFmaKHR %f16vec3 %f16vec3_124 %f16vec3_124 %f16vec3_124 +%val8 = OpFmaKHR %f32vec3 %f32vec3_124 %f32vec3_124 %f32vec3_124 +%val9 = OpFmaKHR %f64vec3 %f64vec3_124 %f64vec3_124 %f64vec3_124 +%val10 = OpFmaKHR %f16vec4 %f16vec4_1241 %f16vec4_1241 %f16vec4_1241 +%val11 = OpFmaKHR %f32vec4 %f32vec4_1241 %f32vec4_1241 %f32vec4_1241 +%val12 = OpFmaKHR %f64vec4 %f64vec4_1241 %f64vec4_1241 %f64vec4_1241 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, FmaTypeIdU32) { + const std::string body = R"( +%val = OpFmaKHR %u32 %u32_1 %u32_2 %u32_4 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FmaKHR")); +} + +TEST_F(ValidateArithmetics, FmaTypeIdVec2U32) { + const std::string body = R"( +%val = OpFmaKHR %u32vec2 %u32vec2_12 %u32vec2_24 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FmaKHR")); +} + +TEST_F(ValidateArithmetics, FmaWrongOperand1) { + const std::string body = R"( +%val = OpFmaKHR %f32 %u32_1 %f32_2 %f32_4 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FmaKHR operand index 2")); +} + +TEST_F(ValidateArithmetics, FmaWrongOperand2) { + const std::string body = R"( +%val = OpFmaKHR %f32 %f32_1 %u32_2 %f32_4 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FmaKHR operand index 3")); +} + +TEST_F(ValidateArithmetics, FmaWrongOperand3) { + const std::string body = R"( +%val = OpFmaKHR %f32 %f32_1 %f32_2 %u32_4 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FmaKHR operand index 4")); +} + +TEST_F(ValidateArithmetics, FmaWrongVectorOperand1) { + const std::string body = R"( +%val = OpFmaKHR %f64vec3 %f32vec3_124 %f64vec3_124 %f64vec3_124 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FmaKHR operand index 2")); +} + +TEST_F(ValidateArithmetics, FmaWrongVectorOperand2) { + const std::string body = R"( +%val = OpFmaKHR %f32vec3 %f32vec3_124 %f64vec3_124 %f32vec3_124 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FmaKHR operand index 3")); +} + +TEST_F(ValidateArithmetics, FmaWrongVectorOperand3) { + const std::string body = R"( +%val = OpFmaKHR %f32vec3 %f32vec3_124 %f32vec3_124 %f64vec3_124 +)"; + + CompileSuccessfully(GenerateFmaCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FmaKHR operand index 4")); +} + std::string GenerateCoopMatCode(const std::string& extra_types, const std::string& main_body) { const std::string prefix = @@ -1176,6 +1413,7 @@ OpExtension "SPV_NV_cooperative_matrix" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 32 1 1 %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool @@ -1280,9 +1518,9 @@ TEST_F(ValidateArithmetics, CoopMatScopeFail) { const std::string types = R"( -%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 -%mat16x16_dv = OpTypeCooperativeMatrixNV %f16 %device %u32_16 %u32_16 +%mat16x16_dv = OpTypeCooperativeMatrixNV %f16 %workgroup %u32_16 %u32_16 %f16matdv_16x16_1 = OpConstantComposite %mat16x16_dv %f16_1 )"; @@ -1477,11 +1715,13 @@ OpCapability CooperativeMatrixKHR OpCapability CooperativeMatrixReductionsNV OpCapability CooperativeMatrixPerElementOperationsNV +OpCapability VulkanMemoryModel OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_NV_cooperative_matrix2" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical Vulkan OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 32 1 1 %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool @@ -1564,8 +1804,9 @@ %val18 = OpCooperativeMatrixMulAddKHR %u32matC %u32mat_A_1 %u32mat_B_1 %u32mat_C_1 )"; - CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateArithmetics, CoopMatMatrixKHRTimesScalarMismatchFail) { @@ -1573,8 +1814,10 @@ %val1 = OpMatrixTimesScalar %f16matA %f16mat_A_1 %f32_1 )"; - CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected scalar operand type to be equal to the component " @@ -1583,8 +1826,8 @@ TEST_F(ValidateArithmetics, CoopMatKHRScopeFail) { const std::string types = R"( -%device = OpConstant %u32 1 -%mat16x16_dv = OpTypeCooperativeMatrixKHR %f16 %device %u32_16 %u32_16 %useC +%workgroup = OpConstant %u32 2 +%mat16x16_dv = OpTypeCooperativeMatrixKHR %f16 %workgroup %u32_16 %u32_16 %useC %f16matdv_16x16_1 = OpConstantComposite %mat16x16_dv %f16_1 )"; @@ -1592,8 +1835,10 @@ %val1 = OpFAdd %f16matA %f16matdv_16x16_1 %f16mat_A_1 )"; - CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected scopes of Matrix and Result Type to be identical")); @@ -1609,8 +1854,10 @@ %val1 = OpCooperativeMatrixMulAddKHR %mat16x4 %f16mat_A_1 %f16mat_B_1 %mat16x4_C_1 )"; - CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Cooperative matrix 'N' mismatch: CooperativeMatrixMulAddKHR")); @@ -1641,8 +1888,9 @@ %val5 = OpCooperativeMatrixReduceNV %f16matC8 %f16mat_C_1 Row|Column %reducefunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateArithmetics, CoopMat2Reduce2x2DimFail) { @@ -1662,8 +1910,10 @@ %val1 = OpCooperativeMatrixReduceNV %f16matC %f16mat_C_1 2x2 %reducefunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For Reduce2x2, result rows/cols must be half of " "matrix rows/cols: CooperativeMatrixReduceNV")); @@ -1688,8 +1938,10 @@ %val1 = OpCooperativeMatrixReduceNV %f16matC8x16 %f16mat_C_1 Row %reducefunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For ReduceRow, result rows must match matrix rows: " "CooperativeMatrixReduceNV")); @@ -1714,8 +1966,10 @@ %val1 = OpCooperativeMatrixReduceNV %f16matC16x8 %f16mat_C_1 Column %reducefunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For ReduceColumn, result cols must match matrix cols: " "CooperativeMatrixReduceNV")); @@ -1740,8 +1994,10 @@ %val1 = OpCooperativeMatrixReduceNV %f16matC8 %f16mat_C_1 Row|Column|2x2 %reducefunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Reduce 2x2 must not be used with Row/Column: " "CooperativeMatrixReduceNV")); @@ -1764,8 +2020,10 @@ %val1 = OpCooperativeMatrixReduceNV %f16matC %f16mat_C_1 Row|Column %reducefunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("CombineFunc return type and parameters must match " "matrix component type: CooperativeMatrixReduceNV")); @@ -1800,8 +2058,9 @@ %val2 = OpCooperativeMatrixPerElementOpNV %f16matC %f16mat_C_1 %elemfunc2 %f16_1 )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateArithmetics, CoopMat2PerElementOpElemTyFail) { @@ -1822,8 +2081,9 @@ %val1 = OpCooperativeMatrixPerElementOpNV %f16matC %f16mat_C_1 %elemfunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("must match matrix component type")); } @@ -1846,11 +2106,173 @@ %val1 = OpCooperativeMatrixPerElementOpNV %f16matC %f16mat_C_1 %elemfunc )"; - CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a 32-bit integer")); } +std::string GenerateCoopVecCode(const std::string& extra_types, + const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpCapability ReplicatedCompositesEXT +OpExtension "SPV_NV_cooperative_vector" +OpExtension "SPV_EXT_replicated_composites" +%ext_inst = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +%u32_4 = OpConstant %u32 4 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8 +%f16vec4 = OpTypeCooperativeVectorNV %f16 %u32_4 +%u32vec = OpTypeCooperativeVectorNV %u32 %u32_8 +%s32vec = OpTypeCooperativeVectorNV %s32 %u32_8 + +%f16_1 = OpConstant %f16 1 +%f32_1 = OpConstant %f32 1 +%u32_1 = OpConstant %u32 1 +%s32_1 = OpConstant %s32 1 + +%f16vec4_1 = OpConstantComposite %f16vec4 %f16_1 %f16_1 %f16_1 %f16_1 +%f16vec_1 = OpConstantComposite %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 +%u32vec_1 = OpConstantComposite %u32vec %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 +%s32vec_1 = OpConstantComposite %s32vec %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 + +%u32_c1 = OpSpecConstant %u32 1 +%u32_c2 = OpSpecConstant %u32 2 + +%f16vecc = OpTypeCooperativeVectorNV %f16 %u32_c1 +%f16vecc_1 = OpConstantCompositeReplicateEXT %f16vecc %f16_1 +)"; + + const std::string func_begin = + R"( +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + extra_types + func_begin + main_body + suffix; +} + +TEST_F(ValidateArithmetics, CoopVecSuccess) { + const std::string body = R"( +%val1 = OpFAdd %f16vec %f16vec_1 %f16vec_1 +%val2 = OpFSub %f16vec %f16vec_1 %f16vec_1 +%val3 = OpFDiv %f16vec %f16vec_1 %f16vec_1 +%val4 = OpFNegate %f16vec %f16vec_1 +%val5 = OpIAdd %u32vec %u32vec_1 %u32vec_1 +%val6 = OpISub %u32vec %u32vec_1 %u32vec_1 +%val7 = OpUDiv %u32vec %u32vec_1 %u32vec_1 +%val8 = OpIAdd %s32vec %s32vec_1 %s32vec_1 +%val9 = OpISub %s32vec %s32vec_1 %s32vec_1 +%val10 = OpSDiv %s32vec %s32vec_1 %s32vec_1 +%val11 = OpSNegate %s32vec %s32vec_1 +%val12 = OpVectorTimesScalar %f16vec %f16vec_1 %f16_1 +%val13 = OpExtInst %f16vec %ext_inst FMin %f16vec_1 %f16vec_1 +%val14 = OpExtInst %f16vec %ext_inst FMax %f16vec_1 %f16vec_1 +%val15 = OpExtInst %f16vec %ext_inst FClamp %f16vec_1 %f16vec_1 %f16vec_1 +%val16 = OpExtInst %f16vec %ext_inst NClamp %f16vec_1 %f16vec_1 %f16vec_1 +%val17 = OpExtInst %f16vec %ext_inst Step %f16vec_1 %f16vec_1 +%val18 = OpExtInst %f16vec %ext_inst Exp %f16vec_1 +%val19 = OpExtInst %f16vec %ext_inst Log %f16vec_1 +%val20 = OpExtInst %f16vec %ext_inst Tanh %f16vec_1 +%val21 = OpExtInst %f16vec %ext_inst Atan %f16vec_1 +%val22 = OpExtInst %f16vec %ext_inst Fma %f16vec_1 %f16vec_1 %f16vec_1 +%val23 = OpExtInst %u32vec %ext_inst UMin %u32vec_1 %u32vec_1 +%val24 = OpExtInst %u32vec %ext_inst UMax %u32vec_1 %u32vec_1 +%val25 = OpExtInst %u32vec %ext_inst UClamp %u32vec_1 %u32vec_1 %u32vec_1 +%val26 = OpExtInst %s32vec %ext_inst SMin %s32vec_1 %s32vec_1 +%val27 = OpExtInst %s32vec %ext_inst SMax %s32vec_1 %s32vec_1 +%val28 = OpExtInst %s32vec %ext_inst SClamp %s32vec_1 %s32vec_1 %s32vec_1 +%val29 = OpShiftRightLogical %u32vec %u32vec_1 %u32vec_1 +%val30 = OpShiftRightArithmetic %u32vec %u32vec_1 %u32vec_1 +%val31 = OpShiftLeftLogical %u32vec %u32vec_1 %u32vec_1 +%val32 = OpBitwiseOr %u32vec %u32vec_1 %u32vec_1 +%val33 = OpBitwiseXor %u32vec %u32vec_1 %u32vec_1 +%val34 = OpBitwiseAnd %u32vec %u32vec_1 %u32vec_1 +%val35 = OpNot %u32vec %u32vec_1 +)"; + + CompileSuccessfully(GenerateCoopVecCode("", body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, CoopVecFMulPass) { + const std::string body = R"( +%val1 = OpFMul %f16vec %f16vec_1 %f16vec_1 +)"; + + CompileSuccessfully(GenerateCoopVecCode("", body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, CoopVecVectorTimesScalarMismatchFail) { + const std::string body = R"( +%val1 = OpVectorTimesScalar %f16vec %f16vec_1 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopVecCode("", body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar operand type to be equal to the component " + "type of the vector operand: VectorTimesScalar")); +} + +TEST_F(ValidateArithmetics, CoopVecDimFail) { + const std::string body = R"( +%val1 = OpFMul %f16vec %f16vec_1 %f16vec4_1 +)"; + + CompileSuccessfully(GenerateCoopVecCode("", body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of components to be identical")); +} + +TEST_F(ValidateArithmetics, CoopVecComponentTypeNotScalarNumeric) { + const std::string types = R"( +%bad = OpTypeCooperativeVectorNV %f16vec %u32_8 +)"; + + CompileSuccessfully(GenerateCoopVecCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeVectorIdEXT Component Type <id> " + "'14[%14]' is not a scalar numerical type.")); +} + +TEST_F(ValidateArithmetics, CoopVecDimNotConstantInt) { + const std::string types = R"( +%bad = OpTypeCooperativeVectorNV %f16 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopVecCode(types, "").c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeVectorIdEXT component count type <id> " + "'7[%float]' is not a 32-bit integer type")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index 1cec51e..c8727ed 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp
@@ -66,6 +66,8 @@ %acquire_release = OpConstant %u32 8 %acquire_and_release = OpConstant %u32 6 %sequentially_consistent = OpConstant %u32 16 +%acquire_uniform_workgroup = OpConstant %u32 322 +%release_uniform_workgroup = OpConstant %u32 324 %acquire_release_uniform_workgroup = OpConstant %u32 328 %f32_ptr = OpTypePointer Workgroup %f32 @@ -261,7 +263,7 @@ TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %u32 %u32_var %device %relaxed -%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire +%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire_uniform_workgroup %val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed )"; @@ -280,8 +282,9 @@ AnyVUID("VUID-StandaloneSpirv-None-04645")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " - "MeshNV, TaskNV, and GLCompute execution model")); + HasSubstr( + "in Vulkan environment, Workgroup Storage Class is limited to " + "MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute execution model")); } TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) { @@ -694,7 +697,7 @@ TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) { const std::string body = R"( %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed -%val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire +%val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); @@ -712,8 +715,9 @@ AnyVUID("VUID-StandaloneSpirv-None-04645")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " - "MeshNV, TaskNV, and GLCompute execution model")); + HasSubstr( + "in Vulkan environment, Workgroup Storage Class is limited to " + "MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute execution model")); } TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) { @@ -737,7 +741,7 @@ TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %u64 %u64_var %device %relaxed - %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire + %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire_uniform_workgroup %val3 = OpAtomicLoad %u64 %u64_var %invocation %relaxed )"; @@ -781,7 +785,8 @@ AnyVUID("VUID-StandaloneSpirv-None-04686")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("AtomicStore: Vulkan spec only allows storage classes for " + HasSubstr("AtomicStore: Function is not allowed, the Vulkan spec only " + "allows storage classes for " "atomic to be: Uniform, Workgroup, Image, StorageBuffer, " "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT.")); } @@ -803,7 +808,7 @@ // reenable once fixed. TEST_F(ValidateAtomics, DISABLED_AtomicLoadVulkanSubgroup) { const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %subgroup %acquire +%val1 = OpAtomicLoad %u32 %u32_var %subgroup %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -813,54 +818,9 @@ "limited to Device, Workgroup and Invocation")); } -TEST_F(ValidateAtomics, AtomicLoadVulkanRelease) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %workgroup %release -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " - "Release, AcquireRelease and SequentiallyConsistent")); -} - -TEST_F(ValidateAtomics, AtomicLoadVulkanAcquireRelease) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %workgroup %acquire_release -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " - "Release, AcquireRelease and SequentiallyConsistent")); -} - -TEST_F(ValidateAtomics, AtomicLoadVulkanSequentiallyConsistent) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " - "Release, AcquireRelease and SequentiallyConsistent")); -} - TEST_F(ValidateAtomics, AtomicLoadVulkanInvocationSemantics) { const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %invocation %acquire +%val1 = OpAtomicLoad %u32 %u32_var %invocation %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -870,7 +830,7 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicLoad: Vulkan specification requires Memory Semantics to " - "be None if used with Invocation Memory Scope")); + "be Relaxed if used with Invocation Memory Scope")); } TEST_F(ValidateAtomics, AtomicLoadShaderFloat) { @@ -1067,7 +1027,7 @@ TEST_F(ValidateAtomics, AtomicStoreVulkanSuccess) { const std::string body = R"( -OpAtomicStore %u32_var %device %release %u32_1 +OpAtomicStore %u32_var %device %release_uniform_workgroup %u32_1 OpAtomicStore %u32_var %invocation %relaxed %u32_1 )"; @@ -1075,54 +1035,9 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } -TEST_F(ValidateAtomics, AtomicStoreVulkanAcquire) { - const std::string body = R"( -OpAtomicStore %u32_var %device %acquire %u32_1 -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " - "Acquire, AcquireRelease and SequentiallyConsistent")); -} - -TEST_F(ValidateAtomics, AtomicStoreVulkanAcquireRelease) { - const std::string body = R"( -OpAtomicStore %u32_var %device %acquire_release %u32_1 -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " - "Acquire, AcquireRelease and SequentiallyConsistent")); -} - -TEST_F(ValidateAtomics, AtomicStoreVulkanSequentiallyConsistent) { - const std::string body = R"( -OpAtomicStore %u32_var %device %sequentially_consistent %u32_1 -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " - "Acquire, AcquireRelease and SequentiallyConsistent")); -} - TEST_F(ValidateAtomics, AtomicStoreVulkanInvocationSemantics) { const std::string body = R"( -OpAtomicStore %u32_var %invocation %acquire %u32_1 +OpAtomicStore %u32_var %invocation %release_uniform_workgroup %u32_1 )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -1132,7 +1047,7 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicStore: Vulkan specification requires Memory Semantics " - "to be None if used with Invocation Memory Scope")); + "to be Relaxed if used with Invocation Memory Scope")); } TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) { @@ -1169,7 +1084,8 @@ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("AtomicStore: storage class must be Function, Workgroup, " + HasSubstr("AtomicStore: storage class is Image, but must be Function, " + "Workgroup, " "CrossWorkGroup or Generic in the OpenCL environment.")); } @@ -1181,8 +1097,8 @@ CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("AtomicStore: storage class forbidden by universal " - "validation rules.")); + HasSubstr("AtomicStore: Can not be used with storage class " + "UniformConstant by universal validation rules")); } TEST_F(ValidateAtomics, AtomicStoreWrongScopeType) { @@ -1337,7 +1253,7 @@ TEST_F(ValidateAtomics, AtomicExchangeVulkanInvocationSemantics) { const std::string body = R"( OpAtomicStore %u32_var %invocation %relaxed %u32_1 -%val2 = OpAtomicExchange %u32 %u32_var %invocation %acquire %u32_0 +%val2 = OpAtomicExchange %u32 %u32_var %invocation %acquire_uniform_workgroup %u32_0 )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -1346,8 +1262,9 @@ AnyVUID("VUID-StandaloneSpirv-None-04641")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("AtomicExchange: Vulkan specification requires Memory " - "Semantics to be None if used with Invocation Memory Scope")); + HasSubstr( + "AtomicExchange: Vulkan specification requires Memory Semantics to " + "be Relaxed if used with Invocation Memory Scope")); } TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) { @@ -1469,9 +1386,10 @@ CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("AtomicCompareExchange: Memory Semantics Release and " - "AcquireRelease cannot be used for operand Unequal")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicCompareExchange Unequal Memory Semantics must not use " + "Release or AcquireRelease memory order")); } TEST_F(ValidateAtomics, AtomicCompareExchangeWrongValueType) { @@ -1526,7 +1444,7 @@ TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsEqual) { const std::string body = R"( OpAtomicStore %u32_var %device %relaxed %u32_1 -%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %release %relaxed %u32_0 %u32_0 +%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %release_uniform_workgroup %relaxed %u32_0 %u32_0 )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -1535,14 +1453,15 @@ AnyVUID("VUID-StandaloneSpirv-None-04641")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory " - "Semantics to be None if used with Invocation Memory Scope")); + HasSubstr( + "AtomicCompareExchange: Vulkan specification requires Memory " + "Semantics to be Relaxed if used with Invocation Memory Scope")); } TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsUnequal) { const std::string body = R"( OpAtomicStore %u32_var %device %relaxed %u32_1 -%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %relaxed %acquire %u32_0 %u32_0 +%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %acquire_uniform_workgroup %acquire_uniform_workgroup %u32_0 %u32_0 )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -1551,8 +1470,9 @@ AnyVUID("VUID-StandaloneSpirv-None-04641")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory " - "Semantics to be None if used with Invocation Memory Scope")); + HasSubstr( + "AtomicCompareExchange: Vulkan specification requires Memory " + "Semantics to be Relaxed if used with Invocation Memory Scope")); } TEST_F(ValidateAtomics, AtomicArithmeticsSuccess) { @@ -1666,9 +1586,10 @@ CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Memory Semantics Acquire and AcquireRelease cannot be " - "used with AtomicFlagClear")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFlagClear: MemorySemantics must not use Acquire or " + "AcquireRelease memory order with AtomicFlagClear")); } TEST_F(ValidateAtomics, AtomicFlagClearNotPointer) { @@ -1743,9 +1664,8 @@ CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("AtomicIIncrement: Memory Semantics can have at most " - "one of the following bits set: Acquire, Release, " - "AcquireRelease or SequentiallyConsistent")); + HasSubstr("AtomicIIncrement: Memory Semantics must have at most " + "one non-relaxed memory order bit set")); } TEST_F(ValidateAtomics, AtomicUniformMemorySemanticsShader) { @@ -1794,357 +1714,6 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicLoad) { - const std::string body = R"( -%ld = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicStore) { - const std::string body = R"( -OpAtomicStore %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, - VulkanMemoryModelBanSequentiallyConsistentAtomicExchange) { - const std::string body = R"( -%ex = OpAtomicExchange %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, - VulkanMemoryModelBanSequentiallyConsistentAtomicCompareExchangeEqual) { - const std::string body = R"( -%ex = OpAtomicCompareExchange %u32 %u32_var %workgroup %sequentially_consistent %relaxed %u32_0 %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, - VulkanMemoryModelBanSequentiallyConsistentAtomicCompareExchangeUnequal) { - const std::string body = R"( -%ex = OpAtomicCompareExchange %u32 %u32_var %workgroup %relaxed %sequentially_consistent %u32_0 %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, - VulkanMemoryModelBanSequentiallyConsistentAtomicIIncrement) { - const std::string body = R"( -%inc = OpAtomicIIncrement %u32 %u32_var %workgroup %sequentially_consistent -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, - VulkanMemoryModelBanSequentiallyConsistentAtomicIDecrement) { - const std::string body = R"( -%dec = OpAtomicIDecrement %u32 %u32_var %workgroup %sequentially_consistent -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicIAdd) { - const std::string body = R"( -%add = OpAtomicIAdd %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicISub) { - const std::string body = R"( -%sub = OpAtomicISub %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicSMin) { - const std::string body = R"( -%min = OpAtomicSMin %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicUMin) { - const std::string body = R"( -%min = OpAtomicUMin %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMinEXT) { - const std::string body = R"( -%max = OpAtomicFMinEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpCapability AtomicFloat32MinMaxEXT -OpExtension "SPV_KHR_vulkan_memory_model" -OpExtension "SPV_EXT_shader_atomic_float_min_max" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicSMax) { - const std::string body = R"( -%max = OpAtomicSMax %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicUMax) { - const std::string body = R"( -%max = OpAtomicUMax %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMaxEXT) { - const std::string body = R"( -%max = OpAtomicFMaxEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpCapability AtomicFloat32MinMaxEXT -OpExtension "SPV_KHR_vulkan_memory_model" -OpExtension "SPV_EXT_shader_atomic_float_min_max" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicAnd) { - const std::string body = R"( -%and = OpAtomicAnd %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicOr) { - const std::string body = R"( -%or = OpAtomicOr %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicXor) { - const std::string body = R"( -%xor = OpAtomicXor %u32 %u32_var %workgroup %sequentially_consistent %u32_0 -)"; - - const std::string extra = R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - - CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), - SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - TEST_F(ValidateAtomics, OutputMemoryKHRRequiresVulkanMemoryModelKHR) { const std::string text = R"( OpCapability Shader @@ -2226,130 +1795,6 @@ "capability VulkanMemoryModelKHR")); } -TEST_F(ValidateAtomics, MakeAvailableKHRRequiresReleaseSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %1 "func" -OpExecutionMode %1 OriginUpperLeft -%2 = OpTypeVoid -%3 = OpTypeInt 32 0 -%semantics = OpConstant %3 8448 -%5 = OpTypeFunction %2 -%workgroup = OpConstant %3 2 -%ptr = OpTypePointer Workgroup %3 -%var = OpVariable %ptr Workgroup -%1 = OpFunction %2 None %5 -%7 = OpLabel -OpAtomicStore %var %workgroup %semantics %workgroup -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("AtomicStore: MakeAvailableKHR Memory Semantics also requires " - "either Release or AcquireRelease Memory Semantics")); -} - -TEST_F(ValidateAtomics, MakeVisibleKHRRequiresAcquireSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %1 "func" -OpExecutionMode %1 OriginUpperLeft -%2 = OpTypeVoid -%3 = OpTypeInt 32 0 -%semantics = OpConstant %3 16640 -%5 = OpTypeFunction %2 -%workgroup = OpConstant %3 2 -%ptr = OpTypePointer Workgroup %3 -%var = OpVariable %ptr Workgroup -%1 = OpFunction %2 None %5 -%7 = OpLabel -%ld = OpAtomicLoad %3 %var %workgroup %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("AtomicLoad: MakeVisibleKHR Memory Semantics also requires " - "either Acquire or AcquireRelease Memory Semantics")); -} - -TEST_F(ValidateAtomics, MakeAvailableKHRRequiresStorageSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %1 "func" -OpExecutionMode %1 OriginUpperLeft -%2 = OpTypeVoid -%3 = OpTypeInt 32 0 -%semantics = OpConstant %3 8196 -%5 = OpTypeFunction %2 -%workgroup = OpConstant %3 2 -%ptr = OpTypePointer Workgroup %3 -%var = OpVariable %ptr Workgroup -%1 = OpFunction %2 None %5 -%7 = OpLabel -OpAtomicStore %var %workgroup %semantics %workgroup -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "AtomicStore: expected Memory Semantics to include a storage class")); -} - -TEST_F(ValidateAtomics, MakeVisibleKHRRequiresStorageSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %1 "func" -OpExecutionMode %1 OriginUpperLeft -%2 = OpTypeVoid -%3 = OpTypeInt 32 0 -%semantics = OpConstant %3 16386 -%5 = OpTypeFunction %2 -%workgroup = OpConstant %3 2 -%ptr = OpTypePointer Workgroup %3 -%var = OpVariable %ptr Workgroup -%1 = OpFunction %2 None %5 -%7 = OpLabel -%ld = OpAtomicLoad %3 %var %workgroup %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "AtomicLoad: expected Memory Semantics to include a storage class")); -} - TEST_F(ValidateAtomics, VulkanMemoryModelAllowsQueueFamilyKHR) { const std::string body = R"( %val = OpAtomicAnd %u32 %u32_var %queuefamily %relaxed %u32_1 @@ -2374,8 +1819,8 @@ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicAnd: Memory Scope QueueFamilyKHR requires " - "capability VulkanMemoryModelKHR\n %42 = OpAtomicAnd " - "%uint %29 %uint_5 %uint_0_1 %uint_1\n")); + "capability VulkanMemoryModelKHR\n %44 = OpAtomicAnd " + "%uint %31 %uint_5 %uint_0_1 %uint_1\n")); } TEST_F(ValidateAtomics, SemanticsSpecConstantShader) { @@ -2536,64 +1981,6 @@ "AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier")); } -TEST_F(ValidateAtomics, CompareExchangeVolatileMatch) { - const std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpCapability Linkage -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%int_0 = OpConstant %int 0 -%int_1 = OpConstant %int 1 -%workgroup = OpConstant %int 2 -%volatile = OpConstant %int 32768 -%ptr_wg_int = OpTypePointer Workgroup %int -%wg_var = OpVariable %ptr_wg_int Workgroup -%void_fn = OpTypeFunction %void -%func = OpFunction %void None %void_fn -%entry = OpLabel -%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %volatile %int_0 %int_1 -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - -TEST_F(ValidateAtomics, CompareExchangeVolatileMismatch) { - const std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpCapability Linkage -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%int_0 = OpConstant %int 0 -%int_1 = OpConstant %int 1 -%workgroup = OpConstant %int 2 -%volatile = OpConstant %int 32768 -%non_volatile = OpConstant %int 0 -%ptr_wg_int = OpTypePointer Workgroup %int -%wg_var = OpVariable %ptr_wg_int Workgroup -%void_fn = OpTypeFunction %void -%func = OpFunction %void None %void_fn -%entry = OpLabel -%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %non_volatile %volatile %int_0 %int_1 -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Volatile mask setting must match for Equal and " - "Unequal memory semantics")); -} - TEST_F(ValidateAtomics, CompareExchangeVolatileMismatchCooperativeMatrix) { const std::string spirv = R"( OpCapability Shader @@ -2621,8 +2008,8 @@ )"; // This is ok because we cannot evaluate the spec constant defaults. - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateAtomics, VolatileRequiresVulkanMemoryModel) {
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp index ba8ac7d..6f2cb45 100644 --- a/test/val/val_barriers_test.cpp +++ b/test/val/val_barriers_test.cpp
@@ -469,7 +469,7 @@ TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationFailure) { const std::string body = R"( -OpControlBarrier %workgroup %invocation %acquire +OpControlBarrier %workgroup %invocation %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); @@ -478,8 +478,9 @@ AnyVUID("VUID-StandaloneSpirv-None-04641")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("ControlBarrier: Vulkan specification requires Memory " - "Semantics to be None if used with Invocation Memory Scope")); + HasSubstr( + "ControlBarrier: Vulkan specification requires Memory " + "Semantics to be Relaxed if used with Invocation Memory Scope")); } TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) { @@ -490,25 +491,8 @@ CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ControlBarrier: Memory Semantics can have at most one " - "of the following bits set: Acquire, Release, " - "AcquireRelease or SequentiallyConsistent")); -} - -TEST_F(ValidateBarriers, OpControlBarrierVulkanSubgroupStorageClass) { - const std::string body = R"( -OpControlBarrier %workgroup %device %acquire_release_subgroup -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04650")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "ControlBarrier: expected Memory Semantics to include a " - "Vulkan-supported storage class if Memory Semantics is not None")); + HasSubstr("ControlBarrier: Memory Semantics must have at most " + "one non-relaxed memory order bit set")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p1) { @@ -687,9 +671,8 @@ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpControlBarrier requires one of the following " - "Execution " - "Models: TessellationControl, GLCompute, Kernel, " - "MeshNV or TaskNV")); + "Execution Models: TessellationControl, GLCompute, " + "Kernel, MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) { @@ -800,53 +783,8 @@ CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("MemoryBarrier: Memory Semantics can have at most one " - "of the following bits set: Acquire, Release, " - "AcquireRelease or SequentiallyConsistent")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsNone) { - const std::string body = R"( -OpMemoryBarrier %device %none -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04732")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("MemoryBarrier: Vulkan specification requires Memory Semantics " - "to have one of the following bits set: Acquire, Release, " - "AcquireRelease or SequentiallyConsistent")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsAcquire) { - const std::string body = R"( -OpMemoryBarrier %device %acquire -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04733")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("MemoryBarrier: expected Memory Semantics to include a " - "Vulkan-supported storage class")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSubgroupStorageClass) { - const std::string body = R"( -OpMemoryBarrier %device %acquire_release_subgroup -)"; - - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04733")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("MemoryBarrier: expected Memory Semantics to include a " - "Vulkan-supported storage class")); + HasSubstr("MemoryBarrier: Memory Semantics must have at most one " + "non-relaxed memory order bit set")); } TEST_F(ValidateBarriers, OpNamedBarrierInitializeSuccess) { @@ -959,9 +897,8 @@ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("MemoryNamedBarrier: Memory Semantics can have at most " - "one of the following bits set: Acquire, Release, " - "AcquireRelease or SequentiallyConsistent")); + HasSubstr("MemoryNamedBarrier: Memory Semantics must have " + "at most one non-relaxed memory order bit set")); } TEST_F(ValidateBarriers, TypeAsMemoryScope) { @@ -975,64 +912,6 @@ "type")); } -TEST_F(ValidateBarriers, - OpControlBarrierVulkanMemoryModelBanSequentiallyConsistent) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %1 "func" -OpExecutionMode %1 OriginUpperLeft -%2 = OpTypeVoid -%3 = OpTypeInt 32 0 -%4 = OpConstant %3 16 -%5 = OpTypeFunction %2 -%6 = OpConstant %3 5 -%1 = OpFunction %2 None %5 -%7 = OpLabel -OpControlBarrier %6 %6 %4 -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - -TEST_F(ValidateBarriers, - OpMemoryBarrierVulkanMemoryModelBanSequentiallyConsistent) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %1 "func" -OpExecutionMode %1 OriginUpperLeft -%2 = OpTypeVoid -%3 = OpTypeInt 32 0 -%4 = OpConstant %3 16 -%5 = OpTypeFunction %2 -%6 = OpConstant %3 5 -%1 = OpFunction %2 None %5 -%7 = OpLabel -OpMemoryBarrier %6 %4 -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("SequentiallyConsistent memory semantics cannot be " - "used with the VulkanKHR memory model.")); -} - TEST_F(ValidateBarriers, OutputMemoryKHRRequireVulkanMemoryModelKHR) { const std::string text = R"( OpCapability Shader @@ -1108,120 +987,6 @@ "requires capability VulkanMemoryModelKHR")); } -TEST_F(ValidateBarriers, MakeAvailableKHRRequiresReleaseSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%workgroup = OpConstant %int 2 -%semantics = OpConstant %int 8448 -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpControlBarrier %workgroup %workgroup %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("ControlBarrier: MakeAvailableKHR Memory Semantics also " - "requires either Release or AcquireRelease Memory Semantics")); -} - -TEST_F(ValidateBarriers, MakeVisibleKHRRequiresAcquireSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%workgroup = OpConstant %int 2 -%semantics = OpConstant %int 16640 -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpControlBarrier %workgroup %workgroup %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("ControlBarrier: MakeVisibleKHR Memory Semantics also requires " - "either Acquire or AcquireRelease Memory Semantics")); -} - -TEST_F(ValidateBarriers, MakeAvailableKHRRequiresStorageSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%workgroup = OpConstant %int 2 -%semantics = OpConstant %int 8196 -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpMemoryBarrier %workgroup %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("MemoryBarrier: expected Memory Semantics to include a " - "storage class")); -} - -TEST_F(ValidateBarriers, MakeVisibleKHRRequiresStorageSemantics) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%workgroup = OpConstant %int 2 -%semantics = OpConstant %int 16386 -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpMemoryBarrier %workgroup %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, - ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("MemoryBarrier: expected Memory Semantics to include a " - "storage class")); -} - TEST_F(ValidateBarriers, SemanticsSpecConstantShader) { const std::string spirv = R"( OpCapability Shader @@ -1333,7 +1098,7 @@ %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 -%semantics = OpConstant %int 0 +%semantics = OpConstant %int 72 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel @@ -1363,7 +1128,7 @@ %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 -%semantics = OpConstant %int 0 +%semantics = OpConstant %int 72 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel @@ -1376,60 +1141,6 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateBarriers, VolatileMemoryBarrier) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpCapability VulkanMemoryModelDeviceScopeKHR -OpCapability Linkage -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%device = OpConstant %int 1 -%semantics = OpConstant %int 32768 -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpMemoryBarrier %device %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Memory Semantics Volatile can only be used with " - "atomic instructions")); -} - -TEST_F(ValidateBarriers, VolatileControlBarrier) { - const std::string text = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpCapability VulkanMemoryModelDeviceScopeKHR -OpCapability Linkage -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -%void = OpTypeVoid -%int = OpTypeInt 32 0 -%device = OpConstant %int 1 -%semantics = OpConstant %int 32768 -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpControlBarrier %device %device %semantics -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Memory Semantics Volatile can only be used with " - "atomic instructions")); -} - TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) { const std::string text = R"( OpCapability Shader @@ -1452,8 +1163,8 @@ OpFunctionEnd )"; - CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) { @@ -1478,8 +1189,9 @@ OpFunctionEnd )"; - CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Memory Semantics must be a constant instruction when " "CooperativeMatrixNV capability is present"));
diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp index b849e7b..c906e47 100644 --- a/test/val/val_bitwise_test.cpp +++ b/test/val/val_bitwise_test.cpp
@@ -420,13 +420,22 @@ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected 32-bit int type for Base operand: BitFieldInsert")); } +TEST_F(ValidateBitwise, OpBitFieldInsertNot32Allow) { + const std::string body = R"( + %val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2 + )"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + spvValidatorOptionsSetAllowVulkan32BitBitwise(getValidatorOptions(), true); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) { const std::string body = R"( %val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2 @@ -504,8 +513,7 @@ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected 32-bit int type for Base operand: BitFieldSExtract")); @@ -562,8 +570,7 @@ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected 32-bit int type for Base operand: BitReverse")); @@ -607,10 +614,8 @@ %val1 = OpBitCount %u32 %f64_1 )"; - CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Base-04781")); + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -637,8 +642,7 @@ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected 32-bit int type for Base operand: BitCount")); }
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 0104969..084a643 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp
@@ -26,8 +26,6 @@ #include <vector> #include "gmock/gmock.h" -#include "source/spirv_target_env.h" -#include "test/unit_spirv.h" #include "test/val/val_code_generator.h" #include "test/val/val_fixtures.h" @@ -99,6 +97,10 @@ generator.before_types_ += built_in; generator.before_types_ += "\n"; + if (strncmp(built_in, "TessLevel", 9) == 0) { + generator.before_types_ += "OpMemberDecorate %built_in_type 0 Patch\n"; + } + std::ostringstream after_types; after_types << "%built_in_type = OpTypeStruct " << data_type << "\n"; @@ -139,7 +141,11 @@ execution_modes << "OpExecutionMode %" << entry_point.name << " OutputPoints\n"; } - if (0 == std::strcmp(execution_model, "GLCompute")) { + if (0 == std::strcmp(execution_model, "GLCompute") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "MeshNV") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "TaskNV")) { execution_modes << "OpExecutionMode %" << entry_point.name << " LocalSize 1 1 1\n"; } @@ -258,6 +264,10 @@ generator.before_types_ += built_in; generator.before_types_ += "\n"; + if (strncmp(built_in, "TessLevel", 9) == 0) { + generator.before_types_ += "OpMemberDecorate %built_in_type 0 Patch\n"; + } + std::ostringstream after_types; after_types << "%built_in_type = OpTypeStruct " << data_type << "\n"; if (InitializerRequired(storage_class)) { @@ -297,7 +307,11 @@ execution_modes << "OpExecutionMode %" << entry_point.name << " OutputPoints\n"; } - if (0 == std::strcmp(execution_model, "GLCompute")) { + if (0 == std::strcmp(execution_model, "GLCompute") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "MeshNV") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "TaskNV")) { execution_modes << "OpExecutionMode %" << entry_point.name << " LocalSize 1 1 1\n"; } @@ -395,6 +409,11 @@ generator.before_types_ = "OpDecorate %built_in_var BuiltIn "; generator.before_types_ += built_in; generator.before_types_ += "\n"; + + if (strncmp(built_in, "TessLevel", 9) == 0) { + generator.before_types_ += "OpDecorate %built_in_var Patch\n"; + } + if ((0 == std::strcmp(storage_class, "Input")) && (0 == std::strcmp(execution_model, "Fragment"))) { // ensure any needed input types that might require Flat @@ -423,7 +442,7 @@ } // Any kind of reference would do. entry_point.body = R"( -%val = OpBitcast %u32 %built_in_var +%val = OpCopyObject %built_in_ptr %built_in_var )"; std::ostringstream execution_modes; @@ -441,7 +460,11 @@ execution_modes << "OpExecutionMode %" << entry_point.name << " OutputPoints\n"; } - if (0 == std::strcmp(execution_model, "GLCompute")) { + if (0 == std::strcmp(execution_model, "GLCompute") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "MeshNV") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "TaskNV")) { execution_modes << "OpExecutionMode %" << entry_point.name << " LocalSize 1 1 1\n"; } @@ -847,6 +870,45 @@ "has components with bit width 64")))); INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("LocalInvocationIndex"), Values("GLCompute"), + Values("Input"), Values("%u32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexNotGLCompute, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("LocalInvocationIndex"), + Values("Vertex", "Fragment", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), + Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04284"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute, MeshNV, " + "TaskNV, MeshEXT or TaskEXT execution model")))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("LocalInvocationIndex"), Values("GLCompute"), + Values("Output"), Values("%u32"), + Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04285"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexNot32Int, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("LocalInvocationIndex"), Values("GLCompute"), + Values("Input"), Values("%u32vec3", "%f32"), + Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04286"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar")))); + +INSTANTIATE_TEST_SUITE_P( InvocationIdSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"), @@ -1423,16 +1485,6 @@ "to be used only with Fragment execution model")))); INSTANTIATE_TEST_SUITE_P( - SampleMaskWrongStorageClass, - ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("SampleMask"), Values("Fragment"), Values("Workgroup"), - Values("%u32arr2"), Values("VUID-SampleMask-SampleMask-04358"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "Vulkan spec allows BuiltIn SampleMask to be only used for " - "variables with Input or Output storage class")))); - -INSTANTIATE_TEST_SUITE_P( SampleMaskNotArray, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("SampleMask"), Values("Fragment"), Values("Input"), @@ -2645,7 +2697,7 @@ entry_point.interfaces = "%built_in_var"; // Any kind of reference would do. entry_point.body = R"( -%val = OpBitcast %u32 %built_in_var +%val = OpCopyObject %built_in_ptr %built_in_var )"; std::ostringstream execution_modes; @@ -2663,7 +2715,11 @@ execution_modes << "OpExecutionMode %" << entry_point.name << " OutputPoints\n"; } - if (0 == std::strcmp(execution_model, "GLCompute")) { + if (0 == std::strcmp(execution_model, "GLCompute") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "MeshNV") || + 0 == std::strcmp(execution_model, "MeshEXT") || + 0 == std::strcmp(execution_model, "TaskNV")) { execution_modes << "OpExecutionMode %" << entry_point.name << " LocalSize 1 1 1\n"; } @@ -3575,6 +3631,7 @@ EntryPoint entry_point; entry_point.name = "main_d_r"; entry_point.execution_model = "MeshNV"; + entry_point.execution_modes = "OpExecutionMode %main_d_r LocalSize 1 1 1"; entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex"; generator.entry_points_.push_back(std::move(entry_point)); @@ -3613,7 +3670,9 @@ EntryPoint entry_point; entry_point.name = "main_d_r"; entry_point.execution_model = "MeshNV"; + entry_point.execution_modes = "OpExecutionMode %main_d_r LocalSize 1 1 1"; entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex"; + entry_point.body = "%ref_load = OpLoad %_arr_float_uint_81 %gl_PrimitiveID"; generator.entry_points_.push_back(std::move(entry_point)); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_1); @@ -3947,7 +4006,8 @@ Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec allows BuiltIn PrimitiveShadingRateKHR to be used " - "only with Vertex, Geometry, or MeshNV execution models.")))); + "only with Vertex, Geometry, MeshNV or MeshEXT execution " + "models.")))); INSTANTIATE_TEST_SUITE_P( PrimitiveShadingRateInvalidStorageClass, @@ -4325,6 +4385,29 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateBuiltIns, + VulkanPrimitiveTriangleIndicesEXTInvalidExecutionMode) { + const std::string declarations = R"( +%array = OpTypeArray %v3uint %uint_16 +%array_ptr = OpTypePointer Output %array +%var = OpVariable %array_ptr Output +%ptr = OpTypePointer Output %v3uint +)"; + const std::string body = R"( +%access = OpAccessChain %ptr %var %int_0 +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveTriangleIndicesEXT", "OutputPoints", + body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07054")); +} + TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTStorageClass) { const std::string declarations = R"( %array = OpTypeArray %v3uint %uint_16 @@ -4408,6 +4491,28 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTInvalidExecutionMode) { + const std::string declarations = R"( + %array = OpTypeArray %v2uint %uint_16 + %array_ptr = OpTypePointer Output %array + %var = OpVariable %array_ptr Output + %ptr = OpTypePointer Output %v2uint + )"; + const std::string body = R"( + %access = OpAccessChain %ptr %var %int_0 + )"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveLineIndicesEXT", "OutputPoints", body, + declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048")); +} + TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTStorageClass) { const std::string declarations = R"( %array = OpTypeArray %v2uint %uint_16 @@ -4471,6 +4576,28 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTInvalidExecutionMode) { + const std::string declarations = R"( + %array = OpTypeArray %uint %uint_16 + %array_ptr = OpTypePointer Output %array + %var = OpVariable %array_ptr Output + %ptr = OpTypePointer Output %uint + )"; + const std::string body = R"( + %access = OpAccessChain %ptr %var %int_0 + )"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitivePointIndicesEXT", "OutputTrianglesNV", + body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042")); +} + TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTStorageClass) { const std::string declarations = R"( %array = OpTypeArray %uint %uint_16 @@ -4515,6 +4642,2607 @@ AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044")); } +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimtiveIDWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_PrimitiveID + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId + OpDecorate %gl_PrimitiveID PerPrimitiveNV + %void = OpTypeVoid + %9 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %uint_0 = OpConstant %uint 0 + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_int_1 Output + %MainMesh = OpFunction %void None %9 + %25 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimtiveIDWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_PrimitiveID + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +%void = OpTypeVoid + %9 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%uint_0 = OpConstant %uint 0 +%v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_int_1 Output +%MainMesh = OpFunction %void None %9 + %25 = OpLabel +%ref_load = OpLoad %_arr_int_int_1 %gl_PrimitiveID + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveId-PrimitiveId-07040")); +} + + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_ViewportIndex + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex +%void = OpTypeVoid +%9 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_3 = OpConstant %int 3 +%uint_0 = OpConstant %uint 0 +%v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_int_int_1 Output +%MainMesh = OpFunction %void None %9 +%25 = OpLabel +%ref_load = OpLoad %_arr_int_int_1 %gl_ViewportIndex + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-ViewportIndex-ViewportIndex-07060")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitivePointIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitivePointIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputPoints + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT" + OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32 +%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Output_uint = OpTypePointer Output %uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0 + OpStore %15 %uint_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveLineIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveLineIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputLinesEXT + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveLineIndicesEXT "gl_PrimitiveLineIndicesEXT" + OpDecorate %gl_PrimitiveLineIndicesEXT BuiltIn PrimitiveLineIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%v2uint = OpTypeVector %uint 2 +%uint_32 = OpConstant %uint 32 +%_arr_v2uint_uint_32 = OpTypeArray %v2uint %uint_32 +%_ptr_Output__arr_v2uint_uint_32 = OpTypePointer Output %_arr_v2uint_uint_32 +%gl_PrimitiveLineIndicesEXT = OpVariable %_ptr_Output__arr_v2uint_uint_32 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 + %15 = OpConstantComposite %v2uint %uint_0 %uint_0 +%_ptr_Output_v2uint = OpTypePointer Output %v2uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpAccessChain %_ptr_Output_v2uint %gl_PrimitiveLineIndicesEXT %int_0 + OpStore %17 %15 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitiveLineIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveLineIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputPoints + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveLineIndicesEXT "gl_PrimitiveLineIndicesEXT" + OpDecorate %gl_PrimitiveLineIndicesEXT BuiltIn PrimitiveLineIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%v2uint = OpTypeVector %uint 2 +%uint_32 = OpConstant %uint 32 +%_arr_v2uint_uint_32 = OpTypeArray %v2uint %uint_32 +%_ptr_Output__arr_v2uint_uint_32 = OpTypePointer Output %_arr_v2uint_uint_32 +%gl_PrimitiveLineIndicesEXT = OpVariable %_ptr_Output__arr_v2uint_uint_32 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 + %15 = OpConstantComposite %v2uint %uint_0 %uint_0 +%_ptr_Output_v2uint = OpTypePointer Output %v2uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpAccessChain %_ptr_Output_v2uint %gl_PrimitiveLineIndicesEXT %int_0 + OpStore %17 %15 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitivePointIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitivePointIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT" + OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32 +%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Output_uint = OpTypePointer Output %uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0 + OpStore %15 %uint_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveTriangleIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveTriangleIndicesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveTriangleIndicesEXT "gl_PrimitiveTriangleIndicesEXT" + OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT +%void = OpTypeVoid +%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_32 = OpTypeArray %v3uint %uint_32 +%_ptr_Output__arr_v3uint_uint_32 = OpTypePointer Output %_arr_v3uint_uint_32 +%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%15 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%17 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %7 +%18 = OpLabel +%19 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0 + OpStore %19 %15 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitiveTriangleIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveTriangleIndicesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputPoints + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveTriangleIndicesEXT "gl_PrimitiveTriangleIndicesEXT" + OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT +%void = OpTypeVoid +%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_32 = OpTypeArray %v3uint %uint_32 +%_ptr_Output__arr_v3uint_uint_32 = OpTypePointer Output %_arr_v3uint_uint_32 +%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%15 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%17 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %7 +%18 = OpLabel +%19 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0 + OpStore %19 %15 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07054")); +} + +// https://github.com/KhronosGroup/SPIRV-Tools/issues/6307 +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveTriangleIndicesMultiEntrypoint) { + const std::string text = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpCapability VulkanMemoryModel + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical Vulkan + OpEntryPoint MeshEXT %1 "mesh" %positions %indices + OpEntryPoint Fragment %4 "frag" %color + OpExecutionMode %1 LocalSize 1 1 1 + OpExecutionMode %1 OutputVertices 3 + OpExecutionMode %1 OutputPrimitivesEXT 1 + OpExecutionMode %1 OutputTrianglesEXT + OpExecutionMode %4 OriginUpperLeft + OpDecorate %_arr_v4float_uint_3 ArrayStride 16 + OpDecorate %_arr_v3uint_uint_1 ArrayStride 16 + OpDecorate %positions BuiltIn Position + OpDecorate %indices BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %color Location 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1 +%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1 + %void = OpTypeVoid + %18 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %positions = OpVariable %_ptr_Output__arr_v4float_uint_3 Output + %indices = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output + %color = OpVariable %_ptr_Output_v4float Output + %1 = OpFunction %void None %18 + %21 = OpLabel + OpSetMeshOutputsEXT %uint_0 %uint_0 + OpNoLine + OpReturn + OpFunctionEnd + %4 = OpFunction %void None %18 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanPrimitivePointIndicesArraySizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitivePointIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputPoints + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT" + OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32 +%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Output_uint = OpTypePointer Output %uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +%15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0 + OpStore %15 %uint_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046")); +} + +TEST_F(ValidateBuiltIns, BadVulkanPrimitiveLineIndicesArraySizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveLineIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputLinesEXT + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveLineIndicesEXT "gl_PrimitiveLineIndicesEXT" + OpDecorate %gl_PrimitiveLineIndicesEXT BuiltIn PrimitiveLineIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%v2uint = OpTypeVector %uint 2 +%uint_32 = OpConstant %uint 32 +%_arr_v2uint_uint_32 = OpTypeArray %v2uint %uint_32 +%_ptr_Output__arr_v2uint_uint_32 = OpTypePointer Output %_arr_v2uint_uint_32 +%gl_PrimitiveLineIndicesEXT = OpVariable %_ptr_Output__arr_v2uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%15 = OpConstantComposite %v2uint %uint_0 %uint_0 +%_ptr_Output_v2uint = OpTypePointer Output %v2uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 + %5 = OpLabel +%17 = OpAccessChain %_ptr_Output_v2uint %gl_PrimitiveLineIndicesEXT %int_0 + OpStore %17 %15 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052")); +} + +TEST_F(ValidateBuiltIns, BadVulkanPrimitiveTriangleIndicesArraySizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveTriangleIndicesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveTriangleIndicesEXT "gl_PrimitiveTriangleIndicesEXT" + OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT +%void = OpTypeVoid +%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_32 = OpTypeArray %v3uint %uint_32 +%_ptr_Output__arr_v3uint_uint_32 = OpTypePointer Output %_arr_v3uint_uint_32 +%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%15 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%17 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %7 +%18 = OpLabel +%19 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0 + OpStore %19 %15 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07058")); +} + +// https://godbolt.org/z/xqsMqqnxd +TEST_F(ValidateBuiltIns, VulkanMeshMultipleTopology) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main1 "main1" %26 %vertices_color + OpEntryPoint MeshEXT %main2 "main2" %43 %vertices_color_0 + OpExecutionMode %main1 OutputVertices 3 + OpExecutionMode %main1 OutputPrimitivesEXT 1 + OpExecutionMode %main1 LocalSize 1 1 1 + OpExecutionMode %main1 OutputTrianglesEXT + OpExecutionMode %main2 OutputVertices 3 + OpExecutionMode %main2 OutputPrimitivesEXT 1 + OpExecutionMode %main2 LocalSize 1 1 1 + OpExecutionMode %main2 OutputLinesEXT + OpDecorate %vertices_color Location 0 + OpDecorate %26 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %vertices_color_0 Location 0 + OpDecorate %43 BuiltIn PrimitiveLineIndicesEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 +%_arr_v4float_int_3 = OpTypeArray %v4float %int_3 +%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %uint_0 = OpConstant %uint 0 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %v3uint = OpTypeVector %uint 3 + %int_1 = OpConstant %int 1 +%_arr_v3uint_int_1 = OpTypeArray %v3uint %int_1 +%_ptr_Output__arr_v3uint_int_1 = OpTypePointer Output %_arr_v3uint_int_1 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %uint_2 = OpConstant %uint 2 + %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 + %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %v2uint = OpTypeVector %uint 2 +%_arr_v2uint_int_1 = OpTypeArray %v2uint %int_1 +%_ptr_Output__arr_v2uint_int_1 = OpTypePointer Output %_arr_v2uint_int_1 +%_ptr_Output_v2uint = OpTypePointer Output %v2uint + %46 = OpConstantComposite %v2uint %uint_0 %uint_1 +%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %26 = OpVariable %_ptr_Output__arr_v3uint_int_1 Output +%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %43 = OpVariable %_ptr_Output__arr_v2uint_int_1 Output + %main1 = OpFunction %void None %3 + %4 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0 + OpStore %17 %19 + %28 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0 + OpStore %28 %29 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %34 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0 + OpStore %37 %38 + %45 = OpAccessChain %_ptr_Output_v2uint %43 %uint_0 + OpStore %45 %46 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +// https://godbolt.org/z/MeTadeYEr +TEST_F(ValidateBuiltIns, VulkanMeshMultipleArraySizes) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main1 "main1" %var1 %vertices_color + OpEntryPoint MeshEXT %main2 "main2" %var2 %vertices_color_0 + OpExecutionMode %main1 OutputVertices 3 + OpExecutionMode %main1 OutputPrimitivesEXT 2 + OpExecutionMode %main1 LocalSize 1 1 1 + OpExecutionMode %main1 OutputTrianglesEXT + OpExecutionMode %main2 OutputVertices 3 + OpExecutionMode %main2 OutputPrimitivesEXT 4 + OpExecutionMode %main2 LocalSize 1 1 1 + OpExecutionMode %main2 OutputTrianglesEXT + OpSource Slang 1 + OpName %vertices_color "vertices.color" + OpName %main1 "main1" + OpName %vertices_color_0 "vertices.color" + OpName %main2 "main2" + OpDecorate %vertices_color Location 0 + OpDecorate %var1 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %var2 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %vertices_color_0 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 +%_arr_v4float_int_3 = OpTypeArray %v4float %int_3 +%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %uint_0 = OpConstant %uint 0 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %v3uint = OpTypeVector %uint 3 + %int_2 = OpConstant %int 2 + %int_4 = OpConstant %int 4 +%_arr_v3uint_int_2 = OpTypeArray %v3uint %int_2 +%_arr_v3uint_int_4 = OpTypeArray %v3uint %int_4 +%_ptr_Output__arr_v3uint_int_2 = OpTypePointer Output %_arr_v3uint_int_2 +%_ptr_Output__arr_v3uint_int_4 = OpTypePointer Output %_arr_v3uint_int_4 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %uint_2 = OpConstant %uint 2 + %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 + %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %41 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 +%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %var1 = OpVariable %_ptr_Output__arr_v3uint_int_2 Output + %var2 = OpVariable %_ptr_Output__arr_v3uint_int_4 Output +%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %main1 = OpFunction %void None %3 + %4 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0 + OpStore %17 %19 + %28 = OpAccessChain %_ptr_Output_v3uint %var1 %uint_0 + OpStore %28 %29 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %34 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0 + OpStore %37 %38 + %40 = OpAccessChain %_ptr_Output_v3uint %var2 %uint_0 + OpStore %40 %41 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanMeshMultipleArraySizes) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main1 "main1" %26 %vertices_color + OpEntryPoint MeshEXT %main2 "main2" %26 %vertices_color_0 + OpExecutionMode %main1 OutputVertices 3 + OpExecutionMode %main1 OutputPrimitivesEXT 2 + OpExecutionMode %main1 LocalSize 1 1 1 + OpExecutionMode %main1 OutputTrianglesEXT + OpExecutionMode %main2 OutputVertices 3 + OpExecutionMode %main2 OutputPrimitivesEXT 42 + OpExecutionMode %main2 LocalSize 1 1 1 + OpExecutionMode %main2 OutputTrianglesEXT + OpSource Slang 1 + OpName %vertices_color "vertices.color" + OpName %main1 "main1" + OpName %vertices_color_0 "vertices.color" + OpName %main2 "main2" + OpDecorate %vertices_color Location 0 + OpDecorate %26 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %vertices_color_0 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 +%_arr_v4float_int_3 = OpTypeArray %v4float %int_3 +%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %uint_0 = OpConstant %uint 0 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %v3uint = OpTypeVector %uint 3 + %int_2 = OpConstant %int 2 +%_arr_v3uint_int_2 = OpTypeArray %v3uint %int_2 +%_ptr_Output__arr_v3uint_int_2 = OpTypePointer Output %_arr_v3uint_int_2 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %uint_2 = OpConstant %uint 2 + %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 + %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %41 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 +%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %26 = OpVariable %_ptr_Output__arr_v3uint_int_2 Output +%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %main1 = OpFunction %void None %3 + %4 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0 + OpStore %17 %19 + %28 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0 + OpStore %28 %29 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %34 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0 + OpStore %37 %38 + %40 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0 + OpStore %40 %41 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07058")); +} + +TEST_F(ValidateBuiltIns, BadVulkanMeshMultipleArraySizesSharedVariable) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main1 "main1" %26 %vertices_color + OpEntryPoint MeshEXT %main2 "main2" %26 %vertices_color_0 + OpExecutionMode %main1 OutputVertices 3 + OpExecutionMode %main1 OutputPrimitivesEXT 2 + OpExecutionMode %main1 LocalSize 1 1 1 + OpExecutionMode %main1 OutputTrianglesEXT + OpExecutionMode %main2 OutputVertices 3 + OpExecutionMode %main2 OutputPrimitivesEXT 4 + OpExecutionMode %main2 LocalSize 1 1 1 + OpExecutionMode %main2 OutputTrianglesEXT + OpSource Slang 1 + OpName %vertices_color "vertices.color" + OpName %main1 "main1" + OpName %vertices_color_0 "vertices.color" + OpName %main2 "main2" + OpDecorate %vertices_color Location 0 + OpDecorate %26 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %vertices_color_0 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 +%_arr_v4float_int_3 = OpTypeArray %v4float %int_3 +%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %uint_0 = OpConstant %uint 0 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %v3uint = OpTypeVector %uint 3 + %int_2 = OpConstant %int 2 +%_arr_v3uint_int_2 = OpTypeArray %v3uint %int_2 +%_ptr_Output__arr_v3uint_int_2 = OpTypePointer Output %_arr_v3uint_int_2 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %uint_2 = OpConstant %uint 2 + %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 + %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %41 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 +%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %26 = OpVariable %_ptr_Output__arr_v3uint_int_2 Output +%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output + %main1 = OpFunction %void None %3 + %4 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0 + OpStore %17 %19 + %28 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0 + OpStore %28 %29 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %34 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0 + OpStore %37 %38 + %40 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0 + OpStore %40 %41 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07058")); +} + +TEST_F(ValidateBuiltIns, BadExecModelVulkanPrimitivePointIndicesEXT) { + const std::string text = R"( + OpCapability MeshShadingNV + OpCapability MeshShadingEXT + OpExtension "SPV_NV_mesh_shader" + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshNV %main "main" %gl_PrimitivePointIndicesEXT + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputPoints + OpSource GLSL 460 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT" + OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32 +%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Output_uint = OpTypePointer Output %uint +%v3uint = OpTypeVector %uint 3 +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +%15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0 + OpStore %15 %uint_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinCullPrimitiveEXTInBlock) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTBlockArraySize) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %uint_16 = OpConstant %uint 16 + %uint_1 = OpConstant %uint 1 + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %gl_MeshPerPrimitiveEXT = OpTypeStruct %bool + %_arr_gl_MeshPerPrimitiveEXT_uint_16 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_16 + %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_16 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_16 + %gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_16 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_16 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-10590")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinCullPrimitiveEXTMissingBlock) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("Scalar boolean must be in a Block")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTType) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036")); +} + +// from https://github.com/KhronosGroup/SPIRV-Tools/issues/5980 +TEST_F(ValidateBuiltIns, VulkanBuiltinCullPrimitiveEXTArrayOfBool) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5 + OpExecutionMode %main LocalSize 2 1 1 + OpExecutionMode %main OutputTrianglesEXT + OpExecutionMode %main OutputVertices 2 + OpExecutionMode %main OutputPrimitivesEXT 2 + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %gl_Position BuiltIn Position + OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %5 BuiltIn CullPrimitiveEXT + OpDecorate %5 PerPrimitiveEXT + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %bool = OpTypeBool + %false = OpConstantFalse %bool +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 + %v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2 +%_arr_bool_uint_2 = OpTypeArray %bool %uint_2 +%_ptr_Output__arr_bool_uint_2 = OpTypePointer Output %_arr_bool_uint_2 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_Output_bool = OpTypePointer Output %bool +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output + %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output + %5 = OpVariable %_ptr_Output__arr_bool_uint_2 Output + %main = OpFunction %void None %21 + %23 = OpLabel + %24 = OpLoad %uint %gl_LocalInvocationIndex + OpSetMeshOutputsEXT %uint_2 %uint_2 + %25 = OpAccessChain %_ptr_Output_bool %5 %24 + OpStore %25 %false + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTArrayType) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5 + OpExecutionMode %main LocalSize 2 1 1 + OpExecutionMode %main OutputTrianglesEXT + OpExecutionMode %main OutputVertices 2 + OpExecutionMode %main OutputPrimitivesEXT 2 + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %gl_Position BuiltIn Position + OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %5 BuiltIn CullPrimitiveEXT + OpDecorate %5 PerPrimitiveEXT + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %bool = OpTypeBool + %false = OpConstantFalse %bool +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 + %v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2 +%_arr_uint_uint_2 = OpTypeArray %uint %uint_2 +%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_Output_uint = OpTypePointer Output %uint +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output + %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output + %5 = OpVariable %_ptr_Output__arr_uint_uint_2 Output + %main = OpFunction %void None %21 + %23 = OpLabel + %24 = OpLoad %uint %gl_LocalInvocationIndex + OpSetMeshOutputsEXT %uint_2 %uint_2 + %25 = OpAccessChain %_ptr_Output_uint %5 %24 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTArrayOfBoolSize) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5 + OpExecutionMode %main LocalSize 2 1 1 + OpExecutionMode %main OutputTrianglesEXT + OpExecutionMode %main OutputVertices 2 + OpExecutionMode %main OutputPrimitivesEXT 2 + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %gl_Position BuiltIn Position + OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %5 BuiltIn CullPrimitiveEXT + OpDecorate %5 PerPrimitiveEXT + %uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%uint_4 = OpConstant %uint 4 + %bool = OpTypeBool + %false = OpConstantFalse %bool +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 +%v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2 +%_arr_bool_uint_4 = OpTypeArray %bool %uint_4 +%_ptr_Output__arr_bool_uint_4 = OpTypePointer Output %_arr_bool_uint_4 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_Output_bool = OpTypePointer Output %bool +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output + %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output + %5 = OpVariable %_ptr_Output__arr_bool_uint_4 Output + %main = OpFunction %void None %21 + %23 = OpLabel + %24 = OpLoad %uint %gl_LocalInvocationIndex + OpSetMeshOutputsEXT %uint_2 %uint_2 + %25 = OpAccessChain %_ptr_Output_bool %5 %24 + OpStore %25 %false + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-10589")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTInterfaceVariable) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5 %gl_MeshPrimitivesEXT + OpExecutionMode %main LocalSize 2 1 1 + OpExecutionMode %main OutputTrianglesEXT + OpExecutionMode %main OutputVertices 2 + OpExecutionMode %main OutputPrimitivesEXT 2 + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %gl_Position BuiltIn Position + OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT + OpDecorate %5 BuiltIn CullPrimitiveEXT + OpDecorate %5 PerPrimitiveEXT + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT + %uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 + %bool = OpTypeBool +%false = OpConstantFalse %bool +%_ptr_Input_uint = OpTypePointer Input %uint +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 +%v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2 +%_arr_bool_uint_2 = OpTypeArray %bool %uint_2 +%_ptr_Output__arr_bool_uint_2 = OpTypePointer Output %_arr_bool_uint_2 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%_ptr_Output_bool = OpTypePointer Output %bool +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output + %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output + %5 = OpVariable %_ptr_Output__arr_bool_uint_2 Output +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_2 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_2 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_2 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_2 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_2 Output + %main = OpFunction %void None %21 + %23 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_2 %gl_MeshPrimitivesEXT + %24 = OpLoad %uint %gl_LocalInvocationIndex + OpSetMeshOutputsEXT %uint_2 %uint_2 + %25 = OpAccessChain %_ptr_Output_bool %5 %24 + OpStore %25 %false + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-10591")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTStorageClass) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Input %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Input +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035")); +} + +TEST_F(ValidateBuiltIns, BadBuiltinCullPrimitiveEXTWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + %ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07038")); +} + +TEST_F(ValidateBuiltIns, BadBuiltinPrimitiveShadingRateWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateKHR" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-07059")); +} + +TEST_F(ValidateBuiltIns, BadExecModelVulkanCullPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingNV + OpCapability MeshShadingEXT + OpExtension "SPV_NV_mesh_shader" + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshNV %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT + OpDecorate %gl_MeshPerPrimitiveEXT Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 + %bool = OpTypeBool + %int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_ptr_Output_bool = OpTypePointer Output %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Output_bool %gl_MeshPrimitivesEXT %int_0 %int_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinLayerInBlockMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_Layer" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn Layer + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinLayerAsArrayOfIntMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_Layer + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpDecorate %gl_Layer BuiltIn Layer + OpDecorate %gl_Layer PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_Layer_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Output__arr_gl_Layer_uint_32 = OpTypePointer Output %_arr_gl_Layer_uint_32 +%gl_Layer = OpVariable %_ptr_Output__arr_gl_Layer_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerArrayTypeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_Layer + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpDecorate %gl_Layer BuiltIn Layer + OpDecorate %gl_Layer PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_Layer_uint_32 = OpTypeArray %bool %uint_32 +%_ptr_Output__arr_gl_Layer_uint_32 = OpTypePointer Output %_arr_gl_Layer_uint_32 +%gl_Layer = OpVariable %_ptr_Output__arr_gl_Layer_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_Layer_uint_32 %gl_Layer + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10592")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerInBlockMeshEXTType) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_Layer" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn Layer + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + %ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10592")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerArrayOfIntSizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_Layer + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpDecorate %gl_Layer BuiltIn Layer + OpDecorate %gl_Layer PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_Layer_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Output__arr_gl_Layer_uint_32 = OpTypePointer Output %_arr_gl_Layer_uint_32 +%gl_Layer = OpVariable %_ptr_Output__arr_gl_Layer_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_Layer_uint_32 %gl_Layer + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10593")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerInBlockArraySizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_Layer" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn Layer + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10594")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_Layer + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_Layer BuiltIn Layer + %void = OpTypeVoid + %9 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 +%uint_0 = OpConstant %uint 0 +%v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_Layer = OpVariable %_ptr_Output__arr_int_int_1 Output +%MainMesh = OpFunction %void None %9 + %25 = OpLabel +%ref_load = OpLoad %_arr_int_int_1 %gl_Layer + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-07039")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveShadingRateKHRInBlockMeshEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 +%5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, + VulkanBuiltinPrimitiveShadingRateKHRInArrayOfIntMeshEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveShadingRateEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveShadingRateEXT "gl_PrimitiveShadingRateEXT" + OpDecorate %gl_PrimitiveShadingRateEXT BuiltIn PrimitiveShadingRateKHR + OpDecorate %gl_PrimitiveShadingRateEXT PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%uint_0 = OpConstant %uint 0 +%int_0 = OpConstant %int 0 +%bool = OpTypeBool +%_arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypePointer Output %_arr_gl_PrimitiveShadingRateEXT_uint_32 +%gl_PrimitiveShadingRateEXT = OpVariable %_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 Output +%uint_ptr = OpTypePointer Output %int +%main = OpFunction %void None %3 + %5 = OpLabel + %21 = OpAccessChain %uint_ptr %gl_PrimitiveShadingRateEXT %uint_0 + OpStore %21 %int_0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, + BadVulkanBuiltinPrimitiveShadingRateKHRInArrayTypeMeshEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveShadingRateEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveShadingRateEXT "gl_PrimitiveShadingRateEXT" + OpDecorate %gl_PrimitiveShadingRateEXT BuiltIn PrimitiveShadingRateKHR + OpDecorate %gl_PrimitiveShadingRateEXT PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypeArray %bool %uint_32 +%_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypePointer Output %_arr_gl_PrimitiveShadingRateEXT_uint_32 +%gl_PrimitiveShadingRateEXT = OpVariable %_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + %ref_load = OpLoad %_arr_gl_PrimitiveShadingRateEXT_uint_32 %gl_PrimitiveShadingRateEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598")); +} + +TEST_F(ValidateBuiltIns, + BadVulkanBuiltinPrimitiveShadingRateKHRInBlockTypeMeshEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 +%5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598")); +} + +TEST_F(ValidateBuiltIns, + BadVulkanBuiltinPrimitiveShadingRateKHRInBlockSizeMeshEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateEXT" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 +%5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10600")); +} + +TEST_F(ValidateBuiltIns, + BadVulkanBuiltinPrimitiveShadingRateKHRInArraySizeMeshEXT) { + const std::string text = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_PrimitiveShadingRateEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_PrimitiveShadingRateEXT "gl_PrimitiveShadingRateEXT" + OpDecorate %gl_PrimitiveShadingRateEXT BuiltIn PrimitiveShadingRateKHR + OpDecorate %gl_PrimitiveShadingRateEXT PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypePointer Output %_arr_gl_PrimitiveShadingRateEXT_uint_32 +%gl_PrimitiveShadingRateEXT = OpVariable %_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_PrimitiveShadingRateEXT_uint_32 %gl_PrimitiveShadingRateEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10599")); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinViewportIndexInBlockMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_ViewportIndex" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn ViewportIndex + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 +%5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, VulkanBuiltinViewportIndexAsArrayOfIntMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_ViewportIndex + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_ViewportIndex "gl_ViewportIndex" + OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex + OpDecorate %gl_ViewportIndex PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_ViewportIndex_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Output__arr_gl_ViewportIndex_uint_32 = OpTypePointer Output %_arr_gl_ViewportIndex_uint_32 +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_gl_ViewportIndex_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexInBlockTypeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_ViewportIndex" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn ViewportIndex + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 +%5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-ViewportIndex-ViewportIndex-10601")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexAsArrayTypeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_ViewportIndex + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_ViewportIndex "gl_ViewportIndex" + OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex + OpDecorate %gl_ViewportIndex PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_ViewportIndex_uint_32 = OpTypeArray %bool %uint_32 +%_ptr_Output__arr_gl_ViewportIndex_uint_32 = OpTypePointer Output %_arr_gl_ViewportIndex_uint_32 +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_gl_ViewportIndex_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_ViewportIndex_uint_32 %gl_ViewportIndex + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-ViewportIndex-ViewportIndex-10601")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexInBlockArraySizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT" + OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_ViewportIndex" + OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT" + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn ViewportIndex + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%main = OpFunction %void None %3 +%5 = OpLabel +%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-ViewportIndex-ViewportIndex-10603")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexAsArrayOfIntSizeMeshEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_ViewportIndex + OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 16 + OpExecutionMode %main OutputTrianglesEXT + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %gl_ViewportIndex "gl_ViewportIndex" + OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex + OpDecorate %gl_ViewportIndex PerPrimitiveEXT +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_1 = OpConstant %uint 1 +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%_arr_gl_ViewportIndex_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Output__arr_gl_ViewportIndex_uint_32 = OpTypePointer Output %_arr_gl_ViewportIndex_uint_32 +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_gl_ViewportIndex_uint_32 Output +%main = OpFunction %void None %3 + %5 = OpLabel +%ref_load = OpLoad %_arr_gl_ViewportIndex_uint_32 %gl_ViewportIndex + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-ViewportIndex-ViewportIndex-10602")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitiveIdFragmentWithRayTracing) { + const std::string text = R"( + OpCapability Shader + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outVar %gl_PrimitiveID + OpExecutionMode %main OriginUpperLeft + OpDecorate %outVar Location 0 + OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId + OpDecorate %gl_PrimitiveID Flat + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 +%ptrOut = OpTypePointer Output %v4int +%outVar = OpVariable %ptrOut Output + %ptrIn = OpTypePointer Input %int +%gl_PrimitiveID = OpVariable %ptrIn Input + %main = OpFunction %void None %4 + %6 = OpLabel + %13 = OpLoad %int %gl_PrimitiveID + %14 = OpCompositeConstruct %v4int %13 %13 %13 %13 + OpStore %outVar %14 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveId-Fragment-04333")); +} + +TEST_F(ValidateBuiltIns, TessellationMissingPatch) { + const std::string spirv = R"( + OpCapability Tessellation + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %main "main" %gl_TessLevelInner %gl_TessLevelOuter + OpExecutionMode %main OutputVertices 3 + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorate %gl_TessLevelOuter Patch + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 +%_ptr_Output_float = OpTypePointer Output %float + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output + %main = OpFunction %void None %4 + %6 = OpLabel + %17 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_0 + OpStore %17 %float_1 + %22 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_0 + OpStore %22 %float_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("BuiltIn TessLevelInner variable needs to also have a " + "Patch decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-TessLevelInner-10880")); +} + +// From dEQP-VK.mesh_shader.ext.builtin.primitive_id_spirv +TEST_F(ValidateBuiltIns, PrimitiveIdInFragmentWithMeshCapability) { + const std::string spirv = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %gl_PrimitiveID + OpExecutionMode %4 OriginUpperLeft + OpDecorate %9 Location 0 + OpDecorate %gl_PrimitiveID Flat + OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %9 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_PrimitiveID = OpVariable %_ptr_Input_int Input +%int_1629198956 = OpConstant %int 1629198956 + %bool = OpTypeBool + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %19 = OpConstantComposite %v4float %float_0 %float_0 %float_1 %float_1 + %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 + %v4bool = OpTypeVector %bool 4 + %4 = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %int %gl_PrimitiveID + %16 = OpIEqual %bool %13 %int_1629198956 + %22 = OpCompositeConstruct %v4bool %16 %16 %16 %16 + %23 = OpSelect %v4float %22 %19 %20 + OpStore %9 %23 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +// https://github.com/KhronosGroup/SPIRV-Tools/issues/6237 +TEST_F(ValidateBuiltIns, MeshBuiltinUnsignedInt) { + const std::string spirv = R"( + OpCapability FragmentShadingRateKHR + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_fragment_shading_rate" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesEXT 32 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveId + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 BuiltIn Layer + OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 BuiltIn ViewportIndex + OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 4 BuiltIn PrimitiveShadingRateKHR + OpMemberDecorate %gl_MeshPerPrimitiveEXT 4 PerPrimitiveEXT + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %int_0 = OpConstant %int 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_81 = OpConstant %uint 81 + %uint_32 = OpConstant %uint 32 +%gl_MeshPerPrimitiveEXT = OpTypeStruct %uint %uint %uint %bool %uint +%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output +%_ptr_Output_uint = OpTypePointer Output %uint + %main = OpFunction %void None %4 + %6 = OpLabel + OpSetMeshOutputsEXT %uint_81 %uint_32 + %20 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_0 + OpStore %20 %uint_1 + %22 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_1 + OpStore %22 %uint_2 + %24 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_2 + OpStore %24 %uint_3 + %26 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_4 + OpStore %26 %uint_4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateBuiltIns, HitTriangleVertexPositionExecutionModel) { + const std::string spirv = R"( + OpCapability RayTracingKHR + OpCapability RayTracingPositionFetchKHR + OpExtension "SPV_KHR_ray_tracing" + OpExtension "SPV_KHR_ray_tracing_position_fetch" + OpMemoryModel Logical GLSL450 + OpEntryPoint IntersectionKHR %main "main" %gl_HitTriangleVertexPositionsEXT + OpDecorate %gl_HitTriangleVertexPositionsEXT BuiltIn HitTriangleVertexPositionsKHR + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 +%gl_HitTriangleVertexPositionsEXT = OpVariable %_ptr_Input__arr_v3float_uint_3 Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %v19 = OpVariable %_ptr_Function_v3float Function + %18 = OpAccessChain %_ptr_Input_v3float %gl_HitTriangleVertexPositionsEXT %int_0 + %19 = OpLoad %v3float %18 + OpStore %v19 %19 + OpTerminateRayKHR + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Vulkan spec does not allow BuiltIn HitTriangleVertexPositionsKHR to " + "be used with the execution model IntersectionKHR")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-HitTriangleVertexPositionsKHR-" + "HitTriangleVertexPositionsKHR-08747")); +} + +TEST_F(ValidateBuiltIns, HitTriangleVertexPositionType) { + const std::string spirv = R"( + OpCapability RayTracingKHR + OpCapability RayTracingPositionFetchKHR + OpExtension "SPV_KHR_ray_tracing" + OpExtension "SPV_KHR_ray_tracing_position_fetch" + OpMemoryModel Logical GLSL450 + OpEntryPoint AnyHitKHR %main "main" %gl_HitTriangleVertexPositionsEXT + OpDecorate %gl_HitTriangleVertexPositionsEXT BuiltIn HitTriangleVertexPositionsKHR + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_v3float_uint_4 = OpTypeArray %v3float %uint_4 +%_ptr_Input__arr_v3float_uint_4 = OpTypePointer Input %_arr_v3float_uint_4 +%gl_HitTriangleVertexPositionsEXT = OpVariable %_ptr_Input__arr_v3float_uint_4 Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %v19 = OpVariable %_ptr_Function_v3float Function + %18 = OpAccessChain %_ptr_Input_v3float %gl_HitTriangleVertexPositionsEXT %int_0 + %19 = OpLoad %v3float %18 + OpStore %v19 %19 + OpTerminateRayKHR + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("array length must be 3")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-HitTriangleVertexPositionsKHR-" + "HitTriangleVertexPositionsKHR-08749")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 0d84caa..70fd98e 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp
@@ -116,6 +116,10 @@ using ValidateCapabilityVulkan11 = spvtest::ValidateBase<CapTestParameter>; // Always assembles using Vulkan 1.2. using ValidateCapabilityVulkan12 = spvtest::ValidateBase<CapTestParameter>; +// Always assembles using Vulkan 1.3. +using ValidateCapabilityVulkan13 = spvtest::ValidateBase<CapTestParameter>; +// Always assembles using Vulkan 1.4. +using ValidateCapabilityVulkan14 = spvtest::ValidateBase<CapTestParameter>; TEST_F(ValidateCapability, Default) { const char str[] = R"( @@ -325,6 +329,119 @@ return *r; } +const std::vector<std::string>& AllSpirV16Capabilities() { + static const auto r = new std::vector<std::string>{ + "", + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "Addresses", + "Linkage", + "Kernel", + "Vector16", + "Float16Buffer", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "Groups", + "DeviceEnqueue", + "LiteralSampler", + "AtomicStorage", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "GenericPointer", + "Int8", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageBuffer", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "SubgroupDispatch", + "NamedBarrier", + "PipeStorage", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers", + "DenormPreserve", + "DenormFlushToZero", + "SignedZeroInfNanPreserve", + "RoundingModeRTE", + "RoundingModeRTZ", + // Omitted due to extra validation requirements on memory model. + //"VulkanMemoryModel", + //"VulkanMemoryModelDeviceScope", + "StorageBuffer8BitAccess", + "UniformAndStorageBuffer8BitAccess", + "StoragePushConstant8", + "ShaderViewportIndex", + "ShaderLayer", + "PhysicalStorageBufferAddresses", + "RuntimeDescriptorArray", + "UniformTexelBufferArrayDynamicIndexing", + "StorageTexelBufferArrayDynamicIndexing", + "UniformBufferArrayNonUniformIndexing", + "SampledImageArrayNonUniformIndexing", + "StorageBufferArrayNonUniformIndexing", + "StorageImageArrayNonUniformIndexing", + "InputAttachmentArrayNonUniformIndexing", + "UniformTexelBufferArrayNonUniformIndexing", + "StorageTexelBufferArrayNonUniformIndexing", + "DotProduct", + "DotProductInputAll", + "DotProductInput4x8Bit", + "DotProductInput4x8BitPacked" + }; + return *r; +} + const std::vector<std::string>& AllSpirV10Capabilities() { static const auto r = new std::vector<std::string>{ "", @@ -586,6 +703,194 @@ return *r; } +const std::vector<std::string>& AllVulkan13Capabilities() { + static const auto r = new std::vector<std::string>{ + "", + "Matrix", + "Shader", + "InputAttachment", + "Sampled1D", + "Image1D", + "SampledBuffer", + "ImageBuffer", + "ImageQuery", + "DerivativeControl", + "Geometry", + "Tessellation", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "Int8", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "InterpolationFunction", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers", + "TransformFeedback", + "GeometryStreams", + "DenormPreserve", + "DenormFlushToZero", + "SignedZeroInfNanPreserve", + "RoundingModeRTE", + "RoundingModeRTZ", + "VulkanMemoryModel", + "VulkanMemoryModelDeviceScope", + "StorageBuffer8BitAccess", + "UniformAndStorageBuffer8BitAccess", + "StoragePushConstant8", + "ShaderViewportIndex", + "ShaderLayer", + "PhysicalStorageBufferAddresses", + "RuntimeDescriptorArray", + "UniformTexelBufferArrayDynamicIndexing", + "StorageTexelBufferArrayDynamicIndexing", + "UniformBufferArrayNonUniformIndexing", + "SampledImageArrayNonUniformIndexing", + "StorageBufferArrayNonUniformIndexing", + "StorageImageArrayNonUniformIndexing", + "InputAttachmentArrayNonUniformIndexing", + "UniformTexelBufferArrayNonUniformIndexing", + "StorageTexelBufferArrayNonUniformIndexing", + "DotProduct", + "DotProductInputAll", + "DotProductInput4x8Bit", + "DotProductInput4x8BitPacked", + "DemoteToHelperInvocation", + }; + return *r; +} + +const std::vector<std::string>& AllVulkan14Capabilities() { + static const auto r = new std::vector<std::string>{ + "", + "Matrix", + "Shader", + "InputAttachment", + "Sampled1D", + "Image1D", + "SampledBuffer", + "ImageBuffer", + "ImageQuery", + "DerivativeControl", + "Geometry", + "Tessellation", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "Int8", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "InterpolationFunction", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers", + "TransformFeedback", + "GeometryStreams", + "DenormPreserve", + "DenormFlushToZero", + "SignedZeroInfNanPreserve", + "RoundingModeRTE", + "RoundingModeRTZ", + "VulkanMemoryModel", + "VulkanMemoryModelDeviceScope", + "StorageBuffer8BitAccess", + "UniformAndStorageBuffer8BitAccess", + "StoragePushConstant8", + "ShaderViewportIndex", + "ShaderLayer", + "PhysicalStorageBufferAddresses", + "RuntimeDescriptorArray", + "UniformTexelBufferArrayDynamicIndexing", + "StorageTexelBufferArrayDynamicIndexing", + "UniformBufferArrayNonUniformIndexing", + "SampledImageArrayNonUniformIndexing", + "StorageBufferArrayNonUniformIndexing", + "StorageImageArrayNonUniformIndexing", + "InputAttachmentArrayNonUniformIndexing", + "UniformTexelBufferArrayNonUniformIndexing", + "StorageTexelBufferArrayNonUniformIndexing", + "DotProduct", + "DotProductInputAll", + "DotProductInput4x8Bit", + "DotProductInput4x8BitPacked", + "DemoteToHelperInvocation", + }; + return *r; +} + const std::vector<std::string>& MatrixDependencies() { static const auto r = new std::vector<std::string>{ "Matrix", @@ -1940,6 +2245,54 @@ AllVulkan12Capabilities()) ))); +INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan13, + Combine( + // All capabilities to try. + ValuesIn(AllSpirV16Capabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + "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), + AllVulkan13Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "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), + AllVulkan13Capabilities()) +))); + +INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan14, + Combine( + // All capabilities to try. + ValuesIn(AllSpirV16Capabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + "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), + AllVulkan14Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "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), + AllVulkan14Capabilities()) +))); + // TODO(umar): Selection Control // TODO(umar): Loop Control // TODO(umar): Function Control @@ -2017,24 +2370,16 @@ // TODO(umar): Instruction capability checks spv_result_t spvCoreOperandTableNameLookup(spv_target_env env, - const spv_operand_table table, const spv_operand_type_t type, const char* name, const size_t nameLength) { - if (!table) return SPV_ERROR_INVALID_TABLE; if (!name) return SPV_ERROR_INVALID_POINTER; - for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { - const auto& group = table->types[typeIndex]; - if (type != group.type) continue; - for (uint64_t index = 0; index < group.count; ++index) { - const auto& entry = group.entries[index]; - // Check for min version only. - if (spvVersionForTargetEnv(env) >= entry.minVersion && - nameLength == strlen(entry.name) && - !strncmp(entry.name, name, nameLength)) { - return SPV_SUCCESS; - } + const spvtools::OperandDesc* entry = nullptr; + if (SPV_SUCCESS == spvtools::LookupOperand(type, name, nameLength, &entry)) { + // Check for min version only. + if (spvVersionForTargetEnv(env) >= entry->minVersion) { + return SPV_SUCCESS; } } @@ -2045,8 +2390,7 @@ bool Exists(const std::string& capability, spv_target_env env) { ScopedContext sc(env); return SPV_SUCCESS == - spvCoreOperandTableNameLookup(env, sc.context->operand_table, - SPV_OPERAND_TYPE_CAPABILITY, + spvCoreOperandTableNameLookup(env, SPV_OPERAND_TYPE_CAPABILITY, capability.c_str(), capability.size()); } @@ -2114,6 +2458,28 @@ } } +TEST_P(ValidateCapabilityVulkan13, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_VULKAN_1_3)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_VULKAN_1_3)) + << test_code; + } +} + +TEST_P(ValidateCapabilityVulkan14, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_VULKAN_1_4)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_4); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_VULKAN_1_4)) + << test_code; + } +} + TEST_P(ValidateCapabilityOpenGL40, Capability) { const std::string capability = Capability(GetParam()); if (Exists(capability, SPV_ENV_OPENGL_4_0)) { @@ -2253,6 +2619,26 @@ HasSubstr("Capability DrawParameters is not allowed by Vulkan 1.0")); } +using OpenCLFloat16CapabilityBase = spvtest::ValidateBase<spv_target_env>; + +TEST_P(OpenCLFloat16CapabilityBase, OpenCLFloat16Capability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Float16 +OpMemoryModel Physical64 OpenCL +%f16 = OpTypeFloat 16 +)"; + CompileSuccessfully(spirv, GetParam()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam())); +} + +INSTANTIATE_TEST_SUITE_P(OpenCLFloat16Capability, OpenCLFloat16CapabilityBase, + ValuesIn(std::vector<spv_target_env>{ + SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0, + SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2})); + TEST_F(ValidateCapability, NonOpenCL12FullCapability) { const std::string spirv = R"( OpCapability Kernel @@ -2828,7 +3214,8 @@ : ""; return std::string("OpCapability ") + cap + extra_cap + R"( OpCapability Shader -OpMemoryModel Logical )" + mem_model + R"( +OpMemoryModel Logical )" + + mem_model + R"( OpEntryPoint Vertex %main "main" %void = OpTypeVoid %void_fn = OpTypeFunction %void @@ -2977,6 +3364,169 @@ EXPECT_THAT(getDiagnosticString(), Eq("")); } +TEST_F(ValidateCapability, + CooperativeMatrixKHRShaderRequiresVulkanMemoryModel) { + const auto spirv = R"( +OpCapability Shader +OpCapability CooperativeMatrixKHR +OpCapability Linkage +OpExtension "SPV_KHR_cooperative_matrix" +OpMemoryModel Logical GLSL450 +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "If the Shader and CooperativeMatrixKHR capabilities are declared, " + "the VulkanMemoryModel capability must also be declared")); +} + +TEST_F(ValidateCapability, TileShadingQCOM) { + const auto spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "main" +)" + std::string(kVoidFVoid); + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingQCOM capability must not be enabled " + "in any stage other than compute or fragment")); +} + +TEST_F(ValidateCapability, ColorAttachmentReadEXTRequireCapability) { + const std::string spirv = R"( +OpCapability Shader +OpCapability StorageImageReadWithoutFormat +OpExtension "SPV_EXT_shader_tile_image" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%f32 = OpTypeFloat 32 +%img_type_f32 = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_image_f32 = OpTypePointer UniformConstant %img_type_f32 +%uniform_image_f32 = OpVariable %ptr_image_f32 UniformConstant +%main = OpFunction %void None %voidfn +%entry = OpLabel +%img = OpLoad %img_type_f32 %uniform_image_f32 +%res1 = OpColorAttachmentReadEXT %f32 %img +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode ColorAttachmentReadEXT requires one of these " + "capabilities: TileImageColorReadAccessEXT")); +} + +TEST_F(ValidateCapability, DepthAttachmentReadEXTRequireCapability) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_EXT_shader_tile_image" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%f32 = OpTypeFloat 32 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%res1 = OpDepthAttachmentReadEXT %f32 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode DepthAttachmentReadEXT requires one of these " + "capabilities: TileImageDepthReadAccessEXT")); +} + +TEST_F(ValidateCapability, StencilAttachmentReadEXTRequireCapability) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_EXT_shader_tile_image" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%u32 = OpTypeInt 32 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%res1 = OpStencilAttachmentReadEXT %u32 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode StencilAttachmentReadEXT requires one of these " + "capabilities: TileImageStencilReadAccessEXT")); +} + +// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/11860#issuecomment-4047435834 +TEST_F(ValidateCapability, InputAttachmentArrayDynamicIndexingVulkan12) { + const std::string spirv = R"( + OpCapability Shader + OpCapability InputAttachment + OpCapability InputAttachmentArrayDynamicIndexing + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color %xs + OpExecutionMode %main OriginUpperLeft + OpDecorate %color Location 0 + OpDecorate %xs Binding 0 + OpDecorate %xs DescriptorSet 0 + OpDecorate %xs InputAttachmentIndex 0 + OpDecorate %index SpecId 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %11 = OpTypeImage %float SubpassData 0 0 0 2 Unknown + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_11_uint_4 = OpTypeArray %11 %uint_4 +%_ptr_UniformConstant__arr_11_uint_4 = OpTypePointer UniformConstant %_arr_11_uint_4 + %xs = OpVariable %_ptr_UniformConstant__arr_11_uint_4 UniformConstant + %int = OpTypeInt 32 1 + %index = OpSpecConstant %int 3 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %int_0 = OpConstant %int 0 + %v2int = OpTypeVector %int 2 + %24 = OpConstantComposite %v2int %int_0 %int_0 + %main = OpFunction %void None %4 + %6 = OpLabel + %20 = OpAccessChain %_ptr_UniformConstant_11 %xs %index + %21 = OpLoad %11 %20 + %25 = OpImageRead %v4float %21 %24 + OpStore %color %25 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index ae2e45b..cf595ac 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp
@@ -2600,20 +2600,20 @@ OpExecutionMode %1 LocalSize 1 1 1 OpSource HLSL 600 %bool = OpTypeBool -%_ptr_Function_bool = OpTypePointer Function %bool +%_ptr_Workgroup_bool = OpTypePointer Workgroup %bool + %7 = OpVariable %_ptr_Workgroup_bool Workgroup + %8 = OpVariable %_ptr_Workgroup_bool Workgroup %void = OpTypeVoid %5 = OpTypeFunction %void %1 = OpFunction %void None %5 %6 = OpLabel - %7 = OpVariable %_ptr_Function_bool Function - %8 = OpVariable %_ptr_Function_bool Function %9 = OpUndef %bool OpSelectionMerge %10 None OpBranchConditional %9 %11 %10 %11 = OpLabel OpBranch %10 %10 = OpLabel - %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11 + %12 = OpPhi %_ptr_Workgroup_bool %7 %6 %8 %11 OpReturn OpFunctionEnd )"; @@ -2625,7 +2625,7 @@ TEST_F(ValidateCFG, VarPtrStgBufShaderWithPhiStgBufPtr) { const std::string text = R"( OpCapability Shader - OpCapability VariablePointersStorageBuffer + OpCapability VariablePointers OpExtension "SPV_KHR_variable_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "main" @@ -4316,7 +4316,7 @@ OpMemoryModel Logical Simple OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft - + %void = OpTypeVoid %void_fn = OpTypeFunction %void @@ -5241,6 +5241,156 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateCFG, LifetimeGood) { + const std::string text = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int64 + OpCapability Int8 + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %func "main" + OpExecutionMode %func ContractionOff + OpDecorate %24 Alignment 4 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_arr_uint_4 = OpTypePointer Function %_arr_uint_4 + %uchar = OpTypeInt 8 0 +%_ptr_uchar = OpTypePointer Function %uchar + %14 = OpTypeFunction %void %_ptr_uchar +%_ptr_uint = OpTypePointer Function %uint + %bool = OpTypeBool +%uint_n = OpConstantNull %uint +%uint_1 = OpConstant %uint 1 + %func = OpFunction %void None %5 + %52 = OpLabel + %24 = OpVariable %_ptr_arr_uint_4 Function + %28 = OpSGreaterThan %bool %uint_1 %uint_n + OpBranchConditional %28 %53 %54 + %53 = OpLabel + %29 = OpBitcast %_ptr_uchar %24 + + OpLifetimeStart %29 16 + %30 = OpBitcast %_ptr_uint %24 + OpStore %30 %uint_1 Aligned 4 + %36 = OpBitcast %_ptr_uchar %24 + OpLifetimeStop %36 16 + + OpBranch %54 + %54 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, LifetimeStorageClass) { + const std::string text = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int64 + OpCapability Int8 + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %func "main" + OpExecutionMode %func ContractionOff + OpDecorate %24 Alignment 4 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_arr_uint_4 = OpTypePointer Function %_arr_uint_4 + %uchar = OpTypeInt 8 0 +%_ptr_uchar = OpTypePointer CrossWorkgroup %uchar + %14 = OpTypeFunction %void %_ptr_uchar +%_ptr_uint = OpTypePointer Function %uint + %bool = OpTypeBool +%uint_n = OpConstantNull %uint +%uint_1 = OpConstant %uint 1 + %func = OpFunction %void None %5 + %52 = OpLabel + %24 = OpVariable %_ptr_arr_uint_4 Function + %28 = OpSGreaterThan %bool %uint_1 %uint_n + OpBranchConditional %28 %53 %54 + %53 = OpLabel + %29 = OpBitcast %_ptr_uchar %24 + + OpLifetimeStart %29 16 + %30 = OpBitcast %_ptr_uint %24 + OpStore %30 %uint_1 Aligned 4 + %36 = OpBitcast %_ptr_uchar %24 + OpLifetimeStop %36 16 + + OpBranch %54 + %54 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpLifetimeStart pointer operand must be in the " + "Function storage class")); +} + +TEST_F(ValidateCFG, LifetimeNonPointer) { + const std::string text = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int64 + OpCapability Int8 + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %func "main" + OpExecutionMode %func ContractionOff + OpDecorate %24 Alignment 4 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_arr_uint_4 = OpTypePointer Function %_arr_uint_4 + %uchar = OpTypeInt 8 0 +%_ptr_uchar = OpTypePointer Function %uchar + %14 = OpTypeFunction %void %_ptr_uchar +%_ptr_uint = OpTypePointer Function %uint + %bool = OpTypeBool +%uint_n = OpConstantNull %uint +%uint_1 = OpConstant %uint 1 + %func = OpFunction %void None %5 + %52 = OpLabel + %24 = OpVariable %_ptr_arr_uint_4 Function + %28 = OpSGreaterThan %bool %uint_1 %uint_n + OpBranchConditional %28 %53 %54 + %53 = OpLabel + + OpLifetimeStart %28 16 + %30 = OpBitcast %_ptr_uint %24 + OpStore %30 %uint_1 Aligned 4 + %36 = OpBitcast %_ptr_uchar %24 + OpLifetimeStop %36 16 + + OpBranch %54 + %54 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpLifetimeStart pointer operand type must be a OpTypePointer")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp index 6e0d7c0..a6dcf83 100644 --- a/test/val/val_composites_test.cpp +++ b/test/val/val_composites_test.cpp
@@ -33,7 +33,9 @@ std::string GenerateShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", - const std::string& execution_model = "Fragment") { + const std::string& execution_model = "Fragment", + const std::string& extra_types = "", + const std::string& memory_model = "Logical GLSL450") { std::ostringstream ss; ss << R"( OpCapability Shader @@ -41,7 +43,7 @@ )"; ss << capabilities_and_extensions; - ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpMemoryModel " << memory_model << "\n"; ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; if (execution_model == "Fragment") { ss << "OpExecutionMode %main OriginUpperLeft\n"; @@ -93,7 +95,11 @@ %ptr_big_struct = OpTypePointer Uniform %big_struct %var_big_struct = OpVariable %ptr_big_struct Uniform +)"; + ss << extra_types; + + ss << R"( %main = OpFunction %void None %func %main_entry = OpLabel )"; @@ -315,6 +321,19 @@ HasSubstr("Expected number of constituents to be at least 2")); } +TEST_F(ValidateComposites, CompositeConstructLongVectorOnlyOneConstituent) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec4_0123 +)"; + const std::string caps = R"( +OpCapability LongVectorEXT +OpExtension "SPV_EXT_long_vector" +)"; + + CompileSuccessfully(GenerateShaderCode(body, caps).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent1) { const std::string body = R"( %val1 = OpCompositeConstruct %f32vec4 %f32 %f32vec2_12 @@ -523,6 +542,201 @@ "corresponding member type of Result Type struct")); } +TEST_F(ValidateComposites, CompositeConstructReplicateVectorGood) { + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32vec4 %f32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_EXT_replicated_composites\"\n") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateMatrixGood) { + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32mat22 %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_EXT_replicated_composites\"\n", + "Fragment") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateArrayGood) { + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32vec2arr3 %f32vec2_12 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_EXT_replicated_composites\"\n") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateStructGood) { + const std::string copy_types = R"( +%f32struct = OpTypeStruct %f32 %f32 %f32 +)"; + + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32struct %f32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_EXT_replicated_composites\"\n", + "Fragment", copy_types) + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateCoopMatGood) { + const std::string extra_types = R"( +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +%subgroup = OpConstant %u32 3 +%useA = OpConstant %u32 0 +%f32mat_nv = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_8 %u32_8 +%f32mat_khr = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_16 %u32_16 %useA +)"; + + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32mat_nv %f32_0 +%val2 = OpCompositeConstructReplicateEXT %f32mat_khr %f32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\n" + "OpCapability CooperativeMatrixNV\n" + "OpCapability CooperativeMatrixKHR\n" + "OpCapability VulkanMemoryModel\n" + "OpCapability Float16\n" + "OpExtension \"SPV_EXT_replicated_composites\"\n" + "OpExtension \"SPV_NV_cooperative_matrix\"\n" + "OpExtension \"SPV_KHR_cooperative_matrix\"\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n", + "Fragment", extra_types, "Logical Vulkan") + .c_str(), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateTensorGood) { + const std::string extra_types = R"( +%arr = OpTypeArray %u32 %u32_1 +%c_arr = OpConstantNull %arr +%tensor = OpTypeTensorARM %f32 %u32_1 %c_arr +)"; + + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %tensor %f32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpCapability " + "TensorsARM\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpExtension " + "\"SPV_ARM_tensors\"\n", + "Fragment", extra_types) + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateCoopMatWrongOperand) { + const std::string extra_types = R"( +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 +%f32mat_nv = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_8 %u32_8 +)"; + + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32mat_nv %u32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\n" + "OpCapability CooperativeMatrixNV\n" + "OpCapability Float16\n" + "OpExtension \"SPV_EXT_replicated_composites\"\n" + "OpExtension \"SPV_NV_cooperative_matrix\"\n", + "Fragment", extra_types) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Value type to be equal to the result's element type")); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateTensorWrongOperand) { + const std::string extra_types = R"( +%tensor = OpTypeTensorARM %f32 +)"; + + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %tensor %u32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpCapability " + "TensorsARM\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpExtension " + "\"SPV_ARM_tensors\"\n", + "Fragment", extra_types) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Value type to be equal to the result's element type")); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateWrongOperandType) { + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32vec4 %u32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_EXT_replicated_composites\"\n") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Value type to be equal to the result's element type")); +} + +TEST_F(ValidateComposites, CompositeConstructReplicateNotComposite) { + const std::string body = R"( +%val1 = OpCompositeConstructReplicateEXT %f32 %f32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, + "OpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_EXT_replicated_composites\"\n") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a composite type")); +} + TEST_F(ValidateComposites, CopyObjectSuccess) { const std::string body = R"( %val1 = OpCopyObject %f32 %f32_0 @@ -1565,9 +1779,10 @@ OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -1592,8 +1807,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -1606,9 +1821,10 @@ OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -1633,8 +1849,9 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected Constituent type to be equal to the component type")); @@ -2102,6 +2319,1414 @@ HasSubstr("OpCopyObject cannot have void result type")); } +TEST_F(ValidateComposites, CoopVecConstantCompositePass) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpExtension "SPV_NV_cooperative_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16 + +%f16_1 = OpConstant %f16 1 + +%f16vec_1 = OpConstantComposite %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CoopVecConstantCompositeMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpExtension "SPV_NV_cooperative_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16 + +%f32_1 = OpConstant %f32 1 + +%f16vec_1 = OpConstantComposite %f16vec %f32_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent <id> count does not match " + "Result Type <id> '11[%11]'s vector component count")); +} + +TEST_F(ValidateComposites, CoopVecCompositeConstructPass) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpExtension "SPV_NV_cooperative_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16 + +%f16_1 = OpConstant %f16 1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%f16vec_1 = OpCompositeConstruct %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CoopVecCompositeConstructMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpExtension "SPV_NV_cooperative_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16 + +%f32_1 = OpConstant %f32 1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%f16vec_1 = OpCompositeConstruct %f16vec %f32_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Constituents to be scalars or vectors of the " + "same type as Result Type components")); +} + +TEST_F(ValidateComposites, CoopVecInsertExtractDynamicPass) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpExtension "SPV_NV_cooperative_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_1 = OpConstant %u32 1 +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16 + +%f16_1 = OpConstant %f16 1 +%f16vec_1 = OpConstantComposite %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%insert = OpVectorInsertDynamic %f16vec %f16vec_1 %f16_1 %u32_1 +%extract = OpVectorExtractDynamic %f16 %insert %u32_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CoopMatVecConvBitCastArrayQCOM1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%u8int = OpTypeInt 8 0 +%uint = OpTypeInt 32 0 +%half = OpTypeFloat 16 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%_arr_u8int_uint_8 = OpTypeArray %u8int %uint_8 +%_arr_half_uint_16 = OpTypeArray %half %uint_16 +%_ptr_Function__arr_half_uint_16 = OpTypePointer Function %_arr_half_uint_16 +%main = OpFunction %void None %3 +%5 = OpLabel +%hvec8A = OpVariable %_ptr_Function__arr_half_uint_16 Function +%16 = OpLoad %_arr_half_uint_16 %hvec8A +%18 = OpBitCastArrayQCOM %_arr_u8int_uint_8 %16 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode BitCastArrayQCOM requires the result element " + "type be one of 32-bit OpTypeInt (signed/unsigned), " + "32-bit OpTypeFloat and 16-bit OpTypeFloat")); +} + +TEST_F(ValidateComposites, CoopMatVecConvBitCastArrayQCOM2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%u8int = OpTypeInt 8 0 +%uint = OpTypeInt 32 0 +;%half = OpTypeFloat 16 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_arr_u8int_uint_32 = OpTypeArray %u8int %uint_32 +%_ptr_Function__arr_u8int_uint_32 = OpTypePointer Function %_arr_u8int_uint_32 +%main = OpFunction %void None %3 +%5 = OpLabel +%u8A = OpVariable %_ptr_Function__arr_u8int_uint_32 Function +%16 = OpLoad %_arr_u8int_uint_32 %u8A +%18 = OpBitCastArrayQCOM %_arr_uint_uint_8 %16 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode BitCastArrayQCOM requires the source element " + "type be one of 32-bit OpTypeInt (signed/unsigned), " + "32-bit OpTypeFloat and 16-bit OpTypeFloat")); +} + +TEST_F(ValidateComposites, CoopMatVecConvBitCastArrayQCOM3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%half = OpTypeFloat 16 +%uint_16 = OpConstant %uint 16 +%_arr_uint_uint_16 = OpTypeArray %uint %uint_16 +%_arr_half_uint_16 = OpTypeArray %half %uint_16 +%_ptr_Function__arr_half_uint_16 = OpTypePointer Function %_arr_half_uint_16 +%main = OpFunction %void None %3 +%5 = OpLabel +%hvec8A = OpVariable %_ptr_Function__arr_half_uint_16 Function +%16 = OpLoad %_arr_half_uint_16 %hvec8A +%18 = OpBitCastArrayQCOM %_arr_uint_uint_16 %16 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode BitCastArrayQCOM requires source and result " + "types be compatible for conversion.")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractSubArrayQCOM1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_64 = OpTypeArray %uint %uint_64 +%_arr_float_uint_8 = OpTypeArray %float %uint_8 +%_ptr_Function__arr_uint_uint_64 = OpTypePointer Function %_arr_uint_uint_64 +%main = OpFunction %void None %3 +%5 = OpLabel +%uvec64Acc = OpVariable %_ptr_Function__arr_uint_uint_64 Function +%83 = OpLoad %_arr_uint_uint_64 %uvec64Acc +%86 = OpExtractSubArrayQCOM %_arr_float_uint_8 %83 %uint_0 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode ExtractSubArrayQCOM requires the input " + "and result element types match.")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMTy) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_32 = OpTypeArray %u8int %uint_32 +%_ptr_Function__arr_u8int_uint_32 = OpTypePointer Function %_arr_u8int_uint_32 +%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_64 %uint_8 %uint_0 +%main = OpFunction %void None %3 +%5 = OpLabel +%u8_32 = OpVariable %_ptr_Function__arr_u8int_uint_32 Function +%116 = OpLoad %_arr_u8int_uint_32 %u8_32 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires ether " + "the input element type is equal to the result element " + "type or it is the unsigned 32-bit integer.")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMA1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_64 %uint_8 %uint_0 +%main = OpFunction %void None %3 +%5 = OpLabel +%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function +%116 = OpLoad %_arr_uint_uint_8 %u32_8 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the result element type is one of 8-bit OpTypeInt " + "signed/unsigned, 16- or 32-bit OpTypeFloat when " + "result coopmat's use is MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMA2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_64 %uint_16 %uint_0 +%main = OpFunction %void None %3 +%5 = OpLabel +%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function +%116 = OpLoad %_arr_uint_uint_8 %u32_8 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the columns of the result coopmat have the bit " + "length of 256 when result coopmat's use is " + "MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMA3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_16 = OpTypeArray %u8int %uint_16 +%_ptr_Function__arr_u8int_uint_16 = OpTypePointer Function %_arr_u8int_uint_16 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_32 %uint_0 +%main = OpFunction %void None %3 +%5 = OpLabel +%u8_16 = OpVariable %_ptr_Function__arr_u8int_uint_16 Function +%116 = OpLoad %_arr_u8int_uint_16 %u8_16 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the source array length be 8 if its elt type is " + "32-bit unsigned OpTypeInt and be the result's " + "number of columns, otherwise when result coopmat's " + "use is MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMB1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_8 %uint_64 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function +%116 = OpLoad %_arr_uint_uint_8 %u32_8 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the result element type is one of 8-bit OpTypeInt " + "signed/unsigned, 16- or 32-bit OpTypeFloat when " + "result coopmat's use is MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMB2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_16 %uint_64 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function +%116 = OpLoad %_arr_uint_uint_8 %u32_8 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the rows of the result operand have the bit " + "length of 256 when result coopmat's use is " + "MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMB3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_16 = OpTypeArray %u8int %uint_16 +%_ptr_Function__arr_u8int_uint_16 = OpTypePointer Function %_arr_u8int_uint_16 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_32 %uint_64 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +%u8_16 = OpVariable %_ptr_Function__arr_u8int_uint_16 Function +%116 = OpLoad %_arr_u8int_uint_16 %u8_16 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the source array length be 8 if its elt type " + "is 32-bit unsigned OpTypeInt and be the result's " + "number of rows, otherwise when result coopmat's use is " + "MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMAcc1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_32 = OpTypeArray %u8int %uint_32 +%_ptr_Function__arr_u8int_uint_32 = OpTypePointer Function %_arr_u8int_uint_32 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_64 %uint_2 +%main = OpFunction %void None %3 +%5 = OpLabel +%u8_32 = OpVariable %_ptr_Function__arr_u8int_uint_32 Function +%116 = OpLoad %_arr_u8int_uint_32 %u8_32 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the result element type is one of 32-bit " + "OpTypeInt signed/unsigned, 16- or 32-bit " + "OpTypeFloat when result coopmat's use is " + "MatrixAccumulatorKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMAcc2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%half = OpTypeFloat 16 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_16 = OpConstant %uint 16 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_16 = OpTypeArray %uint %uint_16 +%_ptr_Function__arr_uint_uint_16 = OpTypePointer Function %_arr_uint_uint_16 +%113 = OpTypeCooperativeMatrixKHR %half %uint_3 %uint_64 %uint_64 %uint_2 +%main = OpFunction %void None %3 +%5 = OpLabel +%u32_16 = OpVariable %_ptr_Function__arr_uint_uint_16 Function +%116 = OpLoad %_arr_uint_uint_16 %u32_16 +%118 = OpCompositeConstructCoopMatQCOM %113 %116 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeConstructCoopMatQCOM requires " + "the source array length be a half of the number " + "of columns of the resulting cooerative matrix if " + "the matrix's componet type is 16-bit OpTypeFloat " + "and be equal to the number of columns, otherwise, " + "when result coopmat's use is " + "MatrixAccumulatorKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_64 %uint_8 %uint_0 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u32_64_8 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "the source element type be one of 8-bit " + "OpTypeInt signed/unsigned, 16- or 32-bit " + "OpTypeFloat when source coopmat's use is " + "MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_64 %uint_16 %uint_0 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u32_64_8 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM " + "requires the columns of the source " + "coopmat have the bit length of 256 " + "when source coopmat's use is " + "MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_64 = OpTypeArray %u8int %uint_64 +%_ptr_Function__arr_u8int_uint_64 = OpTypePointer Function %_arr_u8int_uint_64 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_32 %uint_0 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u8_64_32 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u8_64_32 +%251 = OpCompositeExtractCoopMatQCOM %_arr_u8int_uint_64 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "either the result element type be the same as " + "the source cooperative matrix's component type " + "and its length be the same as the number of " + "columns of the matrix or the result element " + "type be unsigned 32-bit OpTypeInt and the " + "length be 8 when source coopmat's use is " + "MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA4) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_16 = OpTypeArray %uint %uint_16 +%_ptr_Function__arr_uint_uint_16 = OpTypePointer Function %_arr_uint_uint_16 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_32 %uint_0 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u8_64_32 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u8_64_32 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_16 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "either the result element type be the same as " + "the source cooperative matrix's component type " + "and its length be the same as the number of " + "columns of the matrix or the result element " + "type be unsigned 32-bit OpTypeInt and the " + "length be 8 when source coopmat's use is " + "MatrixAKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_8 %uint_64 %uint_1 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u32_64_8 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM " + "requires the source element type be " + "one of 8-bit OpTypeInt signed/unsigned, " + "16- or 32-bit OpTypeFloat when source " + "coopmat's use is " + "MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_16 %uint_64 %uint_1 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u32_64_8 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "the rows of the source coopmat have the bit " + "length of 256 when source coopmat's use is " + "MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_16 = OpTypeArray %u8int %uint_16 +%_ptr_Function__arr_u8int_uint_16 = OpTypePointer Function %_arr_u8int_uint_16 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_32 %uint_64 %uint_1 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u8_16_64 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u8_16_64 +%251 = OpCompositeExtractCoopMatQCOM %_arr_u8int_uint_16 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "either the result element type be the same as " + "the source cooperative matrix's component type " + "and its length be the same as the number of " + "rows of the matrix or the result element " + "type be unsigned 32-bit OpTypeInt and the " + "length be 8 when source coopmat's use is " + "MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB4) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%u8int = OpTypeInt 8 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%uint_16 = OpConstant %uint 16 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_16 = OpTypeArray %uint %uint_16 +%_ptr_Function__arr_uint_uint_16 = OpTypePointer Function %_arr_uint_uint_16 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_32 %uint_64 %uint_1 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u8_32_64 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u8_32_64 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_16 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "either the result element type be the same as " + "the source cooperative matrix's component type " + "and its length be the same as the number of " + "rows of the matrix or the result element " + "type be unsigned 32-bit OpTypeInt and the " + "length be 8 when source coopmat's use is " + "MatrixBKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMAcc1) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%u8int = OpTypeInt 8 0 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_8 = OpTypeArray %uint %uint_8 +%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8 +%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_64 %uint_2 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u32_64_64 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u32_64_64 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM " + "requires the source element type be " + "one of 32-bit OpTypeInt signed/unsigned, " + "16- or 32-bit OpTypeFloat when source " + "coopmat's use is " + "MatrixAccumulatorKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMAcc2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_32 = OpConstant %uint 32 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%_ptr_Function__arr_uint_uint_32 = OpTypePointer Function %_arr_uint_uint_32 +%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_64 %uint_64 %uint_2 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_u32_64_64 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_u32_64_64 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_32 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "either the result element type be the same as " + "the source cooperative matrix's component type " + "and its length be the same as the number of " + "columns of the matrix or the result element " + "type be unsigned 32-bit OpTypeInt and the length " + "be the number of the columns of the matrix if " + "its component type is 32-bit OpTypeFloat and be " + "a half of the number of the columns of the matrix " + "if its component type is 16-bit OpTypeFloat when " + "source coopmat's use is " + "MatrixAccumulatorKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMAcc3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%half = OpTypeFloat 16 +%uint_0 = OpConstant %uint 0 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_64 = OpConstant %uint 64 +%_arr_uint_uint_64 = OpTypeArray %uint %uint_64 +%_ptr_Function__arr_uint_uint_64 = OpTypePointer Function %_arr_uint_uint_64 +%113 = OpTypeCooperativeMatrixKHR %half %uint_3 %uint_64 %uint_64 %uint_2 +%_ptr_Function_113 = OpTypePointer Function %113 +%main = OpFunction %void None %3 +%5 = OpLabel +%matA_f16_64_64 = OpVariable %_ptr_Function_113 Function +%249 = OpLoad %113 %matA_f16_64_64 +%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_64 %249 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode CompositeExtractCoopMatQCOM requires " + "either the result element type be the same as " + "the source cooperative matrix's component type " + "and its length be the same as the number of " + "columns of the matrix or the result element " + "type be unsigned 32-bit OpTypeInt and the length " + "be the number of the columns of the matrix if " + "its component type is 32-bit OpTypeFloat and be " + "a half of the number of the columns of the matrix " + "if its component type is 16-bit OpTypeFloat when " + "source coopmat's use is " + "MatrixAccumulatorKHR")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractSubArrayQCOM2) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int8 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%u8int = OpTypeInt 8 0 +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_u8int_uint_8 = OpTypeArray %u8int %uint_8 +%_arr_u8int_uint_64 = OpTypeArray %u8int %uint_64 +%_ptr_Function__arr_u8int_uint_64 = OpTypePointer Function %_arr_u8int_uint_64 +%main = OpFunction %void None %3 +%5 = OpLabel +%uvec64Acc = OpVariable %_ptr_Function__arr_u8int_uint_64 Function +%83 = OpLoad %_arr_u8int_uint_64 %uvec64Acc +%86 = OpExtractSubArrayQCOM %_arr_u8int_uint_8 %83 %uint_0 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode ExtractSubArrayQCOM requires the element type " + "be one of 32-bit OpTypeInt (signed/unsigned), 32-bit " + "OpTypeFloat and 16-bit OpTypeFloat")); +} + +TEST_F(ValidateComposites, CoopMatVecConvExtractSubArrayQCOM3) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%ui16 = OpTypeInt 16 0 +%uint = OpTypeInt 32 0 +%f16 = OpTypeFloat 16 +%u16int_0 = OpConstant %ui16 0 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_f16_uint_8 = OpTypeArray %f16 %uint_8 +%_arr_f16_uint_64 = OpTypeArray %f16 %uint_64 +%_ptr_Function__arr_f16_uint_64 = OpTypePointer Function %_arr_f16_uint_64 +%main = OpFunction %void None %3 +%5 = OpLabel +%f16vec64Acc = OpVariable %_ptr_Function__arr_f16_uint_64 Function +%83 = OpLoad %_arr_f16_uint_64 %f16vec64Acc +%86 = OpExtractSubArrayQCOM %_arr_f16_uint_8 %83 %u16int_0 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode ExtractSubArrayQCOM requires the type of the " + "start index operand be 32-bit OpTypeInt")); +} + +TEST_F(ValidateComposites, VectorShuffleNotVectorOp1) { + const std::string spirv = R"( +OpCapability ClipDistance +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%67 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v3float = OpTypeVector %float 3 +%_ptr_Function_float = OpTypePointer Function %float +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%3 = OpVariable %_ptr_Input_v4float Input +%func = OpFunction %void None %67 +%label = OpLabel +%43 = OpVariable %_ptr_Function_float Function +%373 = OpLoad %v4float %3 +%shuffle = OpVectorShuffle %v2float %1 %373 538976288 538976288 +%422 = OpLoad %v3float %43 +OpReturnValue %422 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of Vector 1 must be a vector type")); +} + +TEST_F(ValidateComposites, VectorShuffleNotVectorOp2) { + const std::string spirv = R"( +OpCapability ClipDistance +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%67 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v3float = OpTypeVector %float 3 +%_ptr_Function_float = OpTypePointer Function %float +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%3 = OpVariable %_ptr_Input_v4float Input +%func = OpFunction %void None %67 +%label = OpLabel +%43 = OpVariable %_ptr_Function_float Function +%373 = OpLoad %v4float %3 +%shuffle = OpVectorShuffle %v2float %373 %1 538976288 538976288 +%422 = OpLoad %v3float %43 +OpReturnValue %422 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of Vector 2 must be a vector type")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_constants_test.cpp b/test/val/val_constants_test.cpp index 4727877..d447b5c 100644 --- a/test/val/val_constants_test.cpp +++ b/test/val/val_constants_test.cpp
@@ -37,28 +37,39 @@ using ValidateConstant = spvtest::ValidateBase<bool>; -#define kBasicTypes \ - "%bool = OpTypeBool " \ - "%uint = OpTypeInt 32 0 " \ - "%uint2 = OpTypeVector %uint 2 " \ - "%float = OpTypeFloat 32 " \ - "%_ptr_uint = OpTypePointer Workgroup %uint " \ - "%uint_0 = OpConstantNull %uint " \ - "%uint2_0 = OpConstantNull %uint " \ - "%float_0 = OpConstantNull %float " \ - "%false = OpConstantFalse %bool " \ - "%true = OpConstantTrue %bool " \ +#define kBasicTypes \ + "%bool = OpTypeBool " \ + "%uint = OpTypeInt 32 0 " \ + "%uint64 = OpTypeInt 64 0 " \ + "%uint2 = OpTypeVector %uint 2 " \ + "%float = OpTypeFloat 32 " \ + "%float64 = OpTypeFloat 64 " \ + "%_ptr_uint = OpTypePointer Workgroup %uint " \ + "%uint_0 = OpConstantNull %uint " \ + "%uint64_0 = OpConstantNull %uint64 " \ + "%uint2_0 = OpConstantComposite %uint2 %uint_0 %uint_0 " \ + "%float_0 = OpConstantNull %float " \ + "%float64_0 = OpConstantNull %float64 " \ + "%false = OpConstantFalse %bool " \ + "%true = OpConstantTrue %bool " \ "%null = OpConstantNull %_ptr_uint " -#define kShaderPreamble \ - "OpCapability Shader\n" \ - "OpCapability Linkage\n" \ +#define kShaderPreamble \ + "OpCapability Shader\n" \ + "OpCapability Linkage\n" \ + "OpCapability Int64\n" \ + "OpCapability Float64\n" \ + "OpCapability VariablePointers\n" \ + "OpExtension \"SPV_KHR_variable_pointers\"\n" \ "OpMemoryModel Logical Simple\n" -#define kKernelPreamble \ - "OpCapability Kernel\n" \ - "OpCapability Linkage\n" \ - "OpCapability Addresses\n" \ +#define kKernelPreamble \ + "OpCapability Kernel\n" \ + "OpCapability Linkage\n" \ + "OpCapability Int64\n" \ + "OpCapability Float64\n" \ + "OpCapability GenericPointer\n" \ + "OpCapability Addresses\n" \ "OpMemoryModel Physical32 OpenCL\n" struct ConstantOpCase { @@ -66,6 +77,7 @@ std::string assembly; bool expect_success; std::string expect_err; + spv_result_t expected_result = SPV_ERROR_INVALID_ID; }; using ValidateConstantOp = spvtest::ValidateBase<ConstantOpCase>; @@ -78,7 +90,7 @@ EXPECT_EQ(SPV_SUCCESS, result); EXPECT_THAT(getDiagnosticString(), Eq("")); } else { - EXPECT_EQ(SPV_ERROR_INVALID_ID, result); + EXPECT_EQ(GetParam().expected_result, result); EXPECT_THAT(getDiagnosticString(), HasSubstr(GetParam().expect_err)); } } @@ -90,9 +102,8 @@ INSTANTIATE_TEST_SUITE_P( UniversalInShader, ValidateConstantOp, ValuesIn(std::vector<ConstantOpCase>{ - // TODO(dneto): Conversions must change width. - GOOD_SHADER_10("%v = OpSpecConstantOp %uint SConvert %uint_0"), - GOOD_SHADER_10("%v = OpSpecConstantOp %float FConvert %float_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint SConvert %uint64_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %float FConvert %float64_0"), GOOD_SHADER_10("%v = OpSpecConstantOp %uint SNegate %uint_0"), GOOD_SHADER_10("%v = OpSpecConstantOp %uint Not %uint_0"), GOOD_SHADER_10("%v = OpSpecConstantOp %uint IAdd %uint_0 %uint_0"), @@ -147,9 +158,8 @@ INSTANTIATE_TEST_SUITE_P( UniversalInKernel, ValidateConstantOp, ValuesIn(std::vector<ConstantOpCase>{ - // TODO(dneto): Conversions must change width. - GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SConvert %uint_0"), - GOOD_KERNEL_10("%v = OpSpecConstantOp %float FConvert %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SConvert %uint64_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FConvert %float64_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SNegate %uint_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %uint Not %uint_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %uint IAdd %uint_0 %uint_0"), @@ -204,70 +214,64 @@ INSTANTIATE_TEST_SUITE_P( UConvert, ValidateConstantOp, ValuesIn(std::vector<ConstantOpCase>{ - // TODO(dneto): Conversions must change width. {SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, {SPV_ENV_UNIVERSAL_1_1, kKernelPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, {SPV_ENV_UNIVERSAL_1_3, kKernelPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, {SPV_ENV_UNIVERSAL_1_3, kKernelPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, {SPV_ENV_UNIVERSAL_1_4, kKernelPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, {SPV_ENV_UNIVERSAL_1_0, kShaderPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", false, "Prior to SPIR-V 1.4, specialization constant operation " "UConvert requires Kernel capability"}, {SPV_ENV_UNIVERSAL_1_1, kShaderPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", false, "Prior to SPIR-V 1.4, specialization constant operation " "UConvert requires Kernel capability"}, {SPV_ENV_UNIVERSAL_1_3, kShaderPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", false, "Prior to SPIR-V 1.4, specialization constant operation " "UConvert requires Kernel capability"}, {SPV_ENV_UNIVERSAL_1_3, kShaderPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", false, "Prior to SPIR-V 1.4, specialization constant operation " "UConvert requires Kernel capability"}, {SPV_ENV_UNIVERSAL_1_4, kShaderPreamble kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, })); INSTANTIATE_TEST_SUITE_P( KernelInKernel, ValidateConstantOp, ValuesIn(std::vector<ConstantOpCase>{ - // TODO(dneto): Conversions must change width. GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ConvertFToS %float_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %float ConvertSToF %uint_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ConvertFToU %float_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %float ConvertUToF %uint_0"), - GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UConvert %uint_0"), - GOOD_KERNEL_10( - "%v = OpSpecConstantOp %_ptr_uint GenericCastToPtr %null"), - GOOD_KERNEL_10( - "%v = OpSpecConstantOp %_ptr_uint PtrCastToGeneric %null"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UConvert %uint64_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %uint Bitcast %uint_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %float FNegate %float_0"), GOOD_KERNEL_10("%v = OpSpecConstantOp %float FAdd %float_0 %float_0"), @@ -295,7 +299,8 @@ INSTANTIATE_TEST_SUITE_P( KernelInShader, ValidateConstantOp, ValuesIn(std::vector<ConstantOpCase>{ - // TODO(dneto): Conversions must change width. + // Don't need to test GenericCastToPtr or PtrCastToGeneric as a valid + // module can't have a Generic storage class with Shader capability BAD_SHADER_10("%v = OpSpecConstantOp %uint ConvertFToS %float_0", "ConvertFToS"), BAD_SHADER_10("%v = OpSpecConstantOp %float ConvertSToF %uint_0", @@ -304,10 +309,6 @@ "ConvertFToU"), BAD_SHADER_10("%v = OpSpecConstantOp %float ConvertUToF %uint_0", "ConvertUToF"), - BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint GenericCastToPtr %null", - "GenericCastToPtr"), - BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint PtrCastToGeneric %null", - "PtrCastToGeneric"), BAD_SHADER_10("%v = OpSpecConstantOp %uint Bitcast %uint_0", "Bitcast"), BAD_SHADER_10("%v = OpSpecConstantOp %float FNegate %float_0", "FNegate"), @@ -343,11 +344,15 @@ // SPV_AMD_gpu_shader_int16 should enable UConvert for OpSpecConstantOp // https://github.com/KhronosGroup/glslang/issues/848 {SPV_ENV_UNIVERSAL_1_0, - "OpCapability Shader " + "OpCapability Shader\n" + "OpCapability Int64\n" + "OpCapability Float64\n" + "OpCapability VariablePointers\n" "OpCapability Linkage ; So we don't need to define a function\n" - "OpExtension \"SPV_AMD_gpu_shader_int16\" " + "OpExtension \"SPV_AMD_gpu_shader_int16\"\n" + "OpExtension \"SPV_KHR_variable_pointers\"\n" "OpMemoryModel Logical Simple " kBasicTypes - "%v = OpSpecConstantOp %uint UConvert %uint_0", + "%v = OpSpecConstantOp %uint UConvert %uint64_0", true, ""}, })); @@ -490,8 +495,666 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpConstantComposite Constituent <id> '13[%13]'s type " - "does not match Result Type <id> '3[%v2uint]'s vector element type")); + "OpConstantComposite Constituent <id> '17[%17]'s type " + "does not match Result Type <id> '4[%v2uint]'s vector element type")); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateVectorGood) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%v4int = OpTypeVector %int 4 +%int_0 = OpConstant %int 0 +%const_vector = OpConstantCompositeReplicateEXT %v4int %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConstant, SpecConstantCompositeReplicateVectorGood) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%v4int = OpTypeVector %int 4 +%int_0 = OpSpecConstant %int 0 +%const_vector = OpSpecConstantCompositeReplicateEXT %v4int %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateMatrixGood) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%v_0 = OpConstantNull %v2float +%const_matrix = OpConstantCompositeReplicateEXT %mat2x2 %v_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateArrayGood) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%int_4 = OpConstant %int 4 +%arr = OpTypeArray %int %int_4 +%int_0 = OpConstantNull %int +%const_arr = OpConstantCompositeReplicateEXT %arr %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateStructGood) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%struct = OpTypeStruct %int %int %int +%int_0 = OpConstantNull %int +%const_struct = OpConstantCompositeReplicateEXT %struct %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateWrongOperandType) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%v4int = OpTypeVector %int 4 +%const_vector = OpConstantCompositeReplicateEXT %v4int %float_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpConstantCompositeReplicateEXT Constituent <id> '11[%11]'s type " + "does not match Result Type <id> '17[%v4int]'s element type")); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateSpecOperand) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%int_4 = OpConstant %int 4 +%arr = OpTypeArray %int %int_4 +%int_0 = OpSpecConstant %int 0 +%const_arr = OpConstantCompositeReplicateEXT %arr %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantCompositeReplicateEXT must not have spec " + "constant operands: <id>")); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateNotConstant) { + std::string spirv = + std::string( + "OpCapability Kernel\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability Addresses\nOpCapability " + "ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Physical64 " + "OpenCL\n") + + kBasicTypes + R"( +%uint_4 = OpConstant %uint 4 +%ptr = OpTypePointer Private %uint +%var = OpVariable %ptr Private +%arr = OpTypeArray %ptr %uint_4 +%const_arr = OpConstantCompositeReplicateEXT %arr %var +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantCompositeReplicateEXT must only have " + "constant or undef operands: <id>")); +} + +TEST_F(ValidateConstant, ConstantCompositeSpecOperand) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%int_4 = OpConstant %int 4 +%arr = OpTypeArray %int %int_4 +%int_0 = OpSpecConstant %int 0 +%const_arr = OpConstantComposite %arr %int_0 %int_0 %int_0 %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpConstantComposite must not have spec constant operands: <id>")); +} + +TEST_F(ValidateConstant, ConstantCompositeNotConstant) { + std::string spirv = + std::string( + "OpCapability Kernel\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability Addresses\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpMemoryModel Physical64 OpenCL\n") + + kBasicTypes + R"( +%uint_4 = OpConstant %uint 4 +%ptr = OpTypePointer Private %uint +%var = OpVariable %ptr Private +%arr = OpTypeArray %ptr %uint_4 +%const_arr = OpConstantComposite %arr %var %var %var %var +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite must only have constant or undef " + "operands: <id>")); +} + +TEST_F(ValidateConstant, ConstantCompositeReplicateNotComposite) { + std::string spirv = + std::string( + "OpCapability Shader\nOpCapability Linkage\nOpCapability " + "Int64\nOpCapability Float64\nOpCapability " + "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension " + "\"SPV_KHR_variable_pointers\"\nOpExtension " + "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") + + kBasicTypes + R"( +%int = OpTypeInt 32 1 +%int_0 = OpConstantNull %int +%const_vector = OpConstantCompositeReplicateEXT %float %int_0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantCompositeReplicateEXT Result Type <id> '5[%float]' " + "is not a composite type")); +} + +TEST_F(ValidateConstant, BadShaderOperandsQuantizeToF16) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%uint_1 = OpConstant %uint 1 +%float_1 = OpConstant %float 1 +%good = OpSpecConstantOp %float QuantizeToF16 %float_1 +%bad1 = OpSpecConstantOp %uint QuantizeToF16 %float_1 +%bad2 = OpSpecConstantOp %float QuantizeToF16 %uint_1 +)"; + + CompileSuccessfully(spirv); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit float scalar or vector type as Result Type")); +} + +TEST_F(ValidateConstant, BadCooperativeMatrixLength) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%uint_1 = OpConstant %uint 1 +%uint_8 = OpConstant %uint 8 +%float_1 = OpConstant %float 1 +%subgroup = OpConstant %uint 3 +%use_A = OpConstant %uint 0 +%f16mat = OpTypeCooperativeMatrixKHR %float %subgroup %uint_8 %uint_8 %use_A + +%good = OpSpecConstantOp %uint CooperativeMatrixLengthKHR %f16mat +%bad1 = OpSpecConstantOp %uint CooperativeMatrixLengthKHR %float_1 +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be OpTypeCooperativeMatrixKHR")); +} + +// Some check use SPV_ERROR_INVALID_DATA vs SPV_ERROR_INVALID_ID +#define BAD_KERNEL_OPERANDS(STR, ERR) \ + { \ + SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, false, ERR, \ + SPV_ERROR_INVALID_DATA \ + } + +#define BAD_KERNEL_OPERANDS_ID(STR, ERR) \ + { SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, false, ERR, } + +// 2 of each, first has bad return type, second has bad operand +INSTANTIATE_TEST_SUITE_P( + BadOperandsKernel, ValidateConstantOp, + ValuesIn(std::vector<ConstantOpCase>{ + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SConvert %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint SConvert %uint_0", + "Expected input to have different bit width from Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FConvert %float_0", + "Expected float scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float FConvert %float_0", + "Expected component type of Value to be different " + "from component type of Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float UConvert %uint_0", + "Expected unsigned int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint UConvert %uint_0", + "Expected input to have different bit width from Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ConvertFToS %float_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint ConvertFToS %uint2_0", + "Expected input to be float scalar or vector"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint ConvertSToF %uint_0", + "Expected float scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float ConvertSToF %float_0", + "Expected input to be int scalar or vector"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ConvertFToU %float_0", + "Expected unsigned int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint ConvertFToU %uint2_0", + "Expected input to be float scalar or vector"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint ConvertUToF %uint_0", + "Expected float scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float ConvertUToF %float_0", + "Expected input to be int scalar or vector"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint ConvertUToF %uint_0", + "Expected float scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float ConvertUToF %float_0", + "Expected input to be int scalar or vector"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float UDiv %uint_0 %uint_0", + "Expected unsigned int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint UDiv %uint_0 %float_0", + "Expected arithmetic operands to be of Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float UMod %uint_0 %uint_0", + "Expected unsigned int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint UMod %uint_0 %float_0", + "Expected arithmetic operands to be of Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ISub %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint ISub %uint_0 %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float IAdd %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint IAdd %uint_0 %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float IMul %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint IMul %uint_0 %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SDiv %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SDiv %uint_0 %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SRem %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SRem %uint_0 %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SMod %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SMod %uint_0 %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SNegate %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SNegate %float_0", + "Expected int scalar or vector type as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FAdd %float_0 %float_0", + "Expected floating scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float FAdd %float_0 %uint_0", + "Expected arithmetic operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FSub %float_0 %float_0", + "Expected floating scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float FSub %float_0 %uint_0", + "Expected arithmetic operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FMul %float_0 %float_0", + "Expected floating scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float FMul %float_0 %uint_0", + "Expected arithmetic operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FDiv %float_0 %float_0", + "Expected floating scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float FDiv %float_0 %uint_0", + "Expected arithmetic operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FRem %float_0 %float_0", + "Expected floating scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float FRem %float_0 %uint_0", + "Expected arithmetic operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint FMod %float_0 %float_0", + "Expected floating scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float FMod %float_0 %uint_0", + "Expected arithmetic operands to be of Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ShiftRightLogical %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint ShiftRightLogical %uint_0 %float_0", + "Expected Shift to be int scalar or vector"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ShiftRightArithmetic %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint ShiftRightArithmetic %uint_0 %float_0", + "Expected Shift to be int scalar or vector"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ShiftLeftLogical %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint ShiftLeftLogical %uint_0 %float_0", + "Expected Shift to be int scalar or vector"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float BitwiseOr %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint BitwiseOr %uint_0 %float_0", + "Expected int scalar or vector as operand"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float BitwiseXor %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint BitwiseXor %uint_0 %float_0", + "Expected int scalar or vector as operand"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float BitwiseAnd %uint_0 %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint BitwiseAnd %uint_0 %float_0", + "Expected int scalar or vector as operand"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float Not %uint_0", + "Expected int scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint Not %float_0", + "Expected int scalar or vector as operand"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float LogicalOr %true %false", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool LogicalOr %true %uint_0", + "Expected both operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float LogicalAnd %true %false", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool LogicalAnd %true %uint_0", + "Expected both operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float LogicalEqual %true %false", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool LogicalEqual %true %uint_0", + "Expected both operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float LogicalNotEqual %true %false", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool LogicalNotEqual %uint_0 %false", + "Expected both operands to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float LogicalNot %true", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %bool LogicalNot %uint_0", + "Expected operand to be of Result Type"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float Select %true %uint_0 %uint_0", + "Expected both objects to be of Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint Select %uint_0 %uint_0 %uint_0", + "Expected bool scalar or vector type as condition"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float IEqual %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool IEqual %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float INotEqual %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool INotEqual %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ULessThan %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool ULessThan %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SLessThan %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool SLessThan %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float ULessThanEqual %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool ULessThanEqual %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SLessThanEqual %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool SLessThanEqual %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float UGreaterThan %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool UGreaterThan %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float UGreaterThanEqual %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool UGreaterThanEqual %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SGreaterThan %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool SGreaterThan %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float SGreaterThanEqual %uint_0 %uint_0", + "Expected bool scalar or vector type as Result Type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %bool SGreaterThanEqual %uint_0 %float_0", + "Expected operands to be scalar or vector int"), + + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float GenericCastToPtr %null", + "Expected Result Type to be a pointer"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float PtrCastToGeneric %null", + "Expected Result Type to be a pointer"), + + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %bool Bitcast %uint_0", + "Expected Result Type to be a pointer or int or " + "float vector or scalar type"), + BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint Bitcast %true", + "Expected input to be a pointer or int or float " + "vector or scalar"), + + BAD_KERNEL_OPERANDS_ID( + "%v = OpSpecConstantOp %float VectorShuffle %uint2_0 %uint2_0 1 3", + "The Result Type of OpVectorShuffle must be a vector type"), + BAD_KERNEL_OPERANDS_ID( + "%v = OpSpecConstantOp %uint2 VectorShuffle %uint2_0 %uint_0 1 3", + "The type of Vector 2 must be a vector type"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float CompositeExtract %uint2_0 1", + "Result type (OpTypeFloat) does not match the type that results " + "from indexing into the composite (OpTypeInt)"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint CompositeExtract %uint_0 1", + "Reached non-composite type while indexes still remain to be " + "traversed"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %float CompositeInsert %uint_0 %uint2_0 1", + "The Result Type must be the same as Composite type in " + "OpSpecConstantOp yielding Result Id 5"), + BAD_KERNEL_OPERANDS( + "%v = OpSpecConstantOp %uint2 CompositeInsert %uint_0 %uint_0 1", + "The Result Type must be the same as Composite type in " + "OpSpecConstantOp yielding Result Id 4"), + + // TODO - Still need to add access chains + // + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint AccessChain %null", + // "AccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint AccessChain + // %null %float_0", + // "AccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint InBoundsAccessChain + // %null", + // "InBoundsAccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint + // InBoundsAccessChain %null %float_0", + // "InBoundsAccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint PtrAccessChain %null + // %uint_0", + // "PtrAccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint PtrAccessChain + // %float_0 %float_0", + // "PtrAccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint + // InBoundsPtrAccessChain %null %uint_0", + // "InBoundsPtrAccessChain"), + // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint + // InBoundsPtrAccessChain %float_0 %float_0", + // "InBoundsPtrAccessChain"), + })); + +TEST_F(ValidateConstant, ForwardConstantFunctionPointerINTEL) { + const std::string spirv = R"( +OpCapability Linkage +OpCapability Shader +OpCapability FunctionPointersINTEL +OpExtension "SPV_INTEL_function_pointers" +OpMemoryModel Logical Simple +%void = OpTypeVoid +%functype = OpTypeFunction %void +; UniformConstant avoids logical pointer validation conflicts in Function sc +%ptr_fun = OpTypePointer UniformConstant %functype +%const_ptr = OpConstantFunctionPointerINTEL %ptr_fun %target_func +%target_func = OpFunction %void None %functype +%lbl = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } } // namespace
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp index 3869626..61593b2 100644 --- a/test/val/val_conversion_test.cpp +++ b/test/val/val_conversion_test.cpp
@@ -39,6 +39,7 @@ const std::string capabilities = R"( OpCapability Shader +OpCapability Float16 OpCapability Int64 OpCapability Float64)"; @@ -54,6 +55,7 @@ %func = OpTypeFunction %void %bool = OpTypeBool %f32 = OpTypeFloat 32 +%f16 = OpTypeFloat 16 %u32 = OpTypeInt 32 0 %s32 = OpTypeInt 32 1 %f64 = OpTypeFloat 64 @@ -84,6 +86,8 @@ %f32_3 = OpConstant %f32 3 %f32_4 = OpConstant %f32 4 +%f16_1 = OpConstant %f16 1 + %s32_0 = OpConstant %s32 0 %s32_1 = OpConstant %s32 1 %s32_2 = OpConstant %s32 2 @@ -581,7 +585,7 @@ "Type: FConvert")); } -TEST_F(ValidateConversion, FConvertSameBitWidth) { +TEST_F(ValidateConversion, FConvertSameBitWidthNoEncoding) { const std::string body = R"( %val = OpFConvert %f32 %f32_1 )"; @@ -589,8 +593,46 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected input to have different bit width from " - "Result Type: FConvert")); + HasSubstr("Expected component type of Value to be different from " + "component type of Result Type: FConvert")); +} + +TEST_F(ValidateConversion, ValidFConvertSameBitWidthDifferentEncoding) { + const std::string extensions = R"( +OpCapability Float8EXT +OpExtension "SPV_EXT_float8" +)"; + const std::string types = R"( +%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT +%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT +%fp8e4m3_1 = OpConstant %fp8e4m3 1 +%fp8e5m2_1 = OpConstant %fp8e5m2 1 +)"; + const std::string body = R"( +%val1 = OpFConvert %fp8e4m3 %fp8e5m2_1 +%val2 = OpFConvert %fp8e5m2 %fp8e4m3_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, FConvertFloat16ToBFloat16) { + const std::string extensions = R"( +OpCapability BFloat16TypeKHR +OpExtension "SPV_KHR_bfloat16" +)"; + + const std::string types = R"( +%bf16 = OpTypeFloat 16 BFloat16KHR +)"; + + const std::string body = R"( +%val = OpFConvert %bf16 %f16_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateConversion, QuantizeToF16Success) { @@ -1196,9 +1238,10 @@ OpCapability Float16 OpCapability Int16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -1263,8 +1306,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateConversion, CoopMatKHRConversionUseMismatchFail) { @@ -1273,9 +1316,10 @@ OpCapability Float16 OpCapability Int16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -1308,8 +1352,9 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected Use of Matrix type and Result Type to be identical")); @@ -1321,10 +1366,12 @@ OpCapability Float16 OpCapability Int16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 32 1 1 %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool @@ -1338,11 +1385,11 @@ %u32_8 = OpConstant %u32 8 %u32_4 = OpConstant %u32 4 %subgroup = OpConstant %u32 3 -%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 %use_A = OpConstant %u32 0 %f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A -%f32mat = OpTypeCooperativeMatrixKHR %f32 %device %u32_8 %u32_8 %use_A +%f32mat = OpTypeCooperativeMatrixKHR %f32 %workgroup %u32_8 %u32_8 %use_A %f16_1 = OpConstant %f16 1 @@ -1356,8 +1403,9 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Expected scopes of Matrix and Result Type to be identical")); @@ -1468,9 +1516,12 @@ CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected input to be a pointer or int scalar if " - "Result Type is pointer: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In SPIR-V 1.4 or earlier (and without " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer or int scalar if " + "Result Type is pointer: Bitcast")); } TEST_F(ValidateConversion, BitcastPtrWrongInputTypeSPV1p5) { @@ -1481,9 +1532,12 @@ CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected input to be a pointer, int scalar or 32-bit " - "int vector if Result Type is pointer: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In SPIR-V 1.5 or later (or with " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); } TEST_F(ValidateConversion, BitcastPtrWrongInputTypePhysicalStorageBufferKHR) { @@ -1496,9 +1550,12 @@ "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected input to be a pointer, int scalar or 32-bit " - "int vector if Result Type is pointer: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In SPIR-V 1.5 or later (or with " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); } TEST_F(ValidateConversion, BitcastPtrWrongInputTypeIntVectorSPV1p5) { @@ -1509,9 +1566,12 @@ CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected input to be a pointer, int scalar or 32-bit " - "int vector if Result Type is pointer: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In SPIR-V 1.5 or later (or with " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); } TEST_F(ValidateConversion, @@ -1525,9 +1585,12 @@ "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected input to be a pointer, int scalar or 32-bit " - "int vector if Result Type is pointer: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In SPIR-V 1.5 or later (or with " + "SPV_KHR_physical_storage_buffer), expected input to be a " + "pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); } TEST_F(ValidateConversion, BitcastPtrWrongResultType) { @@ -1538,7 +1601,9 @@ CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer can only be converted to another pointer or " + HasSubstr("In SPIR-V 1.4 or earlier (and without " + "SPV_KHR_physical_storage_buffer), pointer can only be " + "converted to another pointer or " "int scalar: Bitcast")); } @@ -1550,9 +1615,13 @@ CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer can only be converted to another pointer, int " - "scalar or 32-bit int vector: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), " + "pointer can only be converted " + "to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); } TEST_F(ValidateConversion, BitcastPtrWrongResultTypePhysicalStorageBufferKHR) { @@ -1565,9 +1634,13 @@ "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer can only be converted to another pointer, int " - "scalar or 32-bit int vector: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), " + "pointer can only be converted " + "to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); } TEST_F(ValidateConversion, BitcastPtrWrongResultTypeIntVectorSPV1p5) { @@ -1578,9 +1651,13 @@ CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer can only be converted to another pointer, int " - "scalar or 32-bit int vector: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), " + "pointer can only be converted " + "to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); } TEST_F(ValidateConversion, @@ -1594,9 +1671,13 @@ "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer can only be converted to another pointer, int " - "scalar or 32-bit int vector: Bitcast")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), " + "pointer can only be converted " + "to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); } TEST_F(ValidateConversion, BitcastDifferentTotalBitWidth) { @@ -1943,25 +2024,30 @@ TEST_F(ValidateConversion, BitcastUntypedPointerInput) { const std::string spirv = R"( OpCapability Shader +OpCapability Int64 OpCapability VariablePointers OpCapability UntypedPointersKHR OpCapability WorkgroupMemoryExplicitLayoutKHR +OpCapability PhysicalStorageBufferAddresses OpExtension "SPV_KHR_workgroup_memory_explicit_layout" OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_untyped_pointers" -OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %main "main" %var +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" OpDecorate %struct Block OpMemberDecorate %struct 0 Offset 0 %void = OpTypeVoid %int = OpTypeInt 32 0 +%long = OpTypeInt 64 0 +%long_0 = OpConstant %long 0 %struct = OpTypeStruct %int -%ptr = OpTypeUntypedPointerKHR Workgroup -%var = OpUntypedVariableKHR %ptr Workgroup %struct +%ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel -%cast = OpBitcast %int %var +%ptr_cast = OpBitcast %ptr %long_0 +%cast = OpBitcast %long %ptr_cast OpReturn OpFunctionEnd )"; @@ -1973,23 +2059,27 @@ TEST_F(ValidateConversion, BitcastUntypedPointerOutput) { const std::string spirv = R"( OpCapability Shader +OpCapability Int64 OpCapability VariablePointers OpCapability UntypedPointersKHR OpCapability WorkgroupMemoryExplicitLayoutKHR +OpCapability PhysicalStorageBufferAddresses OpExtension "SPV_KHR_workgroup_memory_explicit_layout" OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_untyped_pointers" -OpMemoryModel Logical GLSL450 +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 -%ptr = OpTypeUntypedPointerKHR Workgroup -%var = OpUntypedVariableKHR %ptr Workgroup %int +%long = OpTypeInt 64 0 +%long_0 = OpConstant %long 0 +%ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel -%cast = OpBitcast %ptr %int_0 +%cast = OpBitcast %ptr %long_0 OpReturn OpFunctionEnd )"; @@ -2147,10 +2237,11 @@ OpCapability Int16 OpCapability CooperativeMatrixConversionsNV OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_NV_cooperative_matrix2" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -2276,8 +2367,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateConversion, CoopMat2TransposeShapeFail) { @@ -2287,10 +2378,11 @@ OpCapability Int16 OpCapability CooperativeMatrixConversionsNV OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_NV_cooperative_matrix2" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -2319,13 +2411,471 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected rows of Matrix type and Result Type to be " "swapped with columns")); } +TEST_F(ValidateConversion, CoopVecConversionSuccess) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeVectorNV +OpCapability ReplicatedCompositesEXT +OpExtension "SPV_NV_cooperative_vector" +OpExtension "SPV_EXT_replicated_composites" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 +%u32 = OpTypeInt 32 0 +%s16 = OpTypeInt 16 1 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%use_A = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8 +%f32vec = OpTypeCooperativeVectorNV %f32 %u32_8 +%u16vec = OpTypeCooperativeVectorNV %u16 %u32_8 +%u32vec = OpTypeCooperativeVectorNV %u32 %u32_8 +%s16vec = OpTypeCooperativeVectorNV %s16 %u32_8 +%s32vec = OpTypeCooperativeVectorNV %s32 %u32_8 + +%f16_1 = OpConstant %f16 1 +%f32_1 = OpConstant %f32 1 +%u16_1 = OpConstant %u16 1 +%u32_1 = OpConstant %u32 1 +%s16_1 = OpConstant %s16 1 +%s32_1 = OpConstant %s32 1 + +%f16vec_1 = OpConstantCompositeReplicateEXT %f16vec %f16_1 +%f32vec_1 = OpConstantCompositeReplicateEXT %f32vec %f32_1 +%u16vec_1 = OpConstantCompositeReplicateEXT %u16vec %u16_1 +%u32vec_1 = OpConstantCompositeReplicateEXT %u32vec %u32_1 +%s16vec_1 = OpConstantCompositeReplicateEXT %s16vec %s16_1 +%s32vec_1 = OpConstantCompositeReplicateEXT %s32vec %s32_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val11 = OpConvertFToU %u16vec %f16vec_1 +%val12 = OpConvertFToU %u32vec %f16vec_1 +%val13 = OpConvertFToS %s16vec %f16vec_1 +%val14 = OpConvertFToS %s32vec %f16vec_1 +%val15 = OpFConvert %f32vec %f16vec_1 + +%val21 = OpConvertFToU %u16vec %f32vec_1 +%val22 = OpConvertFToU %u32vec %f32vec_1 +%val23 = OpConvertFToS %s16vec %f32vec_1 +%val24 = OpConvertFToS %s32vec %f32vec_1 +%val25 = OpFConvert %f16vec %f32vec_1 + +%val31 = OpConvertUToF %f16vec %u16vec_1 +%val32 = OpConvertUToF %f32vec %u16vec_1 +%val33 = OpUConvert %u32vec %u16vec_1 +%val34 = OpSConvert %s32vec %u16vec_1 + +%val41 = OpConvertSToF %f16vec %s16vec_1 +%val42 = OpConvertSToF %f32vec %s16vec_1 +%val43 = OpUConvert %u32vec %s16vec_1 +%val44 = OpSConvert %s32vec %s16vec_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, CoopVecConversionDimMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeVectorNV +OpCapability ReplicatedCompositesEXT +OpExtension "SPV_NV_cooperative_vector" +OpExtension "SPV_EXT_replicated_composites" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 +%u32 = OpTypeInt 32 0 +%s16 = OpTypeInt 16 1 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%u32_4 = OpConstant %u32 4 +%subgroup = OpConstant %u32 3 +%use_A = OpConstant %u32 0 +%use_B = OpConstant %u32 1 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8 +%f32vec = OpTypeCooperativeVectorNV %f32 %u32_4 + +%f16_1 = OpConstant %f16 1 + +%f16vec_1 = OpConstantCompositeReplicateEXT %f16vec %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val1 = OpFConvert %f32vec %f16vec_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of components to be identical")); +} + +TEST_F(ValidateConversion, ConvertPtrToUVectorWithMaskedGatherScatterSuccess) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2ptr = OpTypeVector %ptr 2 +%v2u64 = OpTypeVector %u64 2 +%fn = OpTypeFunction %void %v2ptr +%main = OpFunction %void None %fn +%p = OpFunctionParameter %v2ptr +%entry = OpLabel +%result = OpConvertPtrToU %v2u64 %p +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertPtrToUVectorWithoutCapabilityFails) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2ptr = OpTypeVector %ptr 2 +%v2u64 = OpTypeVector %u64 2 +%fn = OpTypeFunction %void %v2ptr +%main = OpFunction %void None %fn +%p = OpFunctionParameter %v2ptr +%entry = OpLabel +%result = OpConvertPtrToU %v2u64 %p +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeVector Component Type")); +} + +TEST_F(ValidateConversion, ConvertPtrToUVectorResultScalarInputFails) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2u64 = OpTypeVector %u64 2 +%fn = OpTypeFunction %void %ptr +%main = OpFunction %void None %fn +%p = OpFunctionParameter %ptr +%entry = OpLabel +%result = OpConvertPtrToU %v2u64 %p +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a vector when Result Type is a " + "vector: ConvertPtrToU")); +} + +TEST_F(ValidateConversion, ConvertPtrToUVectorDimensionMismatchFails) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2ptr = OpTypeVector %ptr 2 +%v4u64 = OpTypeVector %u64 4 +%fn = OpTypeFunction %void %v2ptr +%main = OpFunction %void None %fn +%p = OpFunctionParameter %v2ptr +%entry = OpLabel +%result = OpConvertPtrToU %v4u64 %p +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result Type: " + "ConvertPtrToU")); +} + +TEST_F(ValidateConversion, ConvertUToPtrVectorWithMaskedGatherScatterSuccess) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2ptr = OpTypeVector %ptr 2 +%v2u64 = OpTypeVector %u64 2 +%fn = OpTypeFunction %void %v2u64 +%main = OpFunction %void None %fn +%addrs = OpFunctionParameter %v2u64 +%entry = OpLabel +%result = OpConvertUToPtr %v2ptr %addrs +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToPtrVectorWithoutCapabilityFails) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2ptr = OpTypeVector %ptr 2 +%v2u64 = OpTypeVector %u64 2 +%fn = OpTypeFunction %void %v2u64 +%main = OpFunction %void None %fn +%addrs = OpFunctionParameter %v2u64 +%entry = OpLabel +%result = OpConvertUToPtr %v2ptr %addrs +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeVector Component Type")); +} + +TEST_F(ValidateConversion, ConvertUToPtrVectorResultScalarInputFails) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v2ptr = OpTypeVector %ptr 2 +%fn = OpTypeFunction %void %u64 +%main = OpFunction %void None %fn +%addr = OpFunctionParameter %u64 +%entry = OpLabel +%result = OpConvertUToPtr %v2ptr %addr +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a vector when Result Type is a " + "vector: ConvertUToPtr")); +} + +TEST_F(ValidateConversion, ConvertUToPtrVectorDimensionMismatchFails) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v4ptr = OpTypeVector %ptr 4 +%v2u64 = OpTypeVector %u64 2 +%fn = OpTypeFunction %void %v2u64 +%main = OpFunction %void None %fn +%addrs = OpFunctionParameter %v2u64 +%entry = OpLabel +%result = OpConvertUToPtr %v4ptr %addrs +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result Type: " + "ConvertUToPtr")); +} + +TEST_F(ValidateConversion, ConvertPtrToUVec4WithMaskedGatherScatterSuccess) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v4ptr = OpTypeVector %ptr 4 +%v4u64 = OpTypeVector %u64 4 +%fn = OpTypeFunction %void %v4ptr +%main = OpFunction %void None %fn +%p = OpFunctionParameter %v4ptr +%entry = OpLabel +%result = OpConvertPtrToU %v4u64 %p +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToPtrVec4WithMaskedGatherScatterSuccess) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%v4ptr = OpTypeVector %ptr 4 +%v4u64 = OpTypeVector %u64 4 +%fn = OpTypeFunction %void %v4u64 +%main = OpFunction %void None %fn +%addrs = OpFunctionParameter %v4u64 +%entry = OpLabel +%result = OpConvertUToPtr %v4ptr %addrs +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Test that scalar conversion still works with capability present +TEST_F(ValidateConversion, ConvertPtrToUScalarWithMaskedGatherScatterSuccess) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%fn = OpTypeFunction %void %ptr +%main = OpFunction %void None %fn +%p = OpFunctionParameter %ptr +%entry = OpLabel +%result = OpConvertPtrToU %u64 %p +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToPtrScalarWithMaskedGatherScatterSuccess) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Int64 +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%u64 = OpTypeInt 64 0 +%u32 = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %u32 +%fn = OpTypeFunction %void %u64 +%main = OpFunction %void None %fn +%addr = OpFunctionParameter %u64 +%entry = OpLabel +%result = OpConvertUToPtr %ptr %addr +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp index 349e5e9..210e1d1 100644 --- a/test/val/val_data_test.cpp +++ b/test/val/val_data_test.cpp
@@ -14,10 +14,12 @@ // Validation tests for Data Rules. +#include <sstream> #include <string> #include <utility> #include "gmock/gmock.h" +#include "spirv/unified1/spirv.hpp11" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h" @@ -71,6 +73,44 @@ OpCapability Int64 OpMemoryModel Logical GLSL450 )"; +std::string header_with_bfloat16 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability BFloat16TypeKHR + OpCapability BFloat16DotProductKHR + OpCapability BFloat16CooperativeMatrixKHR + OpExtension "SPV_KHR_bfloat16" + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_float8 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float8EXT + OpCapability Float8CooperativeMatrixEXT + OpExtension "SPV_EXT_float8" + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_float8_and_bfloat16 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float8EXT + OpCapability Float8CooperativeMatrixEXT + OpCapability BFloat16TypeKHR + OpExtension "SPV_EXT_float8" + OpExtension "SPV_KHR_bfloat16" + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_float8_no_coop_matrix = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float8EXT + OpCapability CooperativeMatrixKHR + OpCapability VulkanMemoryModel + OpExtension "SPV_EXT_float8" + OpExtension "SPV_KHR_cooperative_matrix" + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR +)"; std::string header_with_float16 = R"( OpCapability Shader OpCapability Linkage @@ -90,9 +130,22 @@ OpMemoryModel Logical GLSL450 )"; +std::string header_with_float64_bfloat16 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float64 + OpCapability BFloat16TypeKHR + OpExtension "SPV_KHR_bfloat16" + OpMemoryModel Logical GLSL450 +)"; + std::string invalid_comp_error = "Illegal number of components"; -std::string missing_cap_error = "requires the Vector16 capability"; +std::string missing_cap_error = + "requires the Vector16 or LongVectorEXT capability"; std::string missing_int8_cap_error = "requires the Int8 capability"; +std::string missing_int4_cap_error = + "Using a 4-bit integer type requires the Int4TypeINTEL " + "or ArbitraryPrecisionIntegersINTEL capability."; std::string missing_int16_cap_error = "requires the Int16 capability," " or an extension that explicitly enables 16-bit integers."; @@ -248,6 +301,33 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); } +TEST_F(ValidateData, int4_bad) { + std::string str = header + "%2 = OpTypeInt 4 0"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int4_cap_error)); +} + +TEST_F(ValidateData, int4_with_arbitrary_precision_good) { + std::string str = + HeaderWith( + "ArbitraryPrecisionIntegersINTEL " + "OpExtension \"SPV_INTEL_arbitrary_precision_integers\"") + + " %2 = OpTypeInt 4 0"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateData, int4_signed_with_arbitrary_precision_good) { + std::string str = + HeaderWith( + "ArbitraryPrecisionIntegersINTEL " + "OpExtension \"SPV_INTEL_arbitrary_precision_integers\"") + + " %2 = OpTypeInt 4 1"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + TEST_F(ValidateData, int16_good) { std::string str = header_with_int16 + "%2 = OpTypeInt 16 1"; CompileSuccessfully(str.c_str()); @@ -334,12 +414,96 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateData, float8_good) { + std::string str = header_with_float8 + + R"(%2 = OpTypeFloat 8 Float8E4M3EXT +%3 = OpTypeFloat 8 Float8E5M2EXT +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateData, bfloat16_good) { + std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 16 BFloat16KHR"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, cooperative_matrix_bfloat16_good) { + std::string str = header_with_bfloat16 + R"( +%u32 = OpTypeInt 32 0 +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 +%bf16 = OpTypeFloat 16 BFloat16KHR +%bf16matA = OpTypeCooperativeMatrixKHR %bf16 %subgroup %u32_16 %u32_16 %useA +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, cooperative_matrix_float8_good) { + std::string str = header_with_float8 + R"( +%u32 = OpTypeInt 32 0 +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 +%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT +%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT +%fp8e4m3_matA = OpTypeCooperativeMatrixKHR %fp8e4m3 %subgroup %u32_16 %u32_16 %useA +%fp8e5m2_matA = OpTypeCooperativeMatrixKHR %fp8e5m2 %subgroup %u32_16 %u32_16 %useA +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, cooperative_matrix_float8_no_capability_bad) { + std::string str = header_with_float8_no_coop_matrix + R"( +%u32 = OpTypeInt 32 0 +%u32_16 = OpConstant %u32 16 +%useA = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 +%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT +%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT +%fp8e4m3_matA = OpTypeCooperativeMatrixKHR %fp8e4m3 %subgroup %u32_16 %u32_16 %useA +%fp8e5m2_matA = OpTypeCooperativeMatrixKHR %fp8e5m2 %subgroup %u32_16 %u32_16 %useA +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("require Float8CooperativeMatrixEXT be declared")); +} + TEST_F(ValidateData, float16_buffer_good) { std::string str = header_with_float16_buffer + "%2 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateData, float32_with_encoding_number_bad) { + std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 32 !9999"; + const auto& err = CompileFailure(str.c_str()); + EXPECT_THAT(err, HasSubstr("Invalid OpTypeFloat encoding")); +} + +TEST_F(ValidateData, float32_with_encoding_enum_bad) { + std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 32 BFloat16"; + const auto& err = CompileFailure(str.c_str()); + EXPECT_THAT(err, HasSubstr("Invalid FP encoding 'BFloat16'")); +} + +TEST_F(ValidateData, float64_with_encoding_number_bad) { + std::string str = header_with_float64 + "%2 = OpTypeFloat 64 !9999"; + const auto& err = CompileFailure(str.c_str()); + EXPECT_THAT(err, HasSubstr("Invalid OpTypeFloat encoding")); +} + +TEST_F(ValidateData, float64_with_encoding_enum_bad) { + std::string str = header_with_float64 + "%2 = OpTypeFloat 64 BFloat16"; + const auto& err = CompileFailure(str.c_str()); + EXPECT_THAT(err, HasSubstr("Invalid FP encoding 'BFloat16'")); +} + TEST_F(ValidateData, float16_bad) { std::string str = header + "%2 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); @@ -347,19 +511,251 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float16_cap_error)); } -TEST_F(ValidateData, float64_good) { - std::string str = header_with_float64 + "%2 = OpTypeFloat 64"; +TEST_F(ValidateData, bfloat16_missing_cap_bad) { + std::string str = header + "%2 = OpTypeFloat 16 BFloat16KHR"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: BFloat16TypeKHR")); +} + +TEST_F(ValidateData, bfloat16_wrong_width_15_bad) { + std::stringstream ss; + ss << header_with_bfloat16 << "!" + << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 15 " + << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid number of bits (15) used for OpTypeFloat")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, bfloat16_wrong_width_8_bad) { + std::stringstream ss; + ss << header_with_float8_and_bfloat16 << "!" + << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 8 " + << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Unsupported 8-bit floating point encoding")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, bfloat16_too_many_operands_bad) { + std::stringstream ss; + ss << header_with_float8_and_bfloat16 << "!" + << (5u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 16 " + << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR) << " 0"; + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected no more operands after 4 words, but stated " + "word count is 5")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float8_E4M3_missing_cap_bad) { + std::string str = header + + R"(%2 = OpTypeFloat 8 Float8E4M3EXT +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: Float8EXT")); +} + +TEST_F(ValidateData, float8_E5M2_missing_cap_bad) { + std::string str = header + + R"(%2 = OpTypeFloat 8 Float8E5M2EXT +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: Float8EXT")); +} + +TEST_F(ValidateData, float8_no_encoding_bad) { + std::string str = header_with_float8 + "%2 = OpTypeFloat 8"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("8-bit floating point type requires an encoding")); +} + +TEST_F(ValidateData, float8_bad_encoding) { + std::string str = + header_with_float8_and_bfloat16 + "%2 = OpTypeFloat 8 BFloat16KHR"; + const auto& err = CompileFailure(str.c_str()); + EXPECT_THAT(err, HasSubstr("Invalid bit width 8 for floating point encoding " + "BFloat16KHR; expected 16")); +} + +TEST_F(ValidateData, float8_E4M3_wrong_width_7_bad) { + std::stringstream ss; + ss << header_with_float8 << "!" + << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 7 " + << static_cast<uint32_t>(spv::FPEncoding::Float8E4M3EXT); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid number of bits (7) used for OpTypeFloat")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float8_E4M3_wrong_width_16_bad) { + std::stringstream ss; + ss << header_with_float8 << "!" + << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 16 " + << static_cast<uint32_t>(spv::FPEncoding::Float8E4M3EXT); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Unsupported 16-bit floating point encoding (4214)")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float8_E5M2_wrong_width_7_bad) { + std::stringstream ss; + ss << header_with_float8 << "!" + << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 7 " + << static_cast<uint32_t>(spv::FPEncoding::Float8E5M2EXT); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid number of bits (7) used for OpTypeFloat")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float8_E5M2_wrong_width_16_bad) { + std::stringstream ss; + ss << header_with_float8 << "!" + << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 16 " + << static_cast<uint32_t>(spv::FPEncoding::Float8E5M2EXT); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Unsupported 16-bit floating point encoding (4215)")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float8_e4m3_too_many_operands_bad) { + std::stringstream ss; + ss << header_with_float8 << "!" + << (5u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 8 " + << static_cast<uint32_t>(spv::FPEncoding::Float8E4M3EXT) << " 0"; + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected no more operands after 4 words, but stated " + "word count is 5")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float8_e5m2_too_many_operands_bad) { + std::stringstream ss; + ss << header_with_float8 << "!" + << (5u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 8 " + << static_cast<uint32_t>(spv::FPEncoding::Float8E5M2EXT) << " 0"; + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected no more operands after 4 words, but stated " + "word count is 5")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, dot_bfloat16_bad) { + std::string str = R"( + OpCapability Shader + OpCapability BFloat16TypeKHR + OpExtension "SPV_KHR_bfloat16" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bfloat16 = OpTypeFloat 16 BFloat16KHR +%_ptr_Function_bfloat16 = OpTypePointer Function %bfloat16 + %v2bfloat16 = OpTypeVector %bfloat16 2 +%_ptr_Function_v2bfloat16 = OpTypePointer Function %v2bfloat16 + %main = OpFunction %void None %3 + %5 = OpLabel + %v1 = OpVariable %_ptr_Function_v2bfloat16 Function + %v2 = OpVariable %_ptr_Function_v2bfloat16 Function + %12 = OpLoad %v2bfloat16 %v1 + %14 = OpLoad %v2bfloat16 %v2 + %15 = OpDot %bfloat16 %12 %14 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires BFloat16DotProductKHR be declared.")); +} + +TEST_F(ValidateData, bfloat16_without_float16_capability_good) { + std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 16 BFloat16KHR"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateData, float64_bad) { +TEST_F(ValidateData, float64_good) { + std::string str = header_with_float64 + "%2 = OpTypeFloat 64"; + + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, float64_missing_cap_bad) { std::string str = header + "%2 = OpTypeFloat 64"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float64_cap_error)); } +TEST_F(ValidateData, float32_encoding_param_bad) { + std::stringstream ss; + ss << header_with_bfloat16 << "!" + << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 32 " + << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("32-bit floating point type must not have encoding parameter")) + << getDiagnosticString(); +} + +TEST_F(ValidateData, float64_encoding_param_bad) { + std::stringstream ss; + ss << header_with_float64_bfloat16 << "!" + << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 64 " + << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR); + CompileSuccessfully(ss.str().c_str()); + OverwriteIdBound(100); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("64-bit floating point type must not have encoding parameter")) + << getDiagnosticString(); +} + // Number of bits in a float may be only one of: {16,32,64} TEST_F(ValidateData, float_invalid_num_bits) { std::string str = header + "%2 = OpTypeFloat 48"; @@ -627,7 +1023,8 @@ OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_16bit_storage" OpMemoryModel Logical GLSL450 - OpDecorate %_ FPRoundingMode )" + mode + R"( + OpDecorate %_ FPRoundingMode )" + + mode + R"( %half = OpTypeFloat 16 %float = OpTypeFloat 32 %float_1_25 = OpConstant %float 1.25 @@ -723,6 +1120,8 @@ OpMemberDecorate %struct_t 0 Offset 0 OpMemberDecorate %struct_t 1 Offset 4 OpDecorate %struct_t Block + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 %uint_t = OpTypeInt 32 0 %array_t = OpTypeRuntimeArray %uint_t %struct_t = OpTypeStruct %uint_t %array_t
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index b5738d7..a87d0af 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,6 +21,7 @@ #include "gmock/gmock.h" #include "source/val/decoration.h" +#include "spirv-tools/libspirv.h" #include "test/unit_spirv.h" #include "test/val/val_code_generator.h" #include "test/val/val_fixtures.h" @@ -1517,9 +1520,9 @@ CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("must be explicitly laid out with ArrayStride decorations")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride or " + "ArrayStrideIdEXT decorations")); } TEST_F(ValidateDecorations, BufferBlockMissingArrayStrideBad) { @@ -1549,9 +1552,9 @@ CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("must be explicitly laid out with ArrayStride decorations")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride or " + "ArrayStrideIdEXT decorations")); } TEST_F(ValidateDecorations, BlockNestedStructMissingArrayStrideBad) { @@ -1586,9 +1589,9 @@ CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("must be explicitly laid out with ArrayStride decorations")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride or " + "ArrayStrideIdEXT decorations")); } TEST_F(ValidateDecorations, BufferBlockNestedStructMissingArrayStrideBad) { @@ -1623,9 +1626,9 @@ CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("must be explicitly laid out with ArrayStride decorations")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride or " + "ArrayStrideIdEXT decorations")); } TEST_F(ValidateDecorations, BlockMissingMatrixStrideBad) { @@ -1861,6 +1864,8 @@ OpMemberDecorate %Output 6 MatrixStride 16 OpMemberDecorate %Output 7 Offset 128 OpDecorate %Output Block + OpDecorate %dataOutput DescriptorSet 0 + OpDecorate %dataOutput Binding 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2074,6 +2079,68 @@ "another Block or BufferBlock.")); } +TEST_F(ValidateDecorations, BlockCannotAppearWithinBlockArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpMemberDecorate %outer 0 Offset 0 +OpMemberDecorate %outer 1 Offset 4 +OpMemberDecorate %outer 2 Offset 20 +OpDecorate %outer Block +OpMemberDecorate %inner 0 Offset 0 +OpDecorate %inner Block +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%inner = OpTypeStruct %int +%array = OpTypeArray %inner %int_4 +%outer = OpTypeStruct %int %array %int +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("rules: A Block or BufferBlock cannot be nested within " + "another Block or BufferBlock.")); +} + +TEST_F(ValidateDecorations, BlockCannotAppearWithinBlockMultiArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpMemberDecorate %outer 0 Offset 0 +OpMemberDecorate %outer 1 Offset 4 +OpDecorate %outer Block +OpMemberDecorate %inner 0 Offset 0 +OpDecorate %inner Block +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%inner = OpTypeStruct %int +%array1 = OpTypeArray %inner %int_4 +%array2 = OpTypeArray %array1 %int_4 +%outer = OpTypeStruct %int %array2 +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("rules: A Block or BufferBlock cannot be nested within " + "another Block or BufferBlock.")); +} + TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) { // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 std::string spirv = R"( @@ -2485,6 +2552,10 @@ OpMemberDecorate %SSBO16 0 Offset 0 OpDecorate %SSBO32 BufferBlock OpDecorate %SSBO16 BufferBlock + OpDecorate %varSSBO32 DescriptorSet 0 + OpDecorate %varSSBO32 Binding 0 + OpDecorate %varSSBO16 DescriptorSet 0 + OpDecorate %varSSBO16 Binding 1 %void = OpTypeVoid %voidf = OpTypeFunction %void %u32 = OpTypeInt 32 0 @@ -3412,6 +3483,7 @@ OpExecutionMode %1 OriginUpperLeft OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 OpDecorate %var Binding 0 %void = OpTypeVoid @@ -3454,6 +3526,7 @@ OpExecutionMode %1 OriginUpperLeft OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 OpDecorate %var DescriptorSet 0 %void = OpTypeVoid @@ -3496,6 +3569,7 @@ OpExecutionMode %1 OriginUpperLeft OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 OpDecorate %var Binding 0 %void = OpTypeVoid @@ -3534,35 +3608,6 @@ "decorations specified")); } -TEST_F(ValidateDecorations, - VulkanStorageBufferMissingDescriptorAndBindingUnusedGood) { - std::string spirv = R"( - OpCapability Shader - OpExtension "SPV_KHR_storage_buffer_storage_class" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %1 "main" - OpExecutionMode %1 OriginUpperLeft - OpDecorate %struct Block - OpMemberDecorate %struct 0 Offset 0 - - %void = OpTypeVoid - %voidfn = OpTypeFunction %void - %float = OpTypeFloat 32 - %struct = OpTypeStruct %float - %ptr = OpTypePointer StorageBuffer %struct - %var = OpVariable %ptr StorageBuffer - - %1 = OpFunction %void None %voidfn - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); - EXPECT_EQ(SPV_SUCCESS, - ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); -} - TEST_F(ValidateDecorations, UniformMissingDescriptorSetGood) { std::string spirv = R"( OpCapability Shader @@ -3839,6 +3884,8 @@ OpMemberDecorate %Output 6 MatrixStride 16 OpMemberDecorate %Output 7 Offset 96 OpDecorate %Output BufferBlock + OpDecorate %dataOutput DescriptorSet 0 + OpDecorate %dataOutput Binding 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -3972,6 +4019,8 @@ OpMemberDecorate %Output 6 MatrixStride 16 OpMemberDecorate %Output 7 Offset 128 OpDecorate %Output Block + OpDecorate %dataOutput DescriptorSet 0 + OpDecorate %dataOutput Binding 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -4040,6 +4089,8 @@ OpMemberDecorate %Output 6 MatrixStride 16 OpMemberDecorate %Output 7 Offset 128 OpDecorate %Output Block + OpDecorate %dataOutput DescriptorSet 0 + OpDecorate %dataOutput Binding 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -5253,6 +5304,71 @@ HasSubstr("In Vulkan, the FPRoundingMode mode must only by RTE or RTZ.")); } +TEST_F(ValidateDecorations, KernelFPRoundingModeGood) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability Float64 + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %kernel "test" + OpDecorate %out_float0 FPRoundingMode RTE + OpDecorate %out_float1 FPRoundingMode RTZ + OpDecorate %out_sint FPRoundingMode RTP + OpDecorate %out_uint FPRoundingMode RTN + OpDecorate %out_double FPRoundingMode RTE + OpDecorate %out_float2 FPRoundingMode RTZ + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %float = OpTypeFloat 32 + %double = OpTypeFloat 64 + %functype = OpTypeFunction %void %uint %float %double + %kernel = OpFunction %void None %functype + %in_int = OpFunctionParameter %uint + %in_float = OpFunctionParameter %float + %in_double = OpFunctionParameter %double + %entry = OpLabel + %out_float0 = OpConvertSToF %float %in_int + %out_float1 = OpConvertUToF %float %in_int + %out_sint = OpConvertFToS %uint %in_float + %out_uint = OpConvertFToU %uint %in_float + %out_double = OpFConvert %double %in_float + %out_float2 = OpFConvert %float %in_double + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0)); +} + +TEST_F(ValidateDecorations, KernelFPRoundingModeBadMode) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %kernel "test" + OpDecorate %out_float FPRoundingMode RTE + %void = OpTypeVoid + %float = OpTypeFloat 32 + %functype = OpTypeFunction %void %float + %kernel = OpFunction %void None %functype + %in_float = OpFunctionParameter %float + %entry = OpLabel + %out_float = OpFAdd %float %in_float %in_float + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("FPRoundingMode decoration can be applied only to a conversion " + "instruction to or from a floating-point type.")); +} + TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup) { std::string spirv = R"( OpCapability Shader @@ -5381,6 +5497,97 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +// OpDecorateId + +TEST_F(ValidateDecorations, DecorateIdGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %subgroupscope "subgroupscope" +OpName %int0 "int0" +OpName %fn "fn" +OpDecorateId %int0 UniformId %subgroupscope +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%subgroupscope = OpConstant %int 3 +%int0 = OpConstantNull %int +%fn = OpTypeFunction %void +%main = OpFunction %void None %fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, DecorateIdGroupBad) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %subgroupscope "subgroupscope" +OpName %int0 "int0" +OpName %fn "fn" +OpName %group "group" +OpDecorateId %group UniformId %subgroupscope +%group = OpDecorationGroup +OpGroupDecorate %group %int0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%subgroupscope = OpConstant %int 3 +%int0 = OpConstantNull %int +%fn = OpTypeFunction %void +%main = OpFunction %void None %fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not be an OpDecorationGroup instruction.\n" + " OpDecorateId %group UniformId %subgroupscope")); +} + +TEST_F(ValidateDecorations, DecorateIdOutOfOrderBad) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %subgroupscope "subgroupscope" +OpName %int0 "int0" +OpName %fn "fn" +OpDecorateId %int0 UniformId %subgroupscope +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%int0 = OpConstantNull %int +%subgroupscope = OpConstant %int 3 +%fn = OpTypeFunction %void +%main = OpFunction %void None %fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("[%subgroupscope]' must appear earlier in the" + " binary than the target\n" + " OpDecorateId %int0 UniformId %subgroupscope")); +} + // Uniform and UniformId decorations TEST_F(ValidateDecorations, UniformDecorationGood) { @@ -5422,6 +5629,7 @@ OpName %call "call" OpName %myfunc "myfunc" OpName %int0 "int0" +OpName %int1 "int1" OpName %float0 "float0" OpName %fn "fn" )") + inst + @@ -5429,10 +5637,11 @@ %void = OpTypeVoid %float = OpTypeFloat 32 %int = OpTypeInt 32 1 -%int0 = OpConstantNull %int +%int1 = OpConstant %int 1 %int_99 = OpConstant %int 99 %subgroupscope = OpConstant %int 3 %float0 = OpConstantNull %float +%int0 = OpConstantNull %int %fn = OpTypeFunction %void %myfunc = OpFunction %void None %fn %myfuncentry = OpLabel @@ -5549,7 +5758,7 @@ TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) { const std::string spirv = - ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int0"); + ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int1"); CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1_SPIRV_1_4); EXPECT_EQ(SPV_ERROR_INVALID_DATA, @@ -6179,34 +6388,6 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) { - const std::string body = R"( -OpCapability PhysicalStorageBufferAddresses -OpCapability Int64 -OpCapability Shader -OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64 GLSL450 -OpEntryPoint Fragment %main "main" -OpExecutionMode %main OriginUpperLeft -%uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBuffer %uint64 -%pptr_f = OpTypePointer Function %ptr -%void = OpTypeVoid -%voidfn = OpTypeFunction %void -%main = OpFunction %void None %voidfn -%entry = OpLabel -%val1 = OpVariable %pptr_f Function -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected AliasedPointer or RestrictPointer for " - "PhysicalStorageBuffer pointer")); -} - TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) { const std::string body = R"( OpCapability PhysicalStorageBufferAddresses @@ -6233,8 +6414,8 @@ CompileSuccessfully(body.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("can't specify both AliasedPointer and RestrictPointer " - "for PhysicalStorageBuffer pointer")); + HasSubstr("decorated with both AliasedPointer and " + "RestrictPointer is not allowed")); } TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) { @@ -6267,38 +6448,6 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) { - const std::string body = R"( -OpCapability PhysicalStorageBufferAddresses -OpCapability Int64 -OpCapability Shader -OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64 GLSL450 -OpEntryPoint Fragment %main "main" -OpExecutionMode %main OriginUpperLeft -%uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBuffer %uint64 -%void = OpTypeVoid -%voidfn = OpTypeFunction %void -%fnptr = OpTypeFunction %void %ptr -%main = OpFunction %void None %voidfn -%entry = OpLabel -OpReturn -OpFunctionEnd -%fn = OpFunction %void None %fnptr -%fparam = OpFunctionParameter %ptr -%lab = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected Aliased or Restrict for " - "PhysicalStorageBuffer pointer")); -} - TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) { const std::string body = R"( OpCapability PhysicalStorageBufferAddresses @@ -6328,9 +6477,9 @@ CompileSuccessfully(body.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("can't specify both Aliased and Restrict for " - "PhysicalStorageBuffer pointer")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decorated with both Aliased and Restrict is not allowed")); } TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) { @@ -6591,12 +6740,14 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } -// NonWritable +// NonReadable/NonWritable // Returns a SPIR-V shader module with variables in various storage classes, -// parameterizable by which ID should be decorated as NonWritable. -std::string ShaderWithNonWritableTarget(const std::string& target, - bool member_decorate = false) { +// parameterizable by which ID should be decorated as NonReadable or +// NonWritable. +std::string ShaderWithNonReadableNonWritableTarget( + const std::string& target, bool member_decorate = false, + const std::string& decoration = "NonWritable") { const std::string decoration_inst = std::string(member_decorate ? "OpMemberDecorate " : "OpDecorate ") + target + (member_decorate ? " 0" : ""); @@ -6604,8 +6755,10 @@ return std::string(R"( OpCapability Shader OpCapability RuntimeDescriptorArrayEXT + OpCapability TensorsARM OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_ARM_tensors" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpName %label "label" @@ -6625,9 +6778,9 @@ OpMemberDecorate %struct_b_rtarr 0 Offset 0 OpDecorate %rtarr ArrayStride 4 )") + decoration_inst + + " " + decoration + - R"( NonWritable - + R"( %void = OpTypeVoid %void_fn = OpTypeFunction %void %float = OpTypeFloat 32 @@ -6642,6 +6795,8 @@ %imstor = OpTypeImage %float 2D 0 0 0 2 R32f ; sampled image %imsam = OpTypeImage %float 2D 0 0 0 1 R32f + ; tensor + %tensor = OpTypeTensorARM %float %int_2 %array_imstor = OpTypeArray %imstor %int_2 %rta_imstor = OpTypeRuntimeArray %imstor @@ -6655,6 +6810,7 @@ %_ptr_imsam = OpTypePointer UniformConstant %imsam %_ptr_array_imstor = OpTypePointer UniformConstant %array_imstor %_ptr_rta_imstor = OpTypePointer UniformConstant %rta_imstor +%_ptr_tensor_UniformConstant = OpTypePointer UniformConstant %tensor %extra_fn = OpTypeFunction %void %float %_ptr_Private %_ptr_imstor @@ -6667,6 +6823,7 @@ %var_imsam = OpVariable %_ptr_imsam UniformConstant %var_array_imstor = OpVariable %_ptr_array_imstor UniformConstant %var_rta_imstor = OpVariable %_ptr_rta_imstor UniformConstant +%var_tensor = OpVariable %_ptr_tensor_UniformConstant UniformConstant %helper = OpFunction %void None %extra_fn %param_f = OpFunctionParameter %float @@ -6686,7 +6843,7 @@ } TEST_F(ValidateDecorations, NonWritableLabelTargetBad) { - std::string spirv = ShaderWithNonWritableTarget("%label"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%label"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); @@ -6695,7 +6852,7 @@ } TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { - std::string spirv = ShaderWithNonWritableTarget("%void"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%void"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); @@ -6704,7 +6861,7 @@ } TEST_F(ValidateDecorations, NonWritableValueTargetBad) { - std::string spirv = ShaderWithNonWritableTarget("%float_0"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%float_0"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); @@ -6713,7 +6870,7 @@ } TEST_F(ValidateDecorations, NonWritableValueParamBad) { - std::string spirv = ShaderWithNonWritableTarget("%param_f"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%param_f"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); @@ -6721,7 +6878,7 @@ } TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) { - std::string spirv = ShaderWithNonWritableTarget("%param_p"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%param_p"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); @@ -6729,12 +6886,13 @@ getDiagnosticString(), HasSubstr( "Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " "buffer\n %param_p = OpFunctionParameter %_ptr_Private_float")); } TEST_F(ValidateDecorations, NonWritablePointerParamStorageImageGood) { - std::string spirv = ShaderWithNonWritableTarget("%param_pimstor"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%param_pimstor"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -6742,7 +6900,7 @@ } TEST_F(ValidateDecorations, NonWritableVarStorageImageGood) { - std::string spirv = ShaderWithNonWritableTarget("%var_imstor"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_imstor"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -6750,18 +6908,20 @@ } TEST_F(ValidateDecorations, NonWritableVarSampledImageBad) { - std::string spirv = ShaderWithNonWritableTarget("%var_imsam"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_imsam"); 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 %var_imsam")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_imsam")); } TEST_F(ValidateDecorations, NonWritableVarUboGood) { - std::string spirv = ShaderWithNonWritableTarget("%var_ubo"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_ubo"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -6794,7 +6954,7 @@ } TEST_F(ValidateDecorations, NonWritableVarSsboInStorageBufferGood) { - std::string spirv = ShaderWithNonWritableTarget("%var_ssbo_sb"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_ssbo_sb"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -6802,7 +6962,8 @@ } TEST_F(ValidateDecorations, NonWritableMemberOfSsboInStorageBufferGood) { - std::string spirv = ShaderWithNonWritableTarget("%struct_b_rtarr", true); + std::string spirv = + ShaderWithNonReadableNonWritableTarget("%struct_b_rtarr", true); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -6810,59 +6971,99 @@ } TEST_F(ValidateDecorations, NonWritableMemberOfStructGood) { - std::string spirv = ShaderWithNonWritableTarget("%simple_struct", true); + std::string spirv = + ShaderWithNonReadableNonWritableTarget("%simple_struct", true); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateDecorations, NonWritableTensorVarUniformConstantGood) { + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_tensor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonReadableTensorVarUniformConstantGood) { + std::string spirv = ShaderWithNonReadableNonWritableTarget( + "%var_tensor", false, "NonReadable"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + TEST_F(ValidateDecorations, NonWritableVarWorkgroupBad) { - std::string spirv = ShaderWithNonWritableTarget("%var_wg"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_wg"); 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 %var_wg")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_wg")); +} + +TEST_F(ValidateDecorations, NonReadableVarWorkgroupBad) { + std::string spirv = + ShaderWithNonReadableNonWritableTarget("%var_wg", false, "NonReadable"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonReadable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_wg")); } TEST_F(ValidateDecorations, NonWritableVarWorkgroupV14Bad) { - std::string spirv = ShaderWithNonWritableTarget("%var_wg"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_wg"); CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, storage " - "buffer, or variable in Private or Function storage " - "class\n %var_wg")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, storage " + "buffer, or variable in Private or Function storage class\n " + "%var_wg")); } TEST_F(ValidateDecorations, NonWritableVarPrivateBad) { - std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv"); 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 %var_priv")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_priv")); } TEST_F(ValidateDecorations, NonWritableVarPrivateV13Bad) { - std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %var_priv")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_priv")); } TEST_F(ValidateDecorations, NonWritableVarPrivateV14Good) { - std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv"); CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); @@ -6870,36 +7071,41 @@ } TEST_F(ValidateDecorations, NonWritableVarPrivateV13TargetV14Bad) { - std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv"); CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %var_priv")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_priv")); } TEST_F(ValidateDecorations, NonWritableVarFunctionBad) { - std::string spirv = ShaderWithNonWritableTarget("%var_func"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func"); 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 %var_func")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_func")); } TEST_F(ValidateDecorations, NonWritableArrayGood) { - std::string spirv = ShaderWithNonWritableTarget("%var_array_imstor"); + std::string spirv = + ShaderWithNonReadableNonWritableTarget("%var_array_imstor"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) { - std::string spirv = ShaderWithNonWritableTarget("%var_rta_imstor"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_rta_imstor"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -6940,18 +7146,20 @@ "is not valid for the Vulkan execution environment.")))); TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) { - std::string spirv = ShaderWithNonWritableTarget("%var_func"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func"); CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %var_func")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_func")); } TEST_F(ValidateDecorations, NonWritableVarFunctionV14Good) { - std::string spirv = ShaderWithNonWritableTarget("%var_func"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func"); CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); @@ -6959,14 +7167,16 @@ } TEST_F(ValidateDecorations, NonWritableVarFunctionV13TargetV14Bad) { - std::string spirv = ShaderWithNonWritableTarget("%var_func"); + std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func"); CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Target of NonWritable decoration is invalid: must " - "point to a storage image, uniform block, or storage " - "buffer\n %var_func")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, tensor variable in UniformConstant " + "storage class, uniform block, or storage " + "buffer\n %var_func")); } TEST_F(ValidateDecorations, BufferBlockV13ValV14Good) { @@ -7072,7 +7282,7 @@ CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Component-04924")); + AnyVUID("VUID-StandaloneSpirv-Component-10583")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Component decoration specified for type")); EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector")); @@ -7298,9 +7508,9 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-Component-04922")); - HasSubstr( - "Sequence of components starting with 2 " - "and ending with 6 gets larger than 3"); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 2 " + "and ending with 5 gets larger than 3")); } TEST_F(ValidateDecorations, ComponentDecoration64VecWideBadVulkan) { @@ -7437,6 +7647,8 @@ OpExecutionMode %main LocalSize 1 1 1 OpDecorate %struct Block OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 %void = OpTypeVoid %uint = OpTypeInt 32 0 %struct = OpTypeStruct %uint @@ -7550,6 +7762,8 @@ OpExecutionMode %main LocalSize 1 1 1 OpDecorate %struct Block OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 %void = OpTypeVoid %uint = OpTypeInt 32 0 %struct = OpTypeStruct %uint @@ -7574,6 +7788,8 @@ OpExecutionMode %main LocalSize 1 1 1 OpDecorate %struct BufferBlock OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 %void = OpTypeVoid %uint = OpTypeInt 32 0 %struct = OpTypeStruct %uint @@ -9388,9 +9604,9 @@ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Non-unique OpEntryPoint interface '2[%var]' is disallowed")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.4 or later, non-unique OpEntryPoint " + "interface '2[%var]' is disallowed")); } TEST_F(ValidateDecorations, PhysicalStorageBufferMissingOffset) { @@ -10239,6 +10455,7 @@ OpMemberDecorate %struct 1 Offset 4 )" + set + R"(OpMemberDecorate %test_type 0 Offset 0 OpMemberDecorate %test_type 1 Offset 1 +OpDecorate %ptr ArrayStride 16 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 @@ -10247,7 +10464,8 @@ %test_val = OpConstantNull %test_type %ptr = OpTypeUntypedPointerKHR )" + sc + R"( -%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct +%var = OpUntypedVariableKHR %ptr )" + + sc + R"( %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel @@ -10290,6 +10508,7 @@ OpMemberDecorate %struct 0 Offset 0 OpMemberDecorate %struct 1 Offset 4 )" + set + R"(OpDecorate %test_type ArrayStride 4 +OpDecorate %ptr ArrayStride 16 %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 @@ -10300,7 +10519,8 @@ %struct = OpTypeStruct %int %int %ptr = OpTypeUntypedPointerKHR )" + sc + R"( -%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct +%var = OpUntypedVariableKHR %ptr )" + + sc + R"( %struct %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel @@ -10437,6 +10657,808 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +const std::string kNodeShaderPrelude = R"( +OpCapability Shader +OpCapability ShaderEnqueueAMDX +OpExtension "SPV_AMDX_shader_enqueue" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpEntryPoint GLCompute %other "other" +)"; + +const std::string kNodeShaderPostlude = R"( +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%node0 = OpConstantStringAMDX "node0" +%node1 = OpConstantStringAMDX "node1" +%node2 = OpConstantStringAMDX "node2" +%S = OpTypeStruct +%_payloadarr_S_0 = OpTypeNodePayloadArrayAMDX %S +%_payloadarr_S = OpTypeNodePayloadArrayAMDX %S +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%other = OpFunction %void None %void_fn +%entry0 = OpLabel +OpReturn +OpFunctionEnd +)"; + +TEST_F(ValidateDecorations, NodeShader) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateDecorations, NodeShaderDecoratePayloadNodeName) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorate %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorate %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Decorations taking ID parameters may not be used with OpDecorate")); +} + +TEST_F(ValidateDecorations, NodeShaderDecoratePayloadNodeBaseIndex) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorate %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Decorations taking ID parameters may not be used with OpDecorate")); +} + +TEST_F(ValidateDecorations, NodeShaderDecoratePayloadNodeArraySize) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorate %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Decorations taking ID parameters may not be used with OpDecorate")); +} + +TEST_F(ValidateDecorations, NodeShaderDecorateNodeSharesPayloadLimitsWith) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorate %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Decorations taking ID parameters may not be used with OpDecorate")); +} + +TEST_F(ValidateDecorations, BlockArrayWithStride) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %array ArrayStride 4 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%struct = OpTypeStruct %int +%array = OpTypeArray %struct %int_4 +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Array containing a Block or BufferBlock must not be " + "decorated with ArrayStride")); +} + +TEST_F(ValidateDecorations, BufferBlockRuntimeArrayWithStride) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct BufferBlock +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %array ArrayStride 4 +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%array = OpTypeRuntimeArray %struct +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Array containing a Block or BufferBlock must not be " + "decorated with ArrayStride")); +} + +TEST_F(ValidateDecorations, BlockArrayWithoutStride) { + 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 %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%struct = OpTypeStruct %int +%array = OpTypeArray %struct %int_4 +%ptr = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr StorageBuffer +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, BlockArrayWithoutStrideUntypedAccessChain) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%struct = OpTypeStruct %int +%array = OpTypeArray %struct %int_4 +%void = OpTypeVoid +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %array +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpUntypedAccessChainKHR %ptr %array %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, InvalidLayoutBlockFunctionPre1p4) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%block = OpTypeStruct %int +%ptr_function_block = OpTypePointer Function %block +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_function_block Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateDecorations, InvalidLayoutBlockFunctionPost1p4) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%block = OpTypeStruct %int +%ptr_function_block = OpTypePointer Function %block +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_function_block Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutOffsetPrivatePre1p4) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%block = OpTypeStruct %int +%ptr_private_block = OpTypePointer Private %block +%void_fn = OpTypeFunction %void +%var = OpVariable %ptr_private_block Private +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, InvalidLayoutOffsetPrivatePost1p4) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%block = OpTypeStruct %int +%ptr_private_block = OpTypePointer Private %block +%void_fn = OpTypeFunction %void +%var = OpVariable %ptr_private_block Private +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutArrayStrideWorkgroupExplicitLayout) { + const std::string spirv = R"( +OpCapability Shader +OpCapability WorkgroupMemoryExplicitLayoutKHR +OpExtension "SPV_KHR_workgroup_memory_explicit_layout" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %array ArrayStride 4 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%ptr_wg_block = OpTypePointer Workgroup %array +%void_fn = OpTypeFunction %void +%var = OpVariable %ptr_wg_block Workgroup +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateDecorations, InvalidLayoutArrayStrideWorkgroup) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %array ArrayStride 4 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%ptr_wg_block = OpTypePointer Workgroup %array +%void_fn = OpTypeFunction %void +%var = OpVariable %ptr_wg_block Workgroup +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutArrayStrideUniformConstant) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %array ArrayStride 4 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%sampler = OpTypeSampler +%array = OpTypeArray %sampler %int_4 +%ptr_uc_block = OpTypePointer UniformConstant %array +%void_fn = OpTypeFunction %void +%var = OpVariable %ptr_uc_block UniformConstant +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutMatrixStrideFunctionPost1p4) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpMemberDecorate %block 0 MatrixStride 16 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%mat4x4 = OpTypeMatrix %v4float 4 +%block = OpTypeStruct %mat4x4 +%ptr_function_block = OpTypePointer Function %block +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_function_block Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutNestedMatrixStrideFunctionPost1p4) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpMemberDecorate %block 0 MatrixStride 16 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%mat4x4 = OpTypeMatrix %v4float 4 +%block = OpTypeStruct %mat4x4 +%block2 = OpTypeStruct %block +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%array = OpTypeArray %block2 %int_2 +%ptr_function_array = OpTypePointer Function %array +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_function_array Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutBufferBlockWorkgroup) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block BufferBlock +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%block = OpTypeStruct %int +%ptr_wg_block = OpTypePointer Workgroup %block +%void_fn = OpTypeFunction %void +%var = OpVariable %ptr_wg_block Workgroup +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10684")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid explicit layout decorations on type for operand")); +} + +TEST_F(ValidateDecorations, InvalidLayoutUntypedStore) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%block = OpTypeStruct %int +%block_null = OpConstantNull %block +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %block +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpStore %var %block_null +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, ExplicitLayoutOnPtrPhysicalStorageBuffer) { + const std::string spirv = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int ; ArrayStride 4 +%Foo = OpTypeStruct %_ptr_PhysicalStorageBuffer_int +%_ptr_Function_Foo = OpTypePointer Function %Foo +%int_0 = OpConstant %int 0 +%_ptr_Function__ptr_PhysicalStorageBuffer_int = OpTypePointer Function %_ptr_PhysicalStorageBuffer_int +%ulong = OpTypeInt 64 0 +%ulong_0 = OpConstant %ulong 0 +%main = OpFunction %void None %void_fn +%entry = OpLabel +%obj = OpVariable %_ptr_Function_Foo Function +%obj_member = OpAccessChain %_ptr_Function__ptr_PhysicalStorageBuffer_int %obj %int_0 +%nullptr = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_int %ulong_0 +OpStore %obj_member %nullptr +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateDecorations, RuntimeArrayNotLargestOffsetInBlock) { + 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 %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 16 +OpMemberDecorate %block 1 Offset 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +%block = OpTypeStruct %int %array +%ptr = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr StorageBuffer +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("has a runtime array at offset 0, but other members at " + "larger offsets")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); +} + +TEST_F(ValidateDecorations, RuntimeArrayNotLargestOffsetInBufferBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block BufferBlock +OpMemberDecorate %block 0 Offset 16 +OpMemberDecorate %block 1 Offset 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +%block = OpTypeStruct %int %array +%ptr = OpTypePointer Uniform %block +%var = OpVariable %ptr Uniform +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("has a runtime array at offset 0, but other members at " + "larger offsets")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); +} + +TEST_F(ValidateDecorations, LongVectorUniformPass) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int8 +OpCapability UniformAndStorageBuffer8BitAccess +OpCapability LongVectorEXT + +OpExtension "SPV_KHR_8bit_storage" +OpExtension "SPV_EXT_long_vector" + +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %BP_main "main" + +OpDecorate %input0 DescriptorSet 0 +OpDecorate %input0 Binding 0 +OpDecorate %a10testtype ArrayStride 12 +OpDecorate %buf BufferBlock +OpMemberDecorate %buf 0 Offset 0 + +%void = OpTypeVoid +%bool = OpTypeBool +%u32 = OpTypeInt 32 0 +%voidf = OpTypeFunction %void +%c_u32_10 = OpConstant %u32 10 +%vectorSizeConst = OpConstant %u32 12 + +%scalartype = OpTypeInt 8 1 +%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst + +%a10testtype = OpTypeArray %testtype %c_u32_10 +%buf = OpTypeStruct %a10testtype +%bufptr = OpTypePointer Uniform %buf + +%input0 = OpVariable %bufptr Uniform + + +%BP_main = OpFunction %void None %voidf +%BP_label = OpLabel +OpReturn +OpFunctionEnd + +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, LongVectorUniformSpecConstantFail) { + const std::string spirv = R"( +OpCapability Shader +OpCapability LongVectorEXT + +OpExtension "SPV_EXT_long_vector" + +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %BP_main "main" + +OpDecorate %input0 DescriptorSet 0 +OpDecorate %input0 Binding 0 +OpDecorate %buf BufferBlock +OpMemberDecorate %buf 0 Offset 0 +OpDecorate %spec_const SpecId 1 + +%void = OpTypeVoid +%bool = OpTypeBool +%u32 = OpTypeInt 32 0 +%voidf = OpTypeFunction %void +%spec_const = OpSpecConstant %u32 12 + +%scalartype = OpTypeInt 32 1 +%testtype = OpTypeVectorIdEXT %scalartype %spec_const + +%buf = OpTypeStruct %testtype +%bufptr = OpTypePointer Uniform %buf + +%input0 = OpVariable %bufptr Uniform + + +%BP_main = OpFunction %void None %voidf +%BP_label = OpLabel +OpReturn +OpFunctionEnd + +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Function-12294")); +} + +TEST_F(ValidateDecorations, LongVectorWorkgroupSpecConstantFail) { + const std::string spirv = R"( +OpCapability Shader +OpCapability LongVectorEXT +OpCapability WorkgroupMemoryExplicitLayoutKHR +OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + +OpExtension "SPV_EXT_long_vector" + +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %BP_main "main" + +OpDecorate %spec_const SpecId 1 +OpMemberDecorate %str 0 Offset 0 +OpDecorate %str Block + +%void = OpTypeVoid +%bool = OpTypeBool +%u32 = OpTypeInt 32 0 +%voidf = OpTypeFunction %void +%spec_const = OpSpecConstant %u32 12 + +%scalartype = OpTypeInt 32 1 +%testtype = OpTypeVectorIdEXT %scalartype %spec_const + +%str = OpTypeStruct %testtype +%ptr_str_Workgroup = OpTypePointer Workgroup %str +%var = OpVariable %ptr_str_Workgroup Workgroup + +%BP_main = OpFunction %void None %voidf +%BP_label = OpLabel +OpReturn +OpFunctionEnd + +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Function-12294")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_dot_product_test.cpp b/test/val/val_dot_product_test.cpp new file mode 100644 index 0000000..c4d5c89 --- /dev/null +++ b/test/val/val_dot_product_test.cpp
@@ -0,0 +1,418 @@ +// Copyright (c) 2026 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <ostream> +#include <string> + +#include "gmock/gmock.h" +#include "spirv-tools/libspirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateDotProductMixAcc = spvtest::ValidateBase<bool>; + +std::string GenerateShaderCode(const std::string& body) { + std::stringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Float8EXT +OpCapability BFloat16TypeKHR +OpCapability DotProductFloat16AccFloat32VALVE +OpCapability DotProductFloat16AccFloat16VALVE +OpCapability DotProductBFloat16AccVALVE +OpCapability DotProductFloat8AccFloat32VALVE +OpExtension "SPV_VALVE_mixed_float_dot_product" +OpExtension "SPV_EXT_float8" +OpExtension "SPV_KHR_bfloat16" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 + +%void = OpTypeVoid +%func = OpTypeFunction %void + +%f32 = OpTypeFloat 32 +%f16 = OpTypeFloat 16 +%bf16 = OpTypeFloat 16 BFloat16KHR +%f64 = OpTypeFloat 64 +%f8_e4m3 = OpTypeFloat 8 Float8E4M3EXT +%f8_e5m2 = OpTypeFloat 8 Float8E5M2EXT + +%v2f32 = OpTypeVector %f32 2 +%v2f16 = OpTypeVector %f16 2 +%v2bf16 = OpTypeVector %bf16 2 +%v3f16 = OpTypeVector %f16 3 +%v2f64 = OpTypeVector %f64 2 +%v4f8_e4m3 = OpTypeVector %f8_e4m3 4 +%v4f8_e5m2 = OpTypeVector %f8_e5m2 4 +%v4f16 = OpTypeVector %f16 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 +%bf16_1 = OpConstant %bf16 1 +%f8_e4m3_1 = OpConstant %f8_e4m3 1 +%f8_e5m2_1 = OpConstant %f8_e5m2 1 + +%v2f16_0 = OpConstantComposite %v2f16 %f16_0 %f16_0 +%v2f16_1 = OpConstantComposite %v2f16 %f16_1 %f16_1 +%v2bf16_1 = OpConstantComposite %v2bf16 %bf16_1 %bf16_1 +%v3f16_1 = OpConstantComposite %v3f16 %f16_1 %f16_1 %f16_1 +%v2f32_1 = OpConstantComposite %v2f32 %f32_1 %f32_1 +%v4f8_e4m3_1 = OpConstantComposite %v4f8_e4m3 %f8_e4m3_1 %f8_e4m3_1 %f8_e4m3_1 %f8_e4m3_1 +%v4f8_e5m2_1 = OpConstantComposite %v4f8_e5m2 %f8_e5m2_1 %f8_e5m2_1 %f8_e5m2_1 %f8_e5m2_1 +%v4f16_1 = OpConstantComposite %v4f16 %f16_1 %f16_1 %f16_1 %f16_1 + +%main = OpFunction %void None %func +%label = OpLabel +)"; + ss << body; + ss << R"( +OpReturn +OpFunctionEnd +)"; + return ss.str(); +} + +TEST_F(ValidateDotProductMixAcc, FDot2MixAcc32Float16Good) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDotProductMixAcc, FDot2MixAcc32BFloat16Good) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %v2bf16_1 %v2bf16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDotProductMixAcc, FDot2MixAcc16Float16Good) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2f16_1 %f16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDotProductMixAcc, FDot2MixAcc16BFloat16Good) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %bf16 %v2bf16_1 %v2bf16_1 %bf16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDotProductMixAcc, FDot4MixAcc32Float8E4M3Good) { + const std::string body = R"( + %res = OpFDot4MixAcc32VALVE %f32 %v4f8_e4m3_1 %v4f8_e4m3_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDotProductMixAcc, FDot4MixAcc32Float8E5M2Good) { + const std::string body = R"( + %res = OpFDot4MixAcc32VALVE %f32 %v4f8_e5m2_1 %v4f8_e5m2_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDotProductMixAcc, FDot4MixAcc32Float8MixedEncodingGood) { + const std::string body = R"( + %res = OpFDot4MixAcc32VALVE %f32 %v4f8_e4m3_1 %v4f8_e5m2_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateDotProductMixAcc, CommonNotVector) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %f16_1 %v2f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 'Vector 1' to be an vector.")); +} + +TEST_F(ValidateDotProductMixAcc, CommonBadLength) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %v3f16_1 %v3f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("components, but both need to be 2-components")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32BadResultType) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f16 %v2f16_1 %v2f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a 32-bit IEEE 754 float scalar type.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32BadComponentType) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %v2f32_1 %v2f32_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 'Vector 1' to be a vector of 16-bit floats.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32BadAccType) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Accumulator Type must be the same as the Result Type.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32MixedEncodingFloat16) { + const std::string body = R"( + %res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2bf16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("'Vector 1' and 'Vector 2' must be the same float encoding.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16BadResultType) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %f32 %v2f16_1 %v2f16_1 %f16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a 16-bit float scalar type.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16BadAccType) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Accumulator Type must be the same as the Result Type.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16MixedEncodingFloat16) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2bf16_1 %f16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("'Vector 1' and 'Vector 2' must be the same float encoding.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16ResultWrongEncoding) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %f16 %v2bf16_1 %v2bf16_1 %f16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must have the same float encoding as 'Vector " + "1' and 'Vector 2'.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16AccumulatorWrongEncoding) { + const std::string body = R"( + %res = OpFDot2MixAcc16VALVE %bf16 %v2bf16_1 %v2bf16_1 %f16_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Accumulator Type must be the same as the Result Type.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32Mix4BadVectorType) { + const std::string body = R"( + %res = OpFDot4MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("components, but both need to be 4-components")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32Mix4BadComponentType) { + const std::string body = R"( + %res = OpFDot4MixAcc32VALVE %f32 %v4f16_1 %v4f16_1 %f32_1 + )"; + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 'Vector 1' to be a vector of 8-bit floats.")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32MissingCapFloat16) { + const std::string ss = R"( +OpCapability Shader +OpCapability Float16 +OpCapability DotProductBFloat16AccVALVE +OpExtension "SPV_VALVE_mixed_float_dot_product" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 + +%void = OpTypeVoid +%func = OpTypeFunction %void +%f32 = OpTypeFloat 32 +%f16 = OpTypeFloat 16 +%v2f16 = OpTypeVector %f16 2 + +%f32_1 = OpConstant %f32 1 +%f16_1 = OpConstant %f16 1 +%v2f16_1 = OpConstantComposite %v2f16 %f16_1 %f16_1 + +%main = OpFunction %void None %func +%label = OpLabel +%res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f32_1 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DotProductFloat16AccFloat32VALVE capability is required")); +} + +TEST_F(ValidateDotProductMixAcc, Acc32MissingCapBFloat16) { + const std::string ss = R"( +OpCapability Shader +OpCapability BFloat16TypeKHR +OpCapability DotProductFloat16AccFloat32VALVE +OpExtension "SPV_VALVE_mixed_float_dot_product" +OpExtension "SPV_KHR_bfloat16" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 + +%void = OpTypeVoid +%func = OpTypeFunction %void +%f32 = OpTypeFloat 32 +%bf16 = OpTypeFloat 16 BFloat16KHR +%v2bf16 = OpTypeVector %bf16 2 + +%f32_1 = OpConstant %f32 1 +%bf16_1 = OpConstant %bf16 1 +%v2bf16_1 = OpConstantComposite %v2bf16 %bf16_1 %bf16_1 + +%main = OpFunction %void None %func +%label = OpLabel +%res = OpFDot2MixAcc32VALVE %f32 %v2bf16_1 %v2bf16_1 %f32_1 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DotProductBFloat16AccVALVE capability is required")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16MissingCapFloat16) { + const std::string ss = R"( +OpCapability Shader +OpCapability Float16 +OpCapability DotProductBFloat16AccVALVE +OpExtension "SPV_VALVE_mixed_float_dot_product" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 + +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%v2f16 = OpTypeVector %f16 2 + +%f16_1 = OpConstant %f16 1 +%v2f16_1 = OpConstantComposite %v2f16 %f16_1 %f16_1 + +%main = OpFunction %void None %func +%label = OpLabel +%res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2f16_1 %f16_1 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DotProductFloat16AccFloat16VALVE capability is required")); +} + +TEST_F(ValidateDotProductMixAcc, Acc16MissingCapBFloat16) { + const std::string ss = R"( +OpCapability Shader +OpCapability BFloat16TypeKHR +OpCapability DotProductFloat16AccFloat16VALVE +OpExtension "SPV_VALVE_mixed_float_dot_product" +OpExtension "SPV_KHR_bfloat16" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 + +%void = OpTypeVoid +%func = OpTypeFunction %void +%bf16 = OpTypeFloat 16 BFloat16KHR +%v2bf16 = OpTypeVector %bf16 2 + +%bf16_1 = OpConstant %bf16 1 +%v2bf16_1 = OpConstantComposite %v2bf16 %bf16_1 %bf16_1 + +%main = OpFunction %void None %func +%label = OpLabel +%res = OpFDot2MixAcc16VALVE %bf16 %v2bf16_1 %v2bf16_1 %bf16_1 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DotProductBFloat16AccVALVE capability is required")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_entry_point_test.cpp b/test/val/val_entry_point_test.cpp index f28cf5d..80b90a5 100644 --- a/test/val/val_entry_point_test.cpp +++ b/test/val/val_entry_point_test.cpp
@@ -50,6 +50,33 @@ HasSubstr("Entry points cannot share the same name")); } +TEST_F(ValidateEntryPoints, DuplicateConditionalEntryPointsAllowed) { + const std::string body = R"( +OpCapability Shader +OpCapability SpecConditionalINTEL +OpExtension "SPV_INTEL_function_variants" +OpMemoryModel Logical GLSL450 +OpConditionalEntryPointINTEL %10 GLCompute %3 "foo" +OpConditionalEntryPointINTEL %11 GLCompute %4 "foo" +%bool = OpTypeBool +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%10 = OpSpecConstantTrue %bool +%11 = OpSpecConstantFalse %bool +%3 = OpFunction %1 None %2 +%20 = OpLabel +OpReturn +OpFunctionEnd +%4 = OpFunction %1 None %2 +%21 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateEntryPoints, UniqueEntryPoints) { const std::string body = R"( OpCapability Shader
diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp index bd4ab3c..e3dedaf 100644 --- a/test/val/val_ext_inst_debug_test.cpp +++ b/test/val/val_ext_inst_debug_test.cpp
@@ -22,9 +22,151 @@ #include "gmock/gmock.h" #include "spirv-tools/libspirv.h" +#include "spirv/unified1/NonSemanticShaderDebugInfo.h" +#include "spirv/unified1/NonSemanticShaderDebugInfo100.h" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h" +// Verify that the frozen NonSemanticShaderDebugInfo100.h opcode values match +// the version-agnostic NonSemanticShaderDebugInfo.h values. The two headers +// use different enum type names but must have identical opcode numbers. +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugInfoNone) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugInfoNone)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugCompilationUnit) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugCompilationUnit)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeBasic) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeBasic)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypePointer) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypePointer)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeQualifier) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeQualifier)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeArray) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeArray)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeVector) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeVector)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypedef) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypedef)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeFunction) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeFunction)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeEnum) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeEnum)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeComposite) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeComposite)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeMember) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeMember)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeInheritance) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeInheritance)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypePtrToMember) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypePtrToMember)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeTemplate) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeTemplate)); +static_assert(static_cast<uint32_t>( + NonSemanticShaderDebugInfo100DebugTypeTemplateParameter) == + static_cast<uint32_t>( + NonSemanticShaderDebugInfoDebugTypeTemplateParameter)); +static_assert( + static_cast<uint32_t>( + NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter) == + static_cast<uint32_t>( + NonSemanticShaderDebugInfoDebugTypeTemplateTemplateParameter)); +static_assert( + static_cast<uint32_t>( + NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack) == + static_cast<uint32_t>( + NonSemanticShaderDebugInfoDebugTypeTemplateParameterPack)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugGlobalVariable) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugGlobalVariable)); +static_assert( + static_cast<uint32_t>( + NonSemanticShaderDebugInfo100DebugFunctionDeclaration) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugFunctionDeclaration)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugFunction) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugFunction)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugLexicalBlock) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugLexicalBlock)); +static_assert( + static_cast<uint32_t>( + NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator) == + static_cast<uint32_t>( + NonSemanticShaderDebugInfoDebugLexicalBlockDiscriminator)); +static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugScope) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugScope)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugNoScope) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugNoScope)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugInlinedAt) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugInlinedAt)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugLocalVariable) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugLocalVariable)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugInlinedVariable) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugInlinedVariable)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugDeclare) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugDeclare)); +static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugValue) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugValue)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugOperation) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugOperation)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugExpression) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugExpression)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugMacroDef) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugMacroDef)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugMacroUndef) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugMacroUndef)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugImportedEntity) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugImportedEntity)); +static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugSource) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugSource)); +static_assert( + static_cast<uint32_t>( + NonSemanticShaderDebugInfo100DebugFunctionDefinition) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugFunctionDefinition)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugSourceContinued) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugSourceContinued)); +static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugLine) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugLine)); +static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugNoLine) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugNoLine)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugBuildIdentifier) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugBuildIdentifier)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugStoragePath) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugStoragePath)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugEntryPoint) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugEntryPoint)); +static_assert( + static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeMatrix) == + static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeMatrix)); + namespace spvtools { namespace val { namespace { @@ -84,12 +226,24 @@ using ValidateVulkan100DebugInfoDebugValue = spvtest::ValidateBase<std::pair<std::string, std::string>>; using ValidateVulkan100DebugInfo = spvtest::ValidateBase<std::string>; +using ValidateVulkan101DebugInfo = spvtest::ValidateBase<std::string>; -const static std::string shader_extension = R"( +const static std::string shader_extension_100 = R"( OpExtension "SPV_KHR_non_semantic_info" %DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" )"; +const static std::string shader_extension_101 = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.101" +)"; + +// Extension string for a future NSDI version; exercises forward-compatibility. +const static std::string shader_extension_9999 = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.9999" +)"; + const static std::string opencl_extension = R"( %DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" )"; @@ -113,16 +267,9 @@ ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; ss << "OpMemoryModel Logical GLSL450\n"; ss << "OpEntryPoint " << execution_model << " %main \"main\"" - << " %f32_output" - << " %f32vec2_output" - << " %u32_output" - << " %u32vec2_output" - << " %u64_output" - << " %f32_input" - << " %f32vec2_input" - << " %u32_input" - << " %u32vec2_input" - << " %u64_input" + << " %f32_output" << " %f32vec2_output" << " %u32_output" + << " %u32vec2_output" << " %u64_output" << " %f32_input" + << " %f32vec2_input" << " %u32_input" << " %u32vec2_input" << " %u64_input" << "\n"; if (execution_model == "Fragment") { ss << "OpExecutionMode %main OriginUpperLeft\n"; @@ -359,7 +506,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", "", dbg_inst, shader_extension, "Vertex")); + src, "", "", dbg_inst, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -427,8 +574,9 @@ %foo_val = OpLoad %u32 %foo )"; - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header + GetParam(), body, shader_extension, "Vertex")); + CompileSuccessfully( + GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header + GetParam(), + body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " @@ -511,7 +659,7 @@ %float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float %v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 13 %main )"; const std::string body = R"( @@ -742,7 +890,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst, "", shader_extension, "Vertex")); + src, "", dbg_inst, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Source must be a result id of " @@ -796,7 +944,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Name must be a result id of " @@ -850,7 +998,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Size must be a result id of " @@ -875,7 +1023,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Flags must be a result id of 32-bit " @@ -1014,7 +1162,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1037,7 +1185,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -1238,6 +1386,36 @@ "integer scalar type")); } +TEST_F(ValidateOpenCL100DebugInfo, + DebugTypeArrayOpSpecConstantComponentCountFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%spec_u32 = OpSpecConstant %u32 4 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %spec_u32 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", opencl_extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) { const std::string src = R"( %src = OpString "simple.hlsl" @@ -1253,7 +1431,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1282,7 +1460,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1301,7 +1479,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Base Type is not a valid debug " @@ -1323,13 +1501,13 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); + HasSubstr("Component Count must be a constant instruction with a " + "32- or 64-bits integer scalar type or " + "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or 64-bits unsigned integer scalar type")); } TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) { @@ -1347,13 +1525,13 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); + HasSubstr("Component Count must be a constant instruction with a " + "32- or 64-bits integer scalar type or " + "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or 64-bits unsigned integer scalar type")); } TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayComponentCountZero) { @@ -1371,7 +1549,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1398,13 +1576,62 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); + HasSubstr("Component Count must be a constant instruction with a " + "32- or 64-bits integer scalar type or " + "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or 64-bits unsigned integer scalar type")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayOpSpecConstantComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%spec_u32 = OpSpecConstant %u32 4 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %spec_u32 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, + DebugTypeArrayOpSpecConstantOpComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%spec_a = OpSpecConstant %u32 2 +%spec_b = OpSpecConstant %u32 2 +%spec_op = OpSpecConstantOp %u32 IMul %spec_a %spec_b +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %spec_op +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { @@ -1523,7 +1750,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1542,7 +1769,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Base Type must be a result id of " @@ -1564,7 +1791,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Component Count must be positive " @@ -1586,7 +1813,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Component Count must be positive " @@ -1613,7 +1840,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1637,7 +1864,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Vector Type must be a result id of " @@ -1664,7 +1891,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Vector Count must be a result id of " @@ -1691,7 +1918,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Vector Count must be positive " @@ -1718,7 +1945,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Vector Count must be positive " @@ -1808,7 +2035,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1831,7 +2058,7 @@ ss << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", ss.str(), "", shader_extension, "Vertex")); + src, "", ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second + @@ -1951,7 +2178,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -1970,7 +2197,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -1992,7 +2219,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -2103,7 +2330,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -2126,7 +2353,7 @@ ss << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", ss.str(), "", shader_extension, "Vertex")); + src, "", ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -2191,7 +2418,7 @@ %v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 %VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 6 1 %comp_unit %main_name FlagIsPublic 13 %main %foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic %child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic )"; @@ -2236,7 +2463,7 @@ %v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 %VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 13 %main %foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic %child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic )"; @@ -2418,7 +2645,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -2456,7 +2683,7 @@ ss << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, ss.str(), "", shader_extension, "Vertex")); + src, constants, ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second + " must be ")); @@ -2517,7 +2744,7 @@ ss << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, ss.str(), "", shader_extension, "Vertex")); + src, constants, ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); if (!param.second.empty()) { EXPECT_THAT(getDiagnosticString(), @@ -2563,8 +2790,8 @@ %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main)"; +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main)"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( src, "", dbg_inst_header, "", opencl_extension, "Vertex")); @@ -2588,7 +2815,7 @@ %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic %main_info = OpExtInst %void %DbgExt DebugFunction )" << param.first; @@ -2603,25 +2830,25 @@ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{ std::make_pair( - R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main)", + R"(%u32_0 %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main)", "Name"), std::make_pair( - R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main)", + R"(%main_name %dbg_src %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main)", "Type"), std::make_pair( - R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_name FlagIsPublic 13 %main)", + R"(%main_name %main_type_info %comp_unit 4 1 %comp_unit %main_name FlagIsPublic 13 %main)", "Source"), std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_name FlagIsPublic 13 %main)", + R"(%main_name %main_type_info %dbg_src 4 1 %dbg_src %main_name FlagIsPublic 13 %main)", "Parent"), std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", + R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %void FlagIsPublic 13 %main)", "Linkage Name"), std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %void)", + R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %void)", "Function"), std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main %dbg_src)", + R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main %dbg_src)", "Declaration"), })); @@ -2657,19 +2884,19 @@ ValidateOpenCL100DebugInfoDebugFunctionDeclaration, ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{ std::make_pair( - R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic)", + R"(%u32_0 %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic)", "Name"), std::make_pair( - R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_name FlagIsPublic)", + R"(%main_name %dbg_src %dbg_src 4 1 %comp_unit %main_name FlagIsPublic)", "Type"), std::make_pair( - R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_name FlagIsPublic)", + R"(%main_name %main_type_info %comp_unit 4 1 %comp_unit %main_name FlagIsPublic)", "Source"), std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_name FlagIsPublic)", + R"(%main_name %main_type_info %dbg_src 4 1 %dbg_src %main_name FlagIsPublic)", "Parent"), std::make_pair( - R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", + R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %void FlagIsPublic)", "Linkage Name"), })); @@ -2683,21 +2910,16 @@ " )"; - const std::string constants = R"( -%u32_12 = OpConstant %u32 12 -%u32_13 = OpConstant %u32 13 -)"; - const std::string dbg_inst_header = R"( %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 -%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13 +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4 )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -2711,11 +2933,6 @@ " )"; - const std::string constants = R"( -%u32_12 = OpConstant %u32 12 -%u32_13 = OpConstant %u32 13 -)"; - const auto& param = GetParam(); std::ostringstream ss; @@ -2723,12 +2940,12 @@ %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void -%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %main_info = OpExtInst %void %DbgExt DebugFunction )" << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, ss.str(), "", shader_extension, "Vertex")); + src, "", ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -2738,22 +2955,22 @@ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugFunction, ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{ std::make_pair( - R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13)", + R"(%u32_0 %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4)", "Name"), std::make_pair( - R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13)", + R"(%main_name %dbg_src %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4)", "Type"), std::make_pair( - R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13)", + R"(%main_name %main_type_info %comp_unit %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4)", "Source"), std::make_pair( - R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_name %u32_3 %u32_13)", + R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %dbg_src %main_name %u32_3 %u32_4)", "Parent"), std::make_pair( - R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3 %u32_13)", + R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %void %u32_3 %u32_4)", "Linkage Name"), std::make_pair( - R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13 %dbg_src)", + R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4 %dbg_src)", "Declaration"), })); @@ -2767,11 +2984,6 @@ " )"; - const std::string constants = R"( -%u32_12 = OpConstant %u32 12 -%u32_13 = OpConstant %u32 13 -)"; - const auto& param = GetParam(); std::ostringstream ss; @@ -2783,7 +2995,7 @@ << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, ss.str(), "", shader_extension, "Vertex")); + src, "", ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -2794,19 +3006,19 @@ ValidateVulkan100DebugInfoDebugFunctionDeclaration, ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{ std::make_pair( - R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3)", + R"(%u32_0 %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3)", "Name"), std::make_pair( - R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3)", + R"(%main_name %dbg_src %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3)", "Type"), std::make_pair( - R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_name %u32_3)", + R"(%main_name %main_type_info %comp_unit %u32_4 %u32_1 %comp_unit %main_name %u32_3)", "Source"), std::make_pair( - R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_name %u32_3)", + R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %dbg_src %main_name %u32_3)", "Parent"), std::make_pair( - R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3)", + R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %void %u32_3)", "Linkage Name"), })); @@ -2831,7 +3043,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunction: expected operand Type must be a result " @@ -2939,7 +3151,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -2959,7 +3171,7 @@ << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", ss.str(), "", shader_extension, "Vertex")); + src, "", ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -2991,7 +3203,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); } @@ -3012,7 +3224,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); } @@ -3107,7 +3319,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, "", shader_extension, "Vertex")); + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3134,7 +3346,7 @@ << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, ss.str(), "", shader_extension, "Vertex")); + src, constants, ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -3227,7 +3439,7 @@ %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 - %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 20 %18 OpLine %4 1 1 %main = OpFunction %void None %23 %24 = OpLabel @@ -3319,7 +3531,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, body, shader_extension, "Vertex")); + src, constants, dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3356,7 +3568,6 @@ %u32_5 = OpConstant %uint 5 %u32_10 = OpConstant %uint 10 %u32_17 = OpConstant %uint 17 - %u32_28 = OpConstant %uint 28 %u32_32 = OpConstant %uint 32 %uint_32 = OpConstant %uint 32 %float = OpTypeFloat 32 @@ -3374,7 +3585,7 @@ %16 = OpExtInst %void %1 DebugTypeFunction %u32_3 %void %15 %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 %u32_1 %u32_1 %13 %17 %u32_3 %u32_1 %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 %u32_1 %u32_17 %18 %u32_4 %u32_0 - %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_28 %18 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_4 %18 %main = OpFunction %void None %23 %24 = OpLabel %param_var_foo = OpVariable %_ptr_Function_float Function @@ -3420,8 +3631,9 @@ %decl = OpExtInst %void %DbgExt DebugDeclare )" << param.first; - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, ss.str(), shader_extension, "Vertex")); + CompileSuccessfully( + GenerateShaderCodeForDebugInfo(src, constants, dbg_inst_header, ss.str(), + shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -3470,7 +3682,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - "", "", dbg_inst_header, "", shader_extension, "Vertex")); + "", "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3481,7 +3693,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - "", "", dbg_inst_header, "", shader_extension, "Vertex")); + "", "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -3677,7 +3889,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3705,7 +3917,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3732,7 +3944,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3757,7 +3969,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Target must be DebugTypeComposite or " @@ -3786,7 +3998,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -3962,7 +4174,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3984,7 +4196,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4005,7 +4217,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4025,7 +4237,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4048,7 +4260,7 @@ << param.first; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", ss.str(), "", shader_extension, "Vertex")); + src, "", ss.str(), "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -4175,7 +4387,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4200,7 +4412,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); } @@ -4226,7 +4438,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); } @@ -4365,7 +4577,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, body, shader_extension, "Vertex")); + src, constants, dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4399,7 +4611,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, body, shader_extension, "Vertex")); + src, constants, dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4431,8 +4643,9 @@ %decl = OpExtInst %void %DbgExt DebugValue )" << param.first; - CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, ss.str(), shader_extension, "Vertex")); + CompileSuccessfully( + GenerateShaderCodeForDebugInfo(src, constants, dbg_inst_header, ss.str(), + shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand " + param.second)); @@ -4625,7 +4838,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4647,7 +4860,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunctionDefinition: expected operand Function " @@ -4672,7 +4885,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunctionDefinition: expected operand Definition " @@ -4700,7 +4913,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunctionDefinition: Was used multiple times in " @@ -4733,7 +4946,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunctionDefinition: Was referenced a " @@ -4765,7 +4978,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunctionDefinition: operand Definition must " @@ -4799,7 +5012,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, constants, dbg_inst_header, body, shader_extension, "Vertex")); + src, constants, dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugFunctionDefinition: must be in the entry basic " @@ -4831,14 +5044,15 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateVulkan100DebugInfo, DebugLine) { const std::string src = R"( %src = OpString "simple.hlsl" -%code = OpString "int main() { }" +%code = OpString "int main() { +}" )"; const std::string dbg_inst_header = R"( @@ -4853,7 +5067,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4869,7 +5083,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("debug info extension must appear in a function body")); @@ -4887,7 +5101,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, "", shader_extension, "Vertex")); + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("debug info extension must appear in a function body")); @@ -4910,7 +5124,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugLine: expected operand Source must be a result " @@ -4932,7 +5146,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugLine: expected operand Line Start must be a " @@ -4954,7 +5168,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugLine: expected operand Line Start must be a " @@ -4977,13 +5191,35 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("DebugLine: expected operand Line Start must be a " "result id of 32-bit unsigned OpConstant")); } +TEST_F(ValidateVulkan100DebugInfo, DebugLineLineStartZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() { }" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_0 %u32_1 %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Line Start (0) is not allowed, " + "source lines start at Line 1")); +} + TEST_F(ValidateVulkan100DebugInfo, DebugLineLineEndSmaller) { const std::string src = R"( %src = OpString "simple.hlsl" @@ -4999,7 +5235,7 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), @@ -5021,12 +5257,1137 @@ )"; CompileSuccessfully(GenerateShaderCodeForDebugInfo( - src, "", dbg_inst_header, body, shader_extension, "Vertex")); + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Column End (0) is less than Column " + "Start (1) when Line Start equals Line End")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnEndSmallerMultiline) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() { +}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_1 %u32_2 %u32_1 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineOutOfBounds) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "// line 1 +int main() { +}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_4 %u32_4 %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Line End (4) is larger then the 3 " + "lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnOutOfBounds) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "// line 1 +123" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_2 %u32_2 %u32_1 %u32_5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Column End (5) is larger then Line " + "2 column length of 4 found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnOutOfBoundsEmptyLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "next line is empty + +line 3" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_2 %u32_2 %u32_2 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Column End (2) is larger then Line " + "2 column length of 1 found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnEmptyLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "next line is empty + +line 3" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_2 %u32_2 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnStartOutOfBounds) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "1 +123 +line 3" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_1 %u32_2 %u32_3 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugLine: operand Column Start (3) is larger then Line 1 " + "column length of 2 found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnEndOutOfBounds) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "1 +123 +line 3" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_1 %u32_2 %u32_1 %u32_5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Column End (5) is larger then Line " + "2 column length of 4 found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineDebugSourceContinuedSuccess) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +line 2 +line 3 +" +%code2 = OpString "line 4 +line 5" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_4 %u32_5 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineDebugSourceContinued) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +line 2 +" +%code2 = OpString "line 3 +line 4" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_4 %u32_5 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Line End (5) is larger then the 4 " + "lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, + DebugLineMultipleDebugSourceContinuedSuccess) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +" +%code2 = OpString "line 2 +line 3 +" +%code3 = OpString "line 4 +5" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3 +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_5 %u32_5 %u32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugLineMultipleDebugSourceContinued) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +" +%code2 = OpString "line 2 +line 3 +" +%code3 = OpString "line 4 +5" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3 +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_5 %u32_5 %u32_1 %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugLine: operand Column End (3) is larger then Line " + "5 column length of 2 found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceLineNoText) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src +)"; + + const std::string body = R"( +%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_5 %u32_5 %u32_1 %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeMemberLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +line 2 +line 3" +%uint_name = OpString "uint" +%member_name = OpString "x" +)"; + + const std::string constants = R"( +%u32_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_4 %u32_0 +%x = OpExtInst %void %DbgExt DebugTypeMember %member_name %uint_info %dbg_src %u32_4 %u32_1 %u32_0 %u32_128 %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugTypeMember: operand Line (4) is larger then the " + "3 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeMemberColumn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +line 2 +3" +%uint_name = OpString "uint" +%member_name = OpString "x" +)"; + + const std::string constants = R"( +%u32_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_4 %u32_0 +%x = OpExtInst %void %DbgExt DebugTypeMember %member_name %uint_info %dbg_src %u32_3 %u32_3 %u32_0 %u32_128 %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugTypeMember: operand Column End (3) is larger then Line 3 " + "column length of 2 found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugFunctionLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() { }" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_2 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +)"; + + const std::string body = R"( +%main_def = OpExtInst %void %DbgExt DebugFunctionDefinition %main_info %main +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugFunction: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugFunctionDeclarationLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() { }" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_2 %u32_1 %comp_unit %main_name %u32_3 +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugFunctionDeclaration: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugLexicalBlockLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src %u32_2 %u32_1 %comp_unit %main_name +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugLexicalBlock: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypedefLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src %u32_2 %u32_1 %comp_unit +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugTypedef: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugEnumLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src %u32_2 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugTypeEnum: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeCompositeLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_2 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugTypeComposite: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugGlobalVariableLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %t %dbg_src %u32_2 %u32_0 %comp_unit %foo_name %f32_input %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugGlobalVariable: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugLocalVariableLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_2 %u32_1 %comp_unit %u32_4 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugLocalVariable: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeTemplateParameterLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3 +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_2 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( - "DebugLine: operand Column End (0) is less than Column Start (1)")); + "DebugTypeTemplateParameter: operand Line (2) is larger then the " + "1 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicExtraOperand) { + // A module declaring NSDI version 9999 may add an optional trailing operand + // to DebugTypeBasic. The parser absorbs it via the VARIABLE_ID sentinel; + // the validator accepts it because nsdi_version > kNSDIKnownVersion. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_9999, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, UnknownInstructionAccepted) { + // Opcode 20000 is not defined in NSDI 100 (highest known opcode is 108). + // Both the text assembler and binary decoder handle it via the VARIABLE_ID + // fallback for unknown non-semantic opcodes; the validator's switch falls + // through to SPV_SUCCESS. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%unknown_inst = OpExtInst %void %DbgExt 20000 %u32_0 %u32_1 %u32_2 %u32_3 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicTwoExtraOperands) { + // Two extra trailing operands on an NSDI 9999 module; confirms the + // VARIABLE_ID sentinel re-expands as many times as needed, not just once. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 %u32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_9999, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugSourceExtraOperand) { + // DebugSource already has one optional operand (Text) in NSDI 100. An + // additional trailing operand tests the VARIABLE_ID sentinel when a + // grammar-defined OPTIONAL_ID is also present. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code %u32_0 +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, UnknownInstructionNoOperands) { + // Opcode 20000 with zero operands; tests the zero-operand edge case. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%unknown_inst = OpExtInst %void %DbgExt 20000 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, UnknownInstructionManyOperands) { + // Opcode 20000 with eight operands; tests that no loop limit is exceeded. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%unknown_inst = OpExtInst %void %DbgExt 20000 %u32_0 %u32_1 %u32_2 %u32_3 %u32_4 %u32_5 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugNoScopeExtraOperandInBody) { + // DebugNoScope is allowed in function bodies and has zero required operands. + // An extra trailing operand exercises the VARIABLE_ID sentinel in the body + // context, where isStartOfNewInst() uses the startsWithOp() fast path + // rather than the '%result = OpXxx' lookahead used at global scope. + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "int main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +)"; + + const std::string body = R"( +%no_scope = OpExtInst %void %DbgExt DebugNoScope %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// From +// https://github.com/KhronosGroup/SPIRV-Tools/pull/5986#issuecomment-4156843064 +TEST_F(ValidateVulkan100DebugInfo, + DebugLineDebugSourceContinuedEmptyNewLineGood) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +line 2 +" +%code2 = OpString "line 3 +line 4 is really long and hold 32char here" + +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_4 %u32_32 %comp_unit %foo_name %u32_32 %u32_3 %a +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// From +// https://github.com/KhronosGroup/SPIRV-Tools/pull/6623#issuecomment-4206751217 +TEST_F(ValidateVulkan100DebugInfo, DebugLineDebugSourceContinuedSameLine) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code0 = OpString "line 1 here +" +%code1 = OpString "line 2 here" +%code2 = OpString " still on line 2" +%code3 = OpString " still on line 2 and its long +line 3 +" +%code4 = OpString "line 4" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src0 = OpExtInst %void %DbgExt DebugSource %src %code0 +%dbg_src1 = OpExtInst %void %DbgExt DebugSourceContinued %code1 +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3 +%dbg_src4 = OpExtInst %void %DbgExt DebugSourceContinued %code4 +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src0 %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src0 %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src0 %u32_2 %u32_32 %comp_unit %foo_name %u32_32 %u32_3 %a +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// From +// https://github.com/KhronosGroup/SPIRV-Tools/pull/5986#issuecomment-4156843064 +TEST_F(ValidateVulkan100DebugInfo, + DebugLineDebugSourceContinuedEmptyNewLineBad) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1 +line 2 +" ; ignored +%code2 = OpString "line 3 +line 4 and there is no line 5" + +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_5 %u32_1 %comp_unit %foo_name %u32_32 %u32_3 %a +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugTypeComposite: operand Line (5) is larger then " + "the 4 lines found in the DebugSource text")); +} + +TEST_F(ValidateVulkan100DebugInfo, + DebugLineDebugSourceContinuedCombinedLineColumnCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "line 1" ; 6 column length +%code2 = OpString "still line 1" ; 12 char +%code3 = OpString "still line 1" ; 12 char + end-line + +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2 +%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3 +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3 +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_32 %comp_unit %foo_name %u32_32 %u32_3 %a +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DebugTypeComposite: operand Column End (32) is larger then " + "Line 1 column length of 31 found in the DebugSource text")); +} + +// Tests for NonSemantic.Shader.DebugInfo.101 instructions + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXT) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %u32_4 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXTWithSpecConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%spec_count = OpSpecConstant %u32 4 +%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %spec_count +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeCooperativeMatrixKHR) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %u32_3 %u32_8 %u32_16 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeCooperativeMatrixKHRWithSpecConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%spec_scope = OpSpecConstant %u32 3 +%spec_rows = OpSpecConstant %u32 8 +%spec_cols = OpSpecConstant %u32 16 +%spec_use = OpSpecConstant %u32 0 +%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %spec_scope %spec_rows %spec_cols %spec_use +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeBasicWithFPEncoding) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_8 = OpConstant %u32 8 +%u32_4214 = OpConstant %u32 4214 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%fp8_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_8 %u32_3 %u32_0 %u32_4214 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorIdEXTFailVersion) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %u32_4 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugTypeVectorIdEXT: requires " + "NonSemantic.Shader.DebugInfo version")); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXTFailComponentType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_vec = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_vec %u32_4 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Component Type must be a result id " + "of DebugTypeBasic")); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXTFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %float_info +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be a result id of a 32-bit " + "unsigned integer constant or specialization " + "constant")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeCooperativeMatrixKHRFailVersion) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %u32_3 %u32_8 %u32_16 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_100, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugTypeCooperativeMatrixKHR: requires " + "NonSemantic.Shader.DebugInfo version")); +} + +TEST_F(ValidateVulkan101DebugInfo, + DebugTypeCooperativeMatrixKHRFailComponentType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%float_vec = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_vec %u32_3 %u32_8 %u32_16 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Component Type must be a result id " + "of DebugTypeBasic")); +} + +TEST_F(ValidateVulkan101DebugInfo, + DebugTypeCooperativeMatrixKHRFailScopeNotConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %dbg_src %u32_8 %u32_16 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Scope must be a result id of a 32-bit unsigned " + "integer constant or specialization constant")); +} + +TEST_F(ValidateVulkan101DebugInfo, DebugTypeBasicFPEncodingFailNotConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_8 = OpConstant %u32 8 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%fp8_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_8 %u32_3 %u32_0 %dbg_src +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", shader_extension_101, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FPEncoding must be a result id of 32-bit unsigned " + "OpConstant")); } } // namespace
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index 23fd3aa..0c900ef 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp
@@ -7569,6 +7569,92 @@ "DebugTypeFunction %uint_0 %12\n")); } +using ValidateNSDI = spvtest::ValidateBase<bool>; + +TEST_F(ValidateNSDI, ValidVersion100) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +OpMemoryModel Logical GLSL450 +)"; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateNSDI, MissingVersion) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo." +OpMemoryModel Logical GLSL450 +)"; + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonSemantic.Shader.DebugInfo import does not encode " + "the version correctly")); +} + +TEST_F(ValidateNSDI, BadVersionNotANumber) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.1a" +OpMemoryModel Logical GLSL450 +)"; + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonSemantic.Shader.DebugInfo import does not encode " + "the version correctly")); +} + +TEST_F(ValidateNSDI, BadVersionTooLow) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.99" +OpMemoryModel Logical GLSL450 +)"; + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonSemantic.Shader.DebugInfo import version 99 is " + "below the minimum supported version 100")); +} + +TEST_F(ValidateNSDI, AcceptedFutureVersion) { + // Any version >= 100 is accepted; later versions are supersets of earlier + // ones. + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.999" +OpMemoryModel Logical GLSL450 +)"; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateNSDI, FutureVersion101) { + // Version 101 is >= kNSDIMinVersion; the validator imposes no upper bound. + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.101" +OpMemoryModel Logical GLSL450 +)"; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_extension_spv_ext_descriptor_heap.cpp b/test/val/val_extension_spv_ext_descriptor_heap.cpp new file mode 100644 index 0000000..e387546 --- /dev/null +++ b/test/val/val_extension_spv_ext_descriptor_heap.cpp
@@ -0,0 +1,2422 @@ +// Copyright 2025 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. + +// Tests for SPV_EXT_descriptor_heap + +#include <string> + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateSpvEXTDescriptorHeap = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvEXTDescriptorHeap, Valid) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability ShaderNonUniform + OpCapability UniformBufferArrayNonUniformIndexing + OpCapability StorageBufferArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs %index + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %sampler_heap "sampler_heap" + OpName %uvs "uvs" + OpName %index "index" + OpName %UniformBuffer "UniformBuffer" + OpMemberName %UniformBuffer 0 "colorOffset" + OpName %StorageBufferA "StorageBufferA" + OpMemberName %StorageBufferA 0 "a" + OpName %StorageBufferB "StorageBufferB" + OpMemberName %StorageBufferB 0 "b" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17 + OpDecorate %sampler_heap BuiltIn SamplerHeapEXT + OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2 + OpDecorate %uvs Location 0 + OpDecorate %index Flat + OpDecorate %index Location 1 + OpDecorate %38 NonUniform + OpDecorate %UniformBuffer Block + OpMemberDecorate %UniformBuffer 0 Offset 0 + OpDecorateId %_runtimearr_41 ArrayStrideIdEXT %42 + OpDecorate %44 NonUniform + OpDecorate %46 NonUniform + OpDecorate %48 NonUniform + OpDecorate %StorageBufferA Block + OpMemberDecorate %StorageBufferA 0 Offset 0 + OpDecorateId %_runtimearr_58 ArrayStrideIdEXT %59 + OpDecorate %64 NonUniform + OpDecorate %StorageBufferB Block + OpMemberDecorate %StorageBufferB 0 Offset 0 + OpDecorateId %_runtimearr_58_0 ArrayStrideIdEXT %69 + OpDecorate %71 NonUniform + OpDecorate %73 NonUniform + OpDecorate %77 NonUniform + OpDecorate %44 NonReadable + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %fragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_27 = OpConstant %int 27 + %14 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %17 = OpConstantSizeOfEXT %int %14 +%_runtimearr_14 = OpTypeRuntimeArray %14 +%sampler_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int_0 = OpConstant %int 0 + %22 = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_runtimearr_22 = OpTypeRuntimeArray %22 + %28 = OpTypeSampledImage %14 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %uvs = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_uint = OpTypePointer Input %uint + %index = OpVariable %_ptr_Input_uint Input +%UniformBuffer = OpTypeStruct %v4float + %41 = OpTypeBufferEXT Uniform + %42 = OpConstantSizeOfEXT %int %41 +%_runtimearr_41 = OpTypeRuntimeArray %41 + %int_1 = OpConstant %int 1 +%StorageBufferA = OpTypeStruct %v4float + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %55 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 +%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer + %58 = OpTypeBufferEXT StorageBuffer + %59 = OpConstantSizeOfEXT %int %58 +%_runtimearr_58 = OpTypeRuntimeArray %58 + %v3float = OpTypeVector %float 3 +%StorageBufferB = OpTypeStruct %v3float + %uint_0 = OpConstant %uint 0 + %69 = OpConstantSizeOfEXT %int %58 +%_runtimearr_58_0 = OpTypeRuntimeArray %58 +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27 + %19 = OpLoad %14 %16 + %23 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_22 %sampler_heap %int_0 + %27 = OpLoad %22 %23 + %29 = OpSampledImage %28 %19 %27 + %33 = OpLoad %v2float %uvs + %34 = OpImageSampleImplicitLod %v4float %29 %33 + OpStore %fragColor %34 + %37 = OpLoad %uint %index + %38 = OpCopyObject %uint %37 + %40 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_41 %resource_heap %38 + %44 = OpBufferPointerEXT %_ptr_Uniform %40 + %45 = OpUntypedAccessChainKHR %_ptr_Uniform %UniformBuffer %44 %int_0 + %46 = OpLoad %v4float %45 + %47 = OpLoad %v4float %fragColor + %48 = OpFAdd %v4float %47 %46 + OpStore %fragColor %48 + %57 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_58 %resource_heap %int_1 + %61 = OpBufferPointerEXT %_ptr_StorageBuffer %57 + %62 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %StorageBufferA %61 %int_0 + OpStore %62 %55 + %63 = OpLoad %uint %index + %64 = OpCopyObject %uint %63 + %68 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_58_0 %resource_heap %64 + %71 = OpBufferPointerEXT %_ptr_StorageBuffer %68 + %72 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %StorageBufferB %71 %int_0 %uint_0 + %73 = OpLoad %float %72 + %75 = OpAccessChain %_ptr_Output_float %fragColor %uint_0 + %76 = OpLoad %float %75 + %77 = OpFAdd %float %76 %73 + %78 = OpAccessChain %_ptr_Output_float %fragColor %uint_0 + OpStore %78 %77 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetId64BitIndexingGood) { + const std::string str = R"( + OpCapability Shader + OpCapability Shader64BitIndexingEXT + OpCapability DescriptorHeapEXT + OpCapability Int64 + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_EXT_shader_64bit_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %3 Binding 0 + OpDecorate %3 DescriptorSet 0 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 0 + %8 = OpTypeInt 64 0 + %4 = OpTypeStruct %7 %7 %7 %7 + %9 = OpTypePointer StorageBuffer %4 + %3 = OpVariable %9 StorageBuffer + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpConstant %10 1 + %13 = OpConstant %10 2 + %14 = OpConstant %10 3 + %15 = OpTypePointer StorageBuffer %7 + %16 = OpTypeBufferEXT StorageBuffer + %17 = OpTypeBufferEXT Uniform + %18 = OpTypeImage %7 2D 0 0 0 1 Unknown + %19 = OpTypeSampler + %20 = OpConstantSizeOfEXT %8 %16 + %21 = OpConstantSizeOfEXT %8 %17 + %22 = OpConstantSizeOfEXT %8 %18 + %23 = OpConstantSizeOfEXT %8 %19 + %2 = OpFunction %5 None %6 + %24 = OpLabel + %25 = OpAccessChain %15 %3 %11 + %26 = OpAccessChain %15 %3 %12 + %27 = OpAccessChain %15 %3 %13 + %28 = OpAccessChain %15 %3 %14 + %29 = OpUConvert %7 %20 + %30 = OpUConvert %7 %21 + %31 = OpUConvert %7 %22 + %32 = OpUConvert %7 %23 + OpStore %25 %29 + OpStore %26 %30 + OpStore %27 %31 + OpStore %28 %32 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, AtomicImageFuncs) { + const std::string str = R"( + OpCapability Shader + OpCapability Image1D + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %resource_heap "resource_heap" + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_10 ArrayStrideIdEXT %13 + %void = OpTypeVoid + %3 = OpTypeFunction %void +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %10 = OpTypeImage %int 1D 0 0 0 2 R32i + %13 = OpConstantSizeOfEXT %int %10 +%_runtimearr_10 = OpTypeRuntimeArray %10 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Image_int = OpTypePointer Image %int + %_ptr_Image = OpTypeUntypedPointerKHR Image + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_10 %resource_heap %int_1 + %19 = OpUntypedImageTexelPointerEXT %_ptr_Image %10 %12 %int_1 %uint_0 + %21 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ValidMemberDecorateIdExt) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability BindlessTextureNV + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" %resource_heap %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "uv" + OpMemberName %U 1 "k" + OpMemberName %U 2 "g" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorateIdEXT %U 0 OffsetIdEXT %specA + OpMemberDecorateIdEXT %U 1 OffsetIdEXT %specB + OpMemberDecorateIdEXT %U 2 OffsetIdEXT %specC + OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float + %fragColor = OpVariable %_ptr_Output_v2float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %samplerTy = OpTypeSampler +%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy + %specA = OpSpecConstant %int 0 + %specB = OpSpecConstant %int 4 + %specC = OpSpecConstant %int 8 + %U = OpTypeStruct %samplerTy %samplerTy %_runtimearr_sampler + %int_0 = OpConstant %int 0 +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %20 = OpTypeBufferEXT Uniform + %uint_2 = OpConstant %uint 2 +%_runtimearr_20 = OpTypeRuntimeArray %20 + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_20 %resource_heap %int_3 + %23 = OpBufferPointerEXT %_ptr_Uniform %19 + %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ValidMemberDecorateIdExt2) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability BindlessTextureNV + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" %resource_heap %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "uv" + OpMemberName %U 1 "k" + OpMemberName %U 2 "g" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorateIdEXT %U 0 OffsetIdEXT %specA + OpMemberDecorateIdEXT %U 1 OffsetIdEXT %specB + OpMemberDecorateIdEXT %U 2 OffsetIdEXT %specC + OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float + %fragColor = OpVariable %_ptr_Output_v2float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %samplerTy = OpTypeSampler +%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy + %specA = OpSpecConstant %int 0 + %specB = OpSpecConstant %int 4 + %specC = OpSpecConstant %int 8 + %U = OpTypeStruct %v2float %int %_runtimearr_sampler + %int_0 = OpConstant %int 0 +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %20 = OpTypeBufferEXT Uniform + %uint_2 = OpConstant %uint 2 +%_runtimearr_20 = OpTypeRuntimeArray %20 + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_20 %resource_heap %int_3 + %23 = OpBufferPointerEXT %_ptr_Uniform %19 + %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0 + %25 = OpLoad %v2float %24 + OpStore %fragColor %25 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +// From VVL CTS +TEST_F(ValidateSpvEXTDescriptorHeap, Alignment) { + const std::string str = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 %4 %5 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpDecorate %3 BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_12 ArrayStrideIdEXT %7 + OpDecorate %4 Binding 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %_struct_8 Block + OpMemberDecorate %_struct_8 0 Offset 0 + OpDecorate %5 Binding 1 + OpDecorate %5 DescriptorSet 0 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %12 = OpTypeImage %uint Buffer 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant + %15 = OpTypeFunction %uint %_ptr_UniformConstant + %uint_0 = OpConstant %uint 0 + %v4uint = OpTypeVector %uint 4 + %v2uint = OpTypeVector %uint 2 + %3 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %7 = OpConstantSizeOfEXT %uint %12 +%_runtimearr_12 = OpTypeRuntimeArray %12 +%_ptr_Function_uint = OpTypePointer Function %uint + %4 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %uint_1 = OpConstant %uint 1 + %_struct_8 = OpTypeStruct %v2uint +%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8 + %5 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer +%_ptr_StorageBuffer_v2uint = OpTypePointer StorageBuffer %v2uint + %v3uint = OpTypeVector %uint 3 + %24 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %25 = OpFunction %uint None %15 + %26 = OpFunctionParameter %_ptr_UniformConstant + %27 = OpLabel + %28 = OpLoad %12 %26 + %29 = OpImageFetch %v4uint %28 %uint_0 ZeroExtend + %30 = OpCompositeExtract %uint %29 0 + OpReturnValue %30 + OpFunctionEnd + %2 = OpFunction %void None %10 + %31 = OpLabel + %32 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_12 %3 %uint_0 + %33 = OpUntypedAccessChainKHR %_ptr_UniformConstant %12 %4 + %34 = OpFunctionCall %uint %25 %32 + %35 = OpFunctionCall %uint %25 %33 + %36 = OpCompositeConstruct %v2uint %34 %35 + %37 = OpAccessChain %_ptr_StorageBuffer_v2uint %5 %uint_0 + OpStore %37 %36 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, HeapBaseVarStorageClassResource) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform +%resource_heap = OpUntypedVariableKHR %_ptr_Uniform Uniform + %int = OpTypeInt 32 1 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT Uniform + %18 = OpConstantSizeOfEXT %int %17 +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_Uniform %16 + %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11167")); + EXPECT_THAT( + diag, + HasSubstr("Storage class is Uniform, but Vulkan requires that Data Type " + "be specified when not using UniformConstant storage class")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, HeapBaseVarStorageClassSampler) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %sampler_heap "sampler_heap" + OpName %uvs "uvs" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17 + OpDecorate %sampler_heap BuiltIn SamplerHeapEXT + OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2 + OpDecorate %uvs Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %fragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer + %int = OpTypeInt 32 1 + %int_27 = OpConstant %int 27 + %14 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant + %17 = OpConstantSizeOfEXT %int %14 +%_runtimearr_14 = OpTypeRuntimeArray %14 +%sampler_heap = OpUntypedVariableKHR %_ptr_StorageBuffer StorageBuffer +%resource_heap = OpUntypedVariableKHR %_ptr_StorageBuffer StorageBuffer + %int_0 = OpConstant %int 0 + %22 = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_runtimearr_22 = OpTypeRuntimeArray %22 + %28 = OpTypeSampledImage %14 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %uvs = OpVariable %_ptr_Input_v2float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %_runtimearr_14 %resource_heap %int_27 + %19 = OpLoad %14 %16 + %23 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %_runtimearr_22 %sampler_heap %int_0 + %27 = OpLoad %22 %23 + %29 = OpSampledImage %28 %19 %27 + %33 = OpLoad %v2float %uvs + %34 = OpImageSampleImplicitLod %v4float %29 %33 + OpStore %fragColor %34 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11167")); + EXPECT_THAT( + diag, + HasSubstr( + "Storage class is StorageBuffer, but Vulkan requires that Data Type " + "be specified when not using UniformConstant storage class")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ResourceHeapWorkgroup) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %15 = OpTypeBufferEXT StorageBuffer +%_ptr_workgroup = OpTypeUntypedPointerKHR Workgroup +%resource_heap = OpUntypedVariableKHR %_ptr_workgroup Workgroup %15 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-ResourceHeapEXT-ResourceHeapEXT-11241")); + EXPECT_THAT(diag, + HasSubstr("The variable decorated with ResourceHeapEXT must be " + "declared using the UniformConstant storage class")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, SamplerHeapWorkgroup) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %resource_heap BuiltIn SamplerHeapEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %15 = OpTypeBufferEXT StorageBuffer +%_ptr_workgroup = OpTypeUntypedPointerKHR Workgroup +%resource_heap = OpUntypedVariableKHR %_ptr_workgroup Workgroup %15 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-SamplerHeapEXT-SamplerHeapEXT-11239")); + EXPECT_THAT(diag, + HasSubstr("The variable decorated with SamplerHeapEXT must be " + "declared using the UniformConstant storage class")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, LoadDescHeapDerivedSampler) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %sampler_heap "sampler_heap" + OpName %uvs "uvs" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17 + OpDecorate %sampler_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2 + OpDecorate %uvs Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %fragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_27 = OpConstant %int 27 + %14 = OpTypeImage %float 2D 0 0 0 1 Unknown + %17 = OpConstantSizeOfEXT %int %14 +%_runtimearr_14 = OpTypeRuntimeArray %14 +%sampler_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int_0 = OpConstant %int 0 + %22 = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_runtimearr_22 = OpTypeRuntimeArray %22 + %28 = OpTypeSampledImage %14 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %uvs = OpVariable %_ptr_Input_v2float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27 + %19 = OpLoad %14 %16 + %23 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_22 %sampler_heap %int_0 + %27 = OpLoad %22 %23 + %29 = OpSampledImage %28 %19 %27 + %33 = OpLoad %v2float %uvs + %34 = OpImageSampleImplicitLod %v4float %29 %33 + OpStore %fragColor %34 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11336")); + EXPECT_THAT( + diag, + HasSubstr( + "pointer instruction has no descriptor set or binding " + "and is not derived from a variable decorated with SamplerHeapEXT")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, LoadDescHeapDerivedImage) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %sampler_heap "sampler_heap" + OpName %uvs "uvs" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn SamplerHeapEXT + OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17 + OpDecorate %sampler_heap BuiltIn SamplerHeapEXT + OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2 + OpDecorate %uvs Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %fragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_27 = OpConstant %int 27 + %14 = OpTypeImage %float 2D 0 0 0 1 Unknown + %17 = OpConstantSizeOfEXT %int %14 +%_runtimearr_14 = OpTypeRuntimeArray %14 +%sampler_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int_0 = OpConstant %int 0 + %22 = OpTypeSampler + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_runtimearr_22 = OpTypeRuntimeArray %22 + %28 = OpTypeSampledImage %14 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %uvs = OpVariable %_ptr_Input_v2float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27 + %19 = OpLoad %14 %16 + %23 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_22 %sampler_heap %int_0 + %27 = OpLoad %22 %23 + %29 = OpSampledImage %28 %19 %27 + %33 = OpLoad %v2float %uvs + %34 = OpImageSampleImplicitLod %v4float %29 %33 + OpStore %fragColor %34 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11337")); + EXPECT_THAT( + diag, + HasSubstr( + "pointer instruction has no descriptor set or binding " + "and is not derived from a variable decorated with ResourceHeapEXT")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, LoadDescHeapDerivedAccStruct) { + const std::string str = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap + OpExecutionMode %main LocalSize 1 1 1 + OpName %resource_heap "resource_heap" + OpDecorate %resource_heap BuiltIn SamplerHeapEXT + OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17 + %_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant + %resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_27 = OpConstant %int 27 + %void = OpTypeVoid + %as = OpTypeAccelerationStructureKHR + %ptr_as = OpTypePointer Function %as + %void_fn = OpTypeFunction %void + %17 = OpConstantSizeOfEXT %int %as + %_runtimearr_14 = OpTypeRuntimeArray %as + %main = OpFunction %void None %void_fn + %entry = OpLabel + %var = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27 + %value = OpLoad %as %var + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11339")); + EXPECT_THAT( + diag, + HasSubstr( + "pointer instruction has no descriptor set or binding " + "and is not derived from a variable decorated with ResourceHeapEXT")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ConstantSizeOfScalarIntType) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT Uniform + %18 = OpConstantSizeOfEXT %v2int %17 +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_Uniform %16 + %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr( + "ArrayStrideIdEXT extra operand must be a 32-bit int scalar type")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ConstantSizeOfDescriptorTypeTarget) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant + %int = OpTypeInt 32 1 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT Uniform + %18 = OpConstantSizeOfEXT %int %U +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_Uniform %16 + %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, + HasSubstr("For OpConstantSizeOfEXT instruction, its Type operand " + "<Id> '5[%U]' must be a Descriptor type")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetId64BitIndexingBad) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %3 Binding 0 + OpDecorate %3 DescriptorSet 0 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 0 + %8 = OpTypeInt 64 0 + %4 = OpTypeStruct %7 %7 %7 %7 + %9 = OpTypePointer StorageBuffer %4 + %3 = OpVariable %9 StorageBuffer + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpConstant %10 1 + %13 = OpConstant %10 2 + %14 = OpConstant %10 3 + %15 = OpTypePointer StorageBuffer %7 + %16 = OpTypeBufferEXT StorageBuffer + %17 = OpTypeBufferEXT Uniform + %18 = OpTypeImage %7 2D 0 0 0 1 Unknown + %19 = OpTypeSampler + %20 = OpConstantSizeOfEXT %8 %16 + %21 = OpConstantSizeOfEXT %8 %17 + %22 = OpConstantSizeOfEXT %8 %18 + %23 = OpConstantSizeOfEXT %8 %19 + %2 = OpFunction %5 None %6 + %24 = OpLabel + %25 = OpAccessChain %15 %3 %11 + %26 = OpAccessChain %15 %3 %12 + %27 = OpAccessChain %15 %3 %13 + %28 = OpAccessChain %15 %3 %14 + %29 = OpUConvert %7 %20 + %30 = OpUConvert %7 %21 + %31 = OpUConvert %7 %22 + %32 = OpUConvert %7 %23 + OpStore %25 %29 + OpStore %26 %30 + OpStore %27 %31 + OpStore %28 %32 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr("Using a 64-bit integer type requires the Int64 capability.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, TypeBufferEXTStorageClass) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant + %int = OpTypeInt 32 1 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT UniformConstant + %18 = OpConstantSizeOfEXT %int %17 +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_Uniform %16 + %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("TypeBufferEXT StorageClass could only be " + "StorageBuffer or Uniform.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTStorageClass) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT Uniform + %18 = OpConstantSizeOfEXT %int %17 +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_UniformConstant %16 + %21 = OpUntypedAccessChainKHR %_ptr_UniformConstant %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr("OpBufferPointerEXT's Result Type must be a pointer " + "type with a Storage Class of Uniform or StorageBuffer.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTLayout) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + OpDecorate %_ptr_UniformConstant Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT Uniform + %18 = OpConstantSizeOfEXT %int %17 +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_UniformConstant %16 + %21 = OpUntypedAccessChainKHR %_ptr_UniformConstant %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11346")); + EXPECT_THAT( + diag, HasSubstr("The result type operand of OpBufferPointerEXT " + "must have a Type operand that is explicitly laid out")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTDecorate) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %o "o" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "inputData" + OpDecorate %o Location 0 + OpDecorate %resource_heap BuiltIn SamplerHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %o = OpVariable %_ptr_Output_uint Output +%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant + %int = OpTypeInt 32 1 + %int_9 = OpConstant %int 9 + %U = OpTypeStruct %uint + %int_0 = OpConstant %int 0 + %17 = OpTypeBufferEXT Uniform + %18 = OpConstantSizeOfEXT %int %17 +%_runtimearr_17 = OpTypeRuntimeArray %17 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9 + %20 = OpBufferPointerEXT %_ptr_Uniform %16 + %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0 + %22 = OpLoad %uint %21 + OpStore %o %22 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("OpBufferPointerEXT's buffer must be an untyped " + "pointer into a variable declared with the " + "ResourceHeapEXT built-in")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTNonWritable) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "uv" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_18 ArrayStrideIdEXT %uint_2 + OpDecorate %22 NonWritable + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float + %fragColor = OpVariable %_ptr_Output_v2float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %U = OpTypeStruct %v2float + %int_0 = OpConstant %int 0 +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %18 = OpTypeBufferEXT Uniform + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_runtimearr_18 = OpTypeRuntimeArray %18 + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_18 %resource_heap %int_3 + %22 = OpBufferPointerEXT %_ptr_Uniform %17 + %23 = OpUntypedAccessChainKHR %_ptr_Uniform %U %22 %int_0 + %24 = OpLoad %v2float %23 + OpStore %fragColor %24 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("Target of NonWritable decoration is invalid")); + EXPECT_THAT( + diag, + HasSubstr( + "cannot be used to OpBufferPointerEXT with Uniform storage class")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTNonWritableWithSSBO) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %resource_heap %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "uv" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpDecorateId %_runtimearr_18 ArrayStrideIdEXT %uint_2 + OpDecorate %22 NonWritable + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float + %fragColor = OpVariable %_ptr_Output_v2float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %U = OpTypeStruct %v2float + %int_0 = OpConstant %int 0 +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform +%_ptr_Buffer = OpTypeUntypedPointerKHR StorageBuffer + %18 = OpTypeBufferEXT Uniform + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_runtimearr_18 = OpTypeRuntimeArray %18 + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_18 %resource_heap %int_3 + %22 = OpBufferPointerEXT %_ptr_Buffer %17 + %23 = OpUntypedAccessChainKHR %_ptr_Buffer %U %22 %int_0 + %24 = OpLoad %v2float %23 + OpStore %fragColor %24 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdExtStruct) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 %4 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %5 0 Offset 0 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 0 + OpDecorate %5 Block + OpDecorate %6 Block + OpDecorate %7 SpecId 0 + OpMemberDecorateIdEXT %11 0 OffsetIdEXT %7 + OpMemberDecorate %6 1 Offset 4 + OpMemberDecorateIdEXT %6 2 OffsetIdEXT %8 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypeInt 32 0 + %5 = OpTypeStruct %11 + %12 = OpTypePointer StorageBuffer %5 + %13 = OpTypeImage %11 2D 0 0 0 1 Unknown + %14 = OpTypeBufferEXT Uniform + %15 = OpConstantSizeOfEXT %11 %13 + %16 = OpConstantSizeOfEXT %11 %14 + %17 = OpConstant %11 1 + %18 = OpConstant %11 8 + %19 = OpSpecConstantOp %11 IAdd %18 %15 + %20 = OpSpecConstantOp %11 ISub %19 %17 + %21 = OpSpecConstantOp %11 UDiv %20 %15 + %22 = OpSpecConstantOp %11 IMul %21 %15 + %23 = OpSpecConstantOp %11 IAdd %22 %15 + %24 = OpSpecConstantOp %11 IAdd %23 %16 + %25 = OpSpecConstantOp %11 ISub %24 %17 + %26 = OpSpecConstantOp %11 UDiv %25 %16 + %27 = OpSpecConstantOp %11 IMul %26 %16 + %8 = OpSpecConstantOp %11 IAdd %27 %16 + %7 = OpSpecConstant %11 4 + %3 = OpVariable %12 StorageBuffer + %28 = OpConstant %11 0 + %6 = OpTypeStruct %11 %11 %11 + %29 = OpTypePointer Uniform %6 + %4 = OpVariable %29 Uniform + %30 = OpTypePointer StorageBuffer %11 + %31 = OpConstant %11 2 + %2 = OpFunction %9 None %10 + %32 = OpLabel + %33 = OpAccessChain %30 %4 %28 + %34 = OpLoad %11 %33 + %35 = OpAccessChain %30 %4 %31 + %36 = OpLoad %11 %35 + %37 = OpIAdd %11 %34 %36 + %38 = OpAccessChain %30 %3 %28 + OpStore %38 %37 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("MemberDecorateIdEXT Structure type")); + EXPECT_THAT(diag, HasSubstr("is not a struct type.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, DecorateIdArrayStrideIdEXT) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 %4 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %5 0 Offset 0 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 0 + OpDecorate %5 Block + OpDecorate %6 Block + OpDecorate %7 SpecId 0 + OpDecorateId %5 ArrayStrideIdEXT %7 + OpMemberDecorate %6 1 Offset 4 + OpMemberDecorateIdEXT %6 2 OffsetIdEXT %8 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypeInt 32 0 + %5 = OpTypeStruct %11 + %12 = OpTypePointer StorageBuffer %5 + %13 = OpTypeImage %11 2D 0 0 0 1 Unknown + %14 = OpTypeBufferEXT Uniform + %15 = OpConstantSizeOfEXT %11 %13 + %16 = OpConstantSizeOfEXT %11 %14 + %17 = OpConstant %11 1 + %18 = OpConstant %11 8 + %19 = OpSpecConstantOp %11 IAdd %18 %15 + %20 = OpSpecConstantOp %11 ISub %19 %17 + %21 = OpSpecConstantOp %11 UDiv %20 %15 + %22 = OpSpecConstantOp %11 IMul %21 %15 + %23 = OpSpecConstantOp %11 IAdd %22 %15 + %24 = OpSpecConstantOp %11 IAdd %23 %16 + %25 = OpSpecConstantOp %11 ISub %24 %17 + %26 = OpSpecConstantOp %11 UDiv %25 %16 + %27 = OpSpecConstantOp %11 IMul %26 %16 + %8 = OpSpecConstantOp %11 IAdd %27 %16 + %7 = OpSpecConstant %11 4 + %3 = OpVariable %12 StorageBuffer + %28 = OpConstant %11 0 + %6 = OpTypeStruct %11 %11 %11 + %29 = OpTypePointer Uniform %6 + %4 = OpVariable %29 Uniform + %30 = OpTypePointer StorageBuffer %11 + %31 = OpConstant %11 2 + %2 = OpFunction %9 None %10 + %32 = OpLabel + %33 = OpAccessChain %30 %4 %28 + %34 = OpLoad %11 %33 + %35 = OpAccessChain %30 %4 %31 + %36 = OpLoad %11 %35 + %37 = OpIAdd %11 %34 %36 + %38 = OpAccessChain %30 %3 %28 + OpStore %38 %37 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr( + "ArrayStrideIdEXT decoration must only be applied to array types.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideNonDescriptor) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %ssbo Block + OpMemberDecorate %ssbo 0 BuiltIn ResourceHeapEXT + OpMemberDecorate %ssbo 0 Offset 0 + OpDecorateId %_runtimearr_13 ArrayStrideIdEXT %16 + OpDecorateId %_runtimearr_24 ArrayStrideIdEXT %25 + %void = OpTypeVoid + %3 = OpTypeFunction %void +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_16 = OpConstant %int 16 + %v4int = OpTypeVector %int 4 + %ssbo = OpTypeStruct %v4int + %int_0 = OpConstant %int 0 + %13 = OpTypeImage %int 2D 0 0 0 2 Rgba8i +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %16 = OpConstantSizeOfEXT %int %13 +%_runtimearr_13 = OpTypeRuntimeArray %13 + %24 = OpTypeBufferEXT StorageBuffer + %25 = OpConstantSizeOfEXT %int %24 +%_runtimearr_24 = OpTypeRuntimeArray %int + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, + HasSubstr("ArrayStrideIdEXT decoration must only be applied to " + "array type containing a Descriptor type.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdArrayStrideIdEXT) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 %4 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %5 0 Offset 0 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 0 + OpDecorate %5 Block + OpDecorate %6 Block + OpDecorate %7 SpecId 0 + OpMemberDecorateIdEXT %6 0 ArrayStrideIdEXT %7 + OpMemberDecorate %6 1 Offset 4 + OpMemberDecorateIdEXT %6 2 OffsetIdEXT %8 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypeInt 32 0 + %5 = OpTypeStruct %11 + %12 = OpTypePointer StorageBuffer %5 + %13 = OpTypeImage %11 2D 0 0 0 1 Unknown + %14 = OpTypeBufferEXT Uniform + %15 = OpConstantSizeOfEXT %11 %13 + %16 = OpConstantSizeOfEXT %11 %14 + %17 = OpConstant %11 1 + %18 = OpConstant %11 8 + %19 = OpSpecConstantOp %11 IAdd %18 %15 + %20 = OpSpecConstantOp %11 ISub %19 %17 + %21 = OpSpecConstantOp %11 UDiv %20 %15 + %22 = OpSpecConstantOp %11 IMul %21 %15 + %23 = OpSpecConstantOp %11 IAdd %22 %15 + %24 = OpSpecConstantOp %11 IAdd %23 %16 + %25 = OpSpecConstantOp %11 ISub %24 %17 + %26 = OpSpecConstantOp %11 UDiv %25 %16 + %27 = OpSpecConstantOp %11 IMul %26 %16 + %8 = OpSpecConstantOp %11 IAdd %27 %16 + %7 = OpSpecConstant %11 4 + %3 = OpVariable %12 StorageBuffer + %28 = OpConstant %11 0 + %6 = OpTypeStruct %11 %11 %11 + %29 = OpTypePointer Uniform %6 + %4 = OpVariable %29 Uniform + %30 = OpTypePointer StorageBuffer %11 + %31 = OpConstant %11 2 + %2 = OpFunction %9 None %10 + %32 = OpLabel + %33 = OpAccessChain %30 %4 %28 + %34 = OpLoad %11 %33 + %35 = OpAccessChain %30 %4 %31 + %36 = OpLoad %11 %35 + %37 = OpIAdd %11 %34 %36 + %38 = OpAccessChain %30 %3 %28 + OpStore %38 %37 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("ArrayStrideIdEXT could only be directly applied " + "to array type using OpDecorateId.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdOffsetIdEXT) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 %4 + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %5 0 Offset 0 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 0 + OpDecorate %5 Block + OpDecorate %6 Block + OpDecorate %7 SpecId 0 + OpMemberDecorateIdEXT %6 0 OffsetIdEXT %7 + OpMemberDecorate %6 1 Offset 4 + OpMemberDecorateIdEXT %6 2 ArrayStrideIdEXT %8 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypeInt 32 0 + %5 = OpTypeStruct %11 + %12 = OpTypePointer StorageBuffer %5 + %13 = OpTypeImage %11 2D 0 0 0 1 Unknown + %14 = OpTypeBufferEXT Uniform + %15 = OpConstantSizeOfEXT %11 %13 + %16 = OpConstantSizeOfEXT %11 %14 + %17 = OpConstant %11 1 + %18 = OpConstant %11 8 + %19 = OpSpecConstantOp %11 IAdd %18 %15 + %20 = OpSpecConstantOp %11 ISub %19 %17 + %21 = OpSpecConstantOp %11 UDiv %20 %15 + %22 = OpSpecConstantOp %11 IMul %21 %15 + %23 = OpSpecConstantOp %11 IAdd %22 %15 + %24 = OpSpecConstantOp %11 IAdd %23 %16 + %25 = OpSpecConstantOp %11 ISub %24 %17 + %26 = OpSpecConstantOp %11 UDiv %25 %16 + %27 = OpSpecConstantOp %11 IMul %26 %16 + %8 = OpSpecConstantOp %11 IAdd %27 %16 + %7 = OpSpecConstant %11 4 + %3 = OpVariable %12 StorageBuffer + %28 = OpConstant %11 0 + %6 = OpTypeStruct %11 %11 %11 + %29 = OpTypePointer Uniform %6 + %4 = OpVariable %29 Uniform + %30 = OpTypePointer StorageBuffer %11 + %31 = OpConstant %11 2 + %2 = OpFunction %9 None %10 + %32 = OpLabel + %33 = OpAccessChain %30 %4 %28 + %34 = OpLoad %11 %33 + %35 = OpAccessChain %30 %4 %31 + %36 = OpLoad %11 %35 + %37 = OpIAdd %11 %34 %36 + %38 = OpAccessChain %30 %3 %28 + OpStore %38 %37 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr( + "OffsetIdEXT decoration in MemberDecorateIdEXT must only be applied " + "to members of structs where the struct contains descriptor types.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdAllSingleImages) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability ImageBuffer + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" %2 + OpExecutionMode %1 LocalSize 1 1 1 + OpDecorate %2 BuiltIn ResourceHeapEXT + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorateIdEXT %_struct_3 1 OffsetIdEXT %uint_0 + OpMemberDecorate %_struct_3 2 Offset 16 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %uint_51966 = OpConstant %uint 51966 + %uint_0_0 = OpConstant %uint 0 +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant + %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui + %_struct_3 = OpTypeStruct %14 %14 %14 + %1 = OpFunction %void None %7 + %15 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_struct_3 %2 %uint_2 + %17 = OpLoad %14 %16 + OpImageWrite %17 %uint_0 %uint_51966 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdAllImages) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability ImageBuffer + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" %2 + OpExecutionMode %1 LocalSize 1 1 1 + OpDecorate %2 BuiltIn ResourceHeapEXT + OpDecorate %4 SpecId 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorateIdEXT %_struct_3 1 OffsetIdEXT %4 + OpMemberDecorateIdEXT %_struct_3 2 OffsetIdEXT %5 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %uint_51966 = OpConstant %uint 51966 + %uint_0_0 = OpConstant %uint 0 + %4 = OpSpecConstant %uint 0 + %5 = OpSpecConstantOp %uint IMul %4 %uint_2 +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant + %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui + %_struct_3 = OpTypeStruct %14 %14 %14 + %1 = OpFunction %void None %7 + %15 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_struct_3 %2 %uint_2 + %17 = OpLoad %14 %16 + OpImageWrite %17 %uint_0 %uint_51966 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdStructOfStructOfImage) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability ImageBuffer + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" %2 + OpExecutionMode %1 LocalSize 1 1 1 + OpDecorate %2 BuiltIn ResourceHeapEXT + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0 + OpMemberDecorate %image_struct 0 Offset 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_51966 = OpConstant %uint 51966 + %uint_0_0 = OpConstant %uint 0 +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant + %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui + %image_struct = OpTypeStruct %14 + %struct = OpTypeStruct %uint %image_struct + %1 = OpFunction %void None %7 + %15 = OpLabel + %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %struct %2 %uint_1 %uint_0 + %17 = OpLoad %14 %16 + OpImageWrite %17 %uint_0 %uint_51966 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdDecorator) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability BindlessTextureNV + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" %resource_heap %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "uv" + OpMemberName %U 1 "k" + OpMemberName %U 2 "g" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpMemberDecorate %U 1 Offset 8 + OpMemberDecorateIdEXT %U 2 Offset 0 + OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float + %fragColor = OpVariable %_ptr_Output_v2float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %specC = OpSpecConstant %int 4 + %samplerTy = OpTypeSampler +%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy + %U = OpTypeStruct %v2float %int %_runtimearr_sampler + %int_0 = OpConstant %int 0 +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %20 = OpTypeBufferEXT Uniform + %uint_2 = OpConstant %uint 2 +%_runtimearr_20 = OpTypeRuntimeArray %20 + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_20 %resource_heap %int_3 + %23 = OpBufferPointerEXT %_ptr_Uniform %19 + %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0 + %25 = OpLoad %v2float %24 + OpStore %fragColor %25 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("Decoration operand could only be OffsetIdEXT.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdExtraIdOrder) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpCapability BindlessTextureNV + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpExtension "SPV_NV_bindless_texture" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSamplerImageAddressingModeNV 64 + OpEntryPoint Fragment %main "main" %resource_heap %fragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %fragColor "fragColor" + OpName %resource_heap "resource_heap" + OpName %U "U" + OpMemberName %U 0 "uv" + OpMemberName %U 1 "k" + OpMemberName %U 2 "g" + OpDecorate %fragColor Location 0 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %U Block + OpMemberDecorate %U 0 Offset 0 + OpMemberDecorate %U 1 Offset 8 + OpMemberDecorateIdEXT %U 2 OffsetIdEXT %specC + OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float + %fragColor = OpVariable %_ptr_Output_v2float Output +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %samplerTy = OpTypeSampler +%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy + %U = OpTypeStruct %v2float %int %_runtimearr_sampler + %specC = OpSpecConstant %int 4 + %int_0 = OpConstant %int 0 +%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform + %20 = OpTypeBufferEXT Uniform + %uint_2 = OpConstant %uint 2 +%_runtimearr_20 = OpTypeRuntimeArray %20 + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_20 %resource_heap %int_3 + %23 = OpBufferPointerEXT %_ptr_Uniform %19 + %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0 + %25 = OpLoad %v2float %24 + OpStore %fragColor %25 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr("All <id> Extra Operands must appear before Structure Type.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OpMemberDecorateIdDuplicateOffset) { + const std::string str = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%struct = OpTypeStruct %14 %uint %uint + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("member '1' decorated with OffsetIdEXT multiple times " + "is not allowed")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetAndOffsetId) { + const std::string str = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%struct = OpTypeStruct %14 %uint %uint + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("member '0' decorated with both OffsetIdEXT and Offset " + "is not allowed")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, AtomicImageType) { + const std::string str = R"( + OpCapability Shader + OpCapability Image1D + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %resource_heap "resource_heap" + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_10 ArrayStrideIdEXT %13 + %void = OpTypeVoid + %3 = OpTypeFunction %void +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %10 = OpTypeImage %int 1D 0 0 0 2 Rgba16i + %13 = OpConstantSizeOfEXT %int %10 +%_runtimearr_10 = OpTypeRuntimeArray %10 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Image_int = OpTypePointer Image %int + %_ptr_Image = OpTypeUntypedPointerKHR Image + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_10 %resource_heap %int_1 + %19 = OpUntypedImageTexelPointerEXT %_ptr_Image %10 %12 %int_1 %uint_0 + %21 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + AnyVUID("VUID-StandaloneSpirv-OpUntypedImageTexelPointerEXT-11416")); + EXPECT_THAT(diag, + HasSubstr("Expected the Image Format in Image to be R64i, R64ui, " + "R32f, R32i, or R32ui for Vulkan environment using " + "OpUntypedImageTexelPointerEXT")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, UntypedPointerStorageClass) { + const std::string str = R"( + OpCapability Shader + OpCapability Image1D + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 460 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %resource_heap "resource_heap" + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorateId %_runtimearr_10 ArrayStrideIdEXT %13 + %void = OpTypeVoid + %3 = OpTypeFunction %void +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %10 = OpTypeImage %int 1D 0 0 0 2 R32i +%_ptr_Uniform = OpTypeUntypedPointerKHR Private + %13 = OpConstantSizeOfEXT %int %10 +%_runtimearr_10 = OpTypeRuntimeArray %10 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Image_int = OpTypePointer Image %int + %_ptr_Image = OpTypeUntypedPointerKHR Image + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_10 %resource_heap %int_1 + %19 = OpUntypedImageTexelPointerEXT %_ptr_Image %10 %12 %int_1 %uint_0 + %21 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, + AnyVUID("VUID-StandaloneSpirv-OpTypeUntypedPointerKHR-11417")); + EXPECT_THAT(diag, HasSubstr("In Vulkan, untyped pointers can only be used " + "in an explicitly laid out storage class")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, InvalidStoreToHeap) { + const std::string str = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap %_ + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_descriptor_heap" + OpName %main "main" + OpName %resource_heap "resource_heap" + OpName %A "A" + OpMemberName %A 0 "a" + OpName %PushConstant "PushConstant" + OpMemberName %PushConstant 0 "b" + OpName %_ "" + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %A Block + OpMemberDecorate %A 0 BuiltIn ResourceHeapEXT + OpMemberDecorate %A 0 Offset 0 + OpDecorate %PushConstant Block + OpMemberDecorate %PushConstant 0 Offset 0 + OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %21 + %void = OpTypeVoid + %3 = OpTypeFunction %void +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %uint +%PushConstant = OpTypeStruct %uint +%_ptr_PushConstant_PushConstant = OpTypePointer PushConstant %PushConstant + %_ = OpVariable %_ptr_PushConstant_PushConstant PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer + %20 = OpTypeBufferEXT StorageBuffer + %21 = OpConstantSizeOfEXT %int %20 +%_runtimearr_20 = OpTypeRuntimeArray %20 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 + %27 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 + %17 = OpLoad %uint %16 + OpStore %resource_heap %17 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("OpStore Pointer <id>")); + EXPECT_THAT(diag, HasSubstr("storage class is read-only")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, NonDescriptorOpaqueType) { + std::string spirv = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" +%float = OpTypeFloat 32 +%image = OpTypeImage %float 2D 0 0 0 1 Unknown +%sampled = OpTypeSampledImage %image +%struct = OpTypeStruct %sampled +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func + %l = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-DescriptorHeapEXT-11482")); + EXPECT_THAT( + diag, + HasSubstr( + "In Vulkan, OpTypeStruct must not contain an invalid opaque type.")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ResourceHeapBuiltinOnMemeber) { + std::string spirv = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %resource_heap %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %resource_heap BuiltIn ResourceHeapEXT + OpDecorate %A Block + OpMemberDecorate %A 0 BuiltIn ResourceHeapEXT + OpMemberDecorate %A 0 Offset 0 + OpDecorate %PushConstant Block + OpMemberDecorate %PushConstant 0 Offset 0 + OpDecorateId %_runtimearr_21 ArrayStrideIdEXT %22 + %void = OpTypeVoid + %3 = OpTypeFunction %void +%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant +%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %uint + %int_0 = OpConstant %int 0 +%PushConstant = OpTypeStruct %uint +%_ptr_PushConstant_PushConstant = OpTypePointer PushConstant %PushConstant + %_ = OpVariable %_ptr_PushConstant_PushConstant PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer + %21 = OpTypeBufferEXT StorageBuffer + %22 = OpConstantSizeOfEXT %int %21 +%_runtimearr_21 = OpTypeRuntimeArray %21 + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 + %18 = OpLoad %uint %17 + %20 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_21 %resource_heap %int_2 + %24 = OpBufferPointerEXT %_ptr_StorageBuffer %20 + %25 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %A %24 %int_0 + OpStore %25 %18 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "BuiltIn ResourceHeapEXT cannot be used as a member decoration")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdFloat) { + const std::string str = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %float_0 + OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%struct = OpTypeStruct %14 %uint %uint + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OffsetIdEXT extra operand must be a 32-bit int scalar type")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdNonConstant) { + const std::string str = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %void + OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0 + OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%struct = OpTypeStruct %14 %uint %uint + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OffsetIdEXT extra operand must be a 32-bit int scalar type")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideFloat) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorateId %array ArrayStrideIdEXT %float_4 +%float = OpTypeFloat 32 +%float_4 = OpConstant %float 4 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%sampler = OpTypeSampler +%array = OpTypeArray %sampler %int_2 +%void = OpTypeVoid + %3 = OpTypeFunction %void +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ArrayStrideIdEXT extra operand must be a 32-bit int scalar type")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideConstantZero) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorateId %array ArrayStrideIdEXT %int_0 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%sampler = OpTypeSampler +%array = OpTypeArray %sampler %int_2 +%void = OpTypeVoid + %3 = OpTypeFunction %void +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ArrayStrideIdEXT contains a stride of zero")); +} + +TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideSpecConstantZero) { + const std::string str = R"( + OpCapability Shader + OpCapability DescriptorHeapEXT + OpExtension "SPV_EXT_descriptor_heap" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %spec SpecId 1 + OpDecorateId %array ArrayStrideIdEXT %spec +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%spec = OpSpecConstant %int 0 +%sampler = OpTypeSampler +%array = OpTypeArray %sampler %int_2 +%void = OpTypeVoid + %3 = OpTypeFunction %void +%main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + + )"; + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4)); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_extension_spv_intel_arbitrary_precision_integers_test.cpp b/test/val/val_extension_spv_intel_arbitrary_precision_integers_test.cpp new file mode 100644 index 0000000..c21bfc8 --- /dev/null +++ b/test/val/val_extension_spv_intel_arbitrary_precision_integers_test.cpp
@@ -0,0 +1,178 @@ +// Copyright (c) 2025 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. + +// Tests for SPV_INTEL_arbitrary_precision_integers extension + +#include <string> + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateIntelArbitraryPrecisionIntegers = spvtest::ValidateBase<bool>; + +TEST_F(ValidateIntelArbitraryPrecisionIntegers, + ArbitraryPrecisionIntegerWithoutExtension) { + const std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int19 = OpTypeInt 19 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid number of bits (19) used for OpTypeInt")); +} + +TEST_F(ValidateIntelArbitraryPrecisionIntegers, + ArbitraryPrecisionIntegerWithExtension) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ArbitraryPrecisionIntegersINTEL + OpExtension "SPV_INTEL_arbitrary_precision_integers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int19 = OpTypeInt 19 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIntelArbitraryPrecisionIntegers, + ArbitraryPrecisionIntegerVariousBitWidths) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ArbitraryPrecisionIntegersINTEL + OpExtension "SPV_INTEL_arbitrary_precision_integers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int1 = OpTypeInt 1 1 + %int3 = OpTypeInt 3 0 + %int7 = OpTypeInt 7 1 + %int13 = OpTypeInt 13 0 + %int19 = OpTypeInt 19 1 + %int33 = OpTypeInt 33 0 + %int65 = OpTypeInt 65 1 + %int128 = OpTypeInt 128 0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIntelArbitraryPrecisionIntegers, + ArbitraryPrecisionIntegerWithCapabilityOnly) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ArbitraryPrecisionIntegersINTEL + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int19 = OpTypeInt 19 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SPV_INTEL_arbitrary_precision_integers")); +} + +TEST_F(ValidateIntelArbitraryPrecisionIntegers, StandardIntegerTypesStillWork) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ArbitraryPrecisionIntegersINTEL + OpExtension "SPV_INTEL_arbitrary_precision_integers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int32 = OpTypeInt 32 1 + %uint32 = OpTypeInt 32 0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIntelArbitraryPrecisionIntegers, ZeroBitIntegerRejected) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ArbitraryPrecisionIntegersINTEL + OpExtension "SPV_INTEL_arbitrary_precision_integers" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int0 = OpTypeInt 0 0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeInt has 0 bits")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_extension_spv_intel_function_variants.cpp b/test/val/val_extension_spv_intel_function_variants.cpp new file mode 100644 index 0000000..adb243f --- /dev/null +++ b/test/val/val_extension_spv_intel_function_variants.cpp
@@ -0,0 +1,123 @@ +// Copyright 2025 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. + +// Tests for SPV_INTEL_inline_assembly + +#include <string> + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateSpvINTELFunctionVariants = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvINTELFunctionVariants, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpCapability SpecConditionalINTEL + OpCapability FunctionVariantsINTEL + OpConditionalCapabilityINTEL %1 Int8 + OpExtension "SPV_INTEL_function_variants" + OpMemoryModel Logical OpenCL + %bool = OpTypeBool + %1 = OpSpecConstantTrue %bool + )"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvINTELFunctionVariants, RequiresExtension1) { + const std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpCapability SpecConditionalINTEL + OpMemoryModel Logical OpenCL + )"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, HasSubstr("1st operand of Capability: operand " + "SpecConditionalINTEL(6245) requires " + "one of these extensions: SPV_INTEL_function_variants")); + EXPECT_THAT(diag, HasSubstr("OpCapability SpecConditionalINTEL")); +} + +TEST_F(ValidateSpvINTELFunctionVariants, RequiresExtension2) { + const std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpCapability FunctionVariantsINTEL + OpMemoryModel Logical OpenCL + )"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, HasSubstr("1st operand of Capability: operand " + "FunctionVariantsINTEL(6246) requires " + "one of these extensions: SPV_INTEL_function_variants")); + EXPECT_THAT(diag, HasSubstr("OpCapability FunctionVariantsINTEL")); +} + +TEST_F(ValidateSpvINTELFunctionVariants, RequiresCapability1) { + const std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpConditionalCapabilityINTEL %1 Int8 + OpExtension "SPV_INTEL_function_variants" + OpMemoryModel Logical OpenCL + %bool = OpTypeBool + %1 = OpSpecConstantTrue %bool +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, HasSubstr("Opcode ConditionalCapabilityINTEL requires one of these " + "capabilities: SpecConditionalINTEL")); + EXPECT_THAT(diag, HasSubstr("OpConditionalCapabilityINTEL %1 Int8")); +} + +TEST_F(ValidateSpvINTELFunctionVariants, RequiresCapability2) { + const std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpCapability SpecConditionalINTEL + OpConditionalCapabilityINTEL %1 Int8 + OpExtension "SPV_INTEL_function_variants" + OpMemoryModel Logical OpenCL + %bool = OpTypeBool + %1 = OpSpecConstantArchitectureINTEL %bool 0 0 170 0 +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr("Opcode SpecConstantArchitectureINTEL requires one of these " + "capabilities: FunctionVariantsINTEL")); + EXPECT_THAT( + diag, HasSubstr("%1 = OpSpecConstantArchitectureINTEL %bool 0 0 170 0")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_extension_spv_intel_inline_assembly.cpp b/test/val/val_extension_spv_intel_inline_assembly.cpp new file mode 100644 index 0000000..a683afd --- /dev/null +++ b/test/val/val_extension_spv_intel_inline_assembly.cpp
@@ -0,0 +1,109 @@ +// Copyright 2025 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. + +// Tests for SPV_INTEL_inline_assembly + +#include <string> + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateSpvINTELInlineAssembly = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvINTELInlineAssembly, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability AsmINTEL + OpExtension "SPV_INTEL_inline_assembly" + OpMemoryModel Physical32 OpenCL + OpDecorate %1 SideEffectsINTEL + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpAsmTargetINTEL "spirv64-unknown-unknown" + %1 = OpAsmINTEL %2 %3 %4 "nop" "" + %5 = OpFunction %2 None %3 + %6 = OpLabel + %7 = OpAsmCallINTEL %2 %1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvINTELInlineAssembly, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability AsmINTEL + OpMemoryModel Physical32 OpenCL + OpDecorate %1 SideEffectsINTEL + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpAsmTargetINTEL "spirv64-unknown-unknown" + %1 = OpAsmINTEL %2 %3 %4 "nop" "" + %5 = OpFunction %2 None %3 + %6 = OpLabel + %7 = OpAsmCallINTEL %2 %1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + const std::string diag = getDiagnosticString(); + EXPECT_THAT( + diag, + HasSubstr("1st operand of Capability: operand AsmINTEL(5606) requires " + "one of these extensions: SPV_INTEL_inline_assembly")); + EXPECT_THAT(diag, HasSubstr("OpCapability AsmINTEL")); +} + +TEST_F(ValidateSpvINTELInlineAssembly, RequiresCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_INTEL_inline_assembly" + OpMemoryModel Physical32 OpenCL + OpDecorate %1 SideEffectsINTEL + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpAsmTargetINTEL "spirv64-unknown-unknown" + %1 = OpAsmINTEL %2 %3 %4 "nop" "" + %5 = OpFunction %2 None %3 + %6 = OpLabel + %7 = OpAsmCallINTEL %2 %1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + const std::string diag = getDiagnosticString(); + EXPECT_THAT(diag, HasSubstr("Operand 2 of Decorate requires one of these " + "capabilities: AsmINTEL")); + EXPECT_THAT(diag, HasSubstr("OpDecorate %1 SideEffectsINTEL")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_abort_test.cpp b/test/val/val_extension_spv_khr_abort_test.cpp new file mode 100644 index 0000000..1c4eae5 --- /dev/null +++ b/test/val/val_extension_spv_khr_abort_test.cpp
@@ -0,0 +1,690 @@ +// Copyright (c) 2026 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. + +// Tests for OpExtension validator rules. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateSpvKHRAbort = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvKHRAbort, Valid) { + const std::string str = R"( +OpCapability Shader +OpCapability AbortKHR +OpExtension "SPV_KHR_abort" +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%uint32_t = OpTypeInt 32 0 +%payload = OpConstant %uint32_t 6 +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpAbortKHR %uint32_t %payload +OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRAbort, RequireFinalInstructionInBlock) { + const std::string str = R"( + OpCapability Shader + OpCapability AbortKHR + OpExtension "SPV_KHR_abort" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%uint32_t = OpTypeInt 32 0 +%payload = OpConstant %uint32_t 6 +%main = OpFunction %void None %void_fn +%entry = OpLabel + OpAbortKHR %uint32_t %payload + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Return must appear in a block")); +} + +TEST_F(ValidateSpvKHRAbort, RequiresCapability) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_abort" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%uint32_t = OpTypeInt 32 0 +%payload = OpConstant %uint32_t 6 +%main = OpFunction %void None %void_fn +%entry = OpLabel + OpAbortKHR %uint32_t %payload + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Opcode AbortKHR requires one of these capabilities: AbortKHR")); +} + +TEST_F(ValidateSpvKHRAbort, RequiresExtension) { + const std::string str = R"( + OpCapability Shader + OpCapability AbortKHR + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%uint32_t = OpTypeInt 32 0 +%payload = OpConstant %uint32_t 6 +%main = OpFunction %void None %void_fn +%entry = OpLabel + OpAbortKHR %uint32_t %payload + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("1st operand of Capability: operand AbortKHR(5120) " + "requires one of these extensions: SPV_KHR_abort")); +} + +TEST_F(ValidateSpvKHRAbort, MismatchedOperandTypes) { + const std::string str = R"( + OpCapability Shader + OpCapability AbortKHR + OpExtension "SPV_KHR_abort" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%uint32_t = OpTypeInt 32 0 +%f32_t = OpTypeFloat 32 +%payload = OpConstant %uint32_t 6 +%main = OpFunction %void None %void_fn +%entry = OpLabel + OpAbortKHR %f32_t %payload + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type of Message operand does not logically match " + "the type of the Message Type operand")); +} + +TEST_F(ValidateSpvKHRAbort, ValidCompositeOperandTypes) { + const std::string str = R"( + OpCapability Shader + OpCapability Int8 + OpCapability AbortKHR + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_abort" + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + + OpDecorate %string1_t UTFEncodedKHR + OpDecorate %string2_t UTFEncodedKHR + + OpDecorate %string1_x UTFEncodedKHR + OpDecorate %string1_x ArrayStride 1 + OpDecorate %string2_x UTFEncodedKHR + OpDecorate %string2_x ArrayStride 1 + OpMemberDecorate %message_x 0 Offset 0 + OpMemberDecorate %message_x 1 Offset 6 + OpMemberDecorate %message_x 2 Offset 8 + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void + + %char_t = OpTypeInt 8 0 + %uint32_t = OpTypeInt 32 0 + %str1len = OpConstant %uint32_t 6 +%string1_t = OpTypeArray %char_t %str1len + ; "test: " + %string1 = OpConstantDataKHR %string1_t 0x74736574 0x0000203A + %str2len = OpSpecConstant %uint32_t 2 +%string2_t = OpTypeArray %char_t %str2len + ; "%u" + %string2 = OpSpecConstantDataKHR %string2_t 0x00007525 +%message_t = OpTypeStruct %string1_t %string2_t %uint32_t +%uintval = OpConstant %uint32_t 6 + +%string1_x = OpTypeArray %char_t %str1len +%string2_x = OpTypeArray %char_t %str2len +%message_x = OpTypeStruct %string1_t %string2_t %uint32_t + +%main = OpFunction %void None %void_fn +%entry = OpLabel + %message = OpCompositeConstruct %message_t %string1 %string2 %uintval + OpAbortKHR %message_x %message + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRAbort, MismatchedCompositeOperandTypes) { + const std::string str = R"( + OpCapability Shader + OpCapability Int8 + OpCapability AbortKHR + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_abort" + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + + OpDecorate %string1_t UTFEncodedKHR + OpDecorate %string2_t UTFEncodedKHR + + OpDecorate %string1_x UTFEncodedKHR + OpDecorate %string1_x ArrayStride 1 + OpDecorate %string2_x UTFEncodedKHR + OpDecorate %string2_x ArrayStride 1 + OpMemberDecorate %message_x 0 Offset 0 + OpMemberDecorate %message_x 1 Offset 6 + OpMemberDecorate %message_x 2 Offset 8 + +%void = OpTypeVoid +%void_fn = OpTypeFunction %void + + %char_t = OpTypeInt 8 0 + %uint32_t = OpTypeInt 32 0 + %str1len = OpConstant %uint32_t 6 +%string1_t = OpTypeArray %char_t %str1len + ; "test: " + %string1 = OpConstantDataKHR %string1_t 0x74736574 0x0000203A + %str2len = OpSpecConstant %uint32_t 2 +%string2_t = OpTypeArray %char_t %str2len + ; "%u" + %string2 = OpSpecConstantDataKHR %string2_t 0x00007525 +%message_t = OpTypeStruct %string1_t %string2_t +%uintval = OpConstant %uint32_t 6 + +%string1_x = OpTypeArray %char_t %str1len +%string2_x = OpTypeArray %char_t %str2len +%message_x = OpTypeStruct %string1_t %string2_t %uint32_t + +%main = OpFunction %void None %void_fn +%entry = OpLabel + %message = OpCompositeConstruct %message_t %string1 %string2 + OpAbortKHR %message_x %message + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type of Message operand does not logically match " + "the type of the Message Type operand")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataNonArray) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %data = OpConstantDataKHR %uint 1 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type must be an array.")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataFloat) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %uint_size = OpConstant %uint 1 + %uint_array = OpTypeArray %float %uint_size + %data = OpConstantDataKHR %uint_array 1 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type must be an array of integer scalar type")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataIntVector) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uvec3 = OpTypeVector %uint 3 + %uint_size = OpConstant %uint 1 + %uint_array = OpTypeArray %uvec3 %uint_size + %data = OpConstantDataKHR %uint_array 1 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type must be an array of integer scalar type")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataMultiLength) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int8 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %char = OpTypeInt 8 1 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %char_array_1 = OpTypeArray %char %uint_1 + %char_array_2 = OpTypeArray %char %uint_2 + %char_array_3 = OpTypeArray %char %uint_3 + %char_array_4 = OpTypeArray %char %uint_4 + %data_1 = OpConstantDataKHR %char_array_1 0 + %data_2 = OpConstantDataKHR %char_array_2 0 + %data_3 = OpConstantDataKHR %char_array_3 0 + %data_4 = OpConstantDataKHR %char_array_4 0 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataSpecLength) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_size = OpSpecConstant %uint 4444 + %uint_array = OpTypeArray %uint %uint_size + %data = OpConstantDataKHR %uint_array 0 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataSpecLengthAndData) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %uint_size SpecId 1 + OpDecorate %data SpecId 2 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_size = OpSpecConstant %uint 4444 + %uint_array = OpTypeArray %uint %uint_size + %data = OpSpecConstantDataKHR %uint_array 1 + ; No SpecID for this one + %data_2 = OpSpecConstantDataKHR %uint_array 2 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataNull) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_size = OpConstant %uint 1 + %uint_array = OpTypeArray %uint %uint_size + %data = OpConstantDataKHR %uint_array + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("There must be at least 1 literal integer")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthOverUint32) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_size = OpConstant %uint 2 + %uint_array = OpTypeArray %uint %uint_size + %data = OpConstantDataKHR %uint_array 1 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 1 words of data, but needs to have 2 words " + "to match the array of 2 of 32-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUnderUint32) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_size = OpConstant %uint 2 + %uint_array = OpTypeArray %uint %uint_size + %data = OpConstantDataKHR %uint_array 1 2 3 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 3 words of data, but needs to have 2 words " + "to match the array of 2 of 32-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthOverUint8) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int8 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %char = OpTypeInt 8 1 + %uint_size = OpConstant %uint 5 + %char_array = OpTypeArray %char %uint_size + %data = OpConstantDataKHR %char_array 1 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 1 words of data, but needs to have 2 words " + "to match the array of 5 of 8-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUnderUint8) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int8 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %char = OpTypeInt 8 1 + %uint_size = OpConstant %uint 4 + %char_array = OpTypeArray %char %uint_size + %data = OpConstantDataKHR %char_array 1 2 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 2 words of data, but needs to have 1 words " + "to match the array of 4 of 8-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUint64Good) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int64 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %u64 = OpTypeInt 64 0 + %uint_size = OpConstant %uint 2 + %u64_array = OpTypeArray %u64 %uint_size + %data = OpConstantDataKHR %u64_array 1 2 3 4 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUint64Short) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int64 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %u64 = OpTypeInt 64 0 + %uint_size = OpConstant %uint 1 + %u64_array = OpTypeArray %u64 %uint_size + %data = OpConstantDataKHR %u64_array 1 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 1 words of data, but needs to have 2 words " + "to match the array of 1 of 64-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUint64Short2) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int64 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %u64 = OpTypeInt 64 0 + %uint_size = OpConstant %uint 2 + %u64_array = OpTypeArray %u64 %uint_size + %data = OpConstantDataKHR %u64_array 1 2 3 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 3 words of data, but needs to have 4 words " + "to match the array of 2 of 64-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthOverUint64) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int64 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %u64 = OpTypeInt 64 0 + %uint_size = OpConstant %uint 1 + %u64_array = OpTypeArray %u64 %uint_size + %data = OpConstantDataKHR %u64_array 1 2 3 4 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 4 words of data, but needs to have 2 words " + "to match the array of 1 of 64-bit ints")); +} + +TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUnderUint64) { + const std::string str = R"( + OpCapability Shader + OpCapability ConstantDataKHR + OpCapability Int64 + OpExtension "SPV_KHR_constant_data" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %u64 = OpTypeInt 64 0 + %uint_size = OpConstant %uint 2 + %u64_array = OpTypeArray %u64 %uint_size + %data = OpConstantDataKHR %u64_array 1 2 + %void_func = OpTypeFunction %void + %main = OpFunction %void None %void_func + %main_label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains 2 words of data, but needs to have 4 words " + "to match the array of 2 of 64-bit ints")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp index 5b3a309..9e3d53a 100644 --- a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp +++ b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
@@ -18,9 +18,7 @@ #include <vector> #include "gmock/gmock.h" -#include "source/extensions.h" -#include "source/spirv_target_env.h" -#include "test/unit_spirv.h" +#include "spirv-tools/libspirv.h" #include "test/val/val_fixtures.h" namespace spvtools { @@ -739,7 +737,7 @@ "%uchar", // match width "%v4uchar", "%v4uchar", - "%uint", + "%uchar", false, ""}, Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, @@ -1247,80 +1245,365 @@ true, ""})); -using ValidateSpvKHRIntegerDotProductSimple = ::testing::Test; +using ValidateIntegerDotProductSimple = spvtest::ValidateBase<bool>; -TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_RequiresExtension) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, RequiresExtension) { + const std::string ss = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%fn = OpTypeFunction %void +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_2 = OpConstant %int 2 +%vec_a = OpConstantComposite %v2int %int_2 %int_2 +%vec_b = OpConstantComposite %v2int %int_2 %int_2 +%main = OpFunction %void None %fn +%label = OpLabel +%x = OpSDot %int %vec_a %vec_b +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Opcode SDot requires one of these capabilities: DotProduct")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_Invalid_ResultTooNarrow) { - // Test across all the instructions. - FAIL(); +std::string GenerateShaderCode(const std::string& body) { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability DotProductKHR +OpCapability DotProductInputAll +OpCapability DotProductInput4x8BitPacked +OpCapability Int16 +OpCapability Int64 +OpCapability LongVectorEXT +OpExtension "SPV_EXT_long_vector" +OpExtension "SPV_KHR_integer_dot_product" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %spec_5 SpecId 0 +OpDecorate %spec_6 SpecId 1 + +%void = OpTypeVoid +%fn = OpTypeFunction %void + +%float = OpTypeFloat 32 +%int16 = OpTypeInt 16 1 +%uint16 = OpTypeInt 16 0 +%uint = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%int64 = OpTypeInt 64 1 +%v2int = OpTypeVector %int 2 +%v3int = OpTypeVector %int 3 +%v2uint = OpTypeVector %uint 2 +%v2int16 = OpTypeVector %int16 2 +%v2uint16 = OpTypeVector %uint16 2 +%v2int64 = OpTypeVector %int64 2 + +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%uint_1 = OpConstant %uint 1 +%int64_1 = OpConstant %int64 1 +%int16_1 = OpConstant %int16 1 +%uint16_1 = OpConstant %uint16 1 + +%ivec2_1 = OpConstantComposite %v2int %int_1 %int_1 +%ivec3_1 = OpConstantComposite %v3int %int_1 %int_1 %int_1 +%uvec2_1 = OpConstantComposite %v2uint %uint_1 %uint_1 +%i16vec2_1 = OpConstantComposite %v2int16 %int16_1 %int16_1 +%u16vec2_1 = OpConstantComposite %v2uint16 %uint16_1 %uint16_1 +%i64vec2_1 = OpConstantComposite %v2int64 %int64_1 %int64_1 + +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%spec_5 = OpSpecConstant %uint 5 +%spec_6 = OpSpecConstant %uint 6 + +%uvec5 = OpTypeVectorIdEXT %uint %uint_5 +%uvec6 = OpTypeVectorIdEXT %uint %uint_6 +%spec_uvec5 = OpTypeVectorIdEXT %uint %spec_5 +%spec_uvec6 = OpTypeVectorIdEXT %uint %spec_6 + +%uvec5_1 = OpConstantComposite %uvec5 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 +%uvec6_1 = OpConstantComposite %uvec6 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 +%spec_uvec5_1 = OpConstantComposite %spec_uvec5 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 +%spec_uvec6_1 = OpConstantComposite %spec_uvec6 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 + +%main = OpFunction %void None %fn +%label = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + return ss.str(); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_UDot_OperandTypesMatch) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, Dot16BitGood) { + const std::string ss = R"( + %x = OpSDot %int %i16vec2_1 %i16vec2_1 + %y = OpUDot %uint %u16vec2_1 %u16vec2_1 + %z = OpSUDot %uint %i16vec2_1 %u16vec2_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_SDot_OperandTypesMatchExceptSignedness) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotDifferentVectors) { + const std::string ss = R"( + %x = OpSDot %int %ivec2_1 %ivec3_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("'Vector 1' is 2 components but 'Vector 2' is 3 components")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_SUDot_OperandTypesMatchExceptSignedness) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotFloat) { + const std::string ss = R"( + %x = OpSDot %int %float_1 %float_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 'Vector 1' to be an int scalar or vector")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_UDotAccSat_OperandTypesMatch) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDot16BitScalar) { + const std::string ss = R"( + %x = OpSDot %int %int16_1 %int16_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 'Vector 1' to be an int scalar or vector")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_SDotAccSat_OperandTypesMatchExceptSignedness) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotResultVector) { + const std::string ss = R"( + %x = OpSDot %v2int %ivec2_1 %ivec2_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be an int scalar type")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_SUDotAccSat_OperandTypesMatchExceptSignedness) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotResultSmall) { + const std::string ss = R"( + %x = OpSDot %int %i64vec2_1 %i64vec2_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result width (32) must be greater than or equal to " + "the vectors width (64)")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_UDot_RequiresUnsigned) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotNoPackedVectorFormat) { + const std::string ss = R"( + %x = OpSDot %int %int_1 %int_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("'Vector 1' and 'Vector 2' are a 32-bit int scalar, " + "but no Packed Vector Format was provided")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_SUDot_RequiresUnsignedSecondArg) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, UDotResultSigned) { + const std::string ss = R"( + %x = OpUDot %int %uvec2_1 %uvec2_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be an unsigned int scalar type")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_UDotAccSat_RequiresUnsigned) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, UDotVectorSigned) { + const std::string ss = R"( + %x = OpUDot %uint %ivec2_1 %ivec2_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 'Vector 1' to be an vector of unsigned integers")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_SUDotAccSat_RequiresUnsignedSecondArg) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SUDotVectorSigned) { + const std::string ss = R"( + %x = OpSUDot %uint %ivec2_1 %ivec2_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 'Vector 2' to be an vector of unsigned integers")); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_VectorOperandsDisallowPackedFormat) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotLongVectorGood) { + const std::string ss = R"( + %x = OpSDot %uint %uvec5_1 %uvec5_1 + %y = OpSDot %uint %uvec6_1 %uvec6_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST(ValidateSpvKHRIntegerDotProductSimple, - DISABLED_Invalid_ScalarOperandsRequirePackedFormat) { - FAIL(); +TEST_F(ValidateIntegerDotProductSimple, SDotLongVectorSpec) { + const std::string ss = R"( + %x = OpSDot %uint %spec_uvec5_1 %spec_uvec6_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -// TODO(dneto): Test valid cases with other scalar integer types -// TODO(dneto): Test valid cases of length-8 vectors -// TODO(dneto): Test valid cases of length-16 vectors +TEST_F(ValidateIntegerDotProductSimple, SDotLongVector) { + const std::string ss = R"( + %x = OpSDot %uint %uvec5_1 %uvec6_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("'Vector 1' is 5 components but 'Vector 2' is 6 components")); +} + +TEST_F(ValidateIntegerDotProductSimple, UDotAccSat) { + const std::string ss = R"( + %x = OpUDotAccSat %uint %uvec2_1 %uvec2_1 %int_1 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be the same as the Accumulator type")); +} + +TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInput4x8BitPacked) { + const std::string ss = R"( + OpCapability Shader + OpCapability DotProductKHR + OpExtension "SPV_KHR_integer_dot_product" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %fn = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %fn + %label = OpLabel + %x = OpSDot %int %int_1 %int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DotProductInput4x8BitPacked capability is required to " + "use scalar integers")); +} + +TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInput4x8Bit) { + const std::string ss = R"( + OpCapability Shader + OpCapability DotProductKHR + OpCapability Int8 + OpExtension "SPV_KHR_integer_dot_product" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %fn = OpTypeFunction %void + %char = OpTypeInt 8 1 + %v4char = OpTypeVector %char 4 + %char_1 = OpConstant %char 1 + %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1 + %main = OpFunction %void None %fn + %label = OpLabel + %x = OpSDot %char %v4char_1 %v4char_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DotProductInput4x8Bit or DotProductInputAll capability is " + "required to use 4-component vectors of 8-bit integers")); +} + +TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInputAll) { + const std::string ss = R"( + OpCapability Shader + OpCapability DotProductKHR + OpExtension "SPV_KHR_integer_dot_product" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %fn = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %v4int = OpTypeVector %int 4 + %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1 + %main = OpFunction %void None %fn + %label = OpLabel + %x = OpSDot %int %v4int_1 %v4int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DotProductInputAll capability is additionally required to the " + "DotProduct capability to use vectors. (It is possible to set " + "DotProductInput4x8BitPacked to only use 32-bit scalars packed " + "as a 4-wide 8-byte vector)")); +} + +TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInputAll2) { + const std::string ss = R"( + OpCapability Shader + OpCapability DotProductKHR + OpCapability DotProductInput4x8BitPacked + OpExtension "SPV_KHR_integer_dot_product" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %fn = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %v4int = OpTypeVector %int 4 + %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1 + %main = OpFunction %void None %fn + %label = OpLabel + %x = OpSDot %int %v4int_1 %v4int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DotProductInputAll capability is required use " + "vectors. (DotProductInput4x8BitPacked capability " + "declared allows for only 32-bit int scalars)")); +} } // namespace } // namespace val
diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp index f528cb9..c4c8355 100644 --- a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp +++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
@@ -18,9 +18,9 @@ #include <vector> #include "gmock/gmock.h" -#include "source/enum_string_mapping.h" #include "source/extensions.h" #include "source/spirv_target_env.h" +#include "source/table2.h" #include "test/test_fixture.h" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h"
diff --git a/test/val/val_extensions_test.cpp b/test/val/val_extensions_test.cpp index bc8e972..555fd31 100644 --- a/test/val/val_extensions_test.cpp +++ b/test/val/val_extensions_test.cpp
@@ -61,8 +61,8 @@ "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", - "SPV_KHR_terminate_invocation", - "SPV_KHR_relaxed_extended_instruction")); + "SPV_KHR_terminate_invocation", "SPV_KHR_relaxed_extended_instruction", + "SPV_EXT_float8")); INSTANTIATE_TEST_SUITE_P(FailSilently, ValidateUnknownExtensions, Values("ERROR_unknown_extension", "SPV_KHR_", @@ -132,6 +132,134 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_KHR_device_group")); } +TEST_F(ValidateExtensionCapabilities, SpirvVersionImageProcessingQCOM) { + const std::string str = R"( + OpCapability Shader + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %v_texcoord %fragColor %target_samp %ref_samp + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_QCOM_image_processing" + OpSourceExtension "GL_QCOM_image_processing2" + OpDecorate %v_texcoord Location 0 + OpDecorate %fragColor Location 0 + OpDecorate %target_samp DescriptorSet 0 + OpDecorate %target_samp Binding 4 + OpDecorate %ref_samp DescriptorSet 0 + OpDecorate %ref_samp Binding 5 + OpDecorate %target_samp BlockMatchTextureQCOM + OpDecorate %target_samp BlockMatchSamplerQCOM + OpDecorate %ref_samp BlockMatchTextureQCOM + OpDecorate %ref_samp BlockMatchSamplerQCOM + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v_texcoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %39 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %fragColor = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown + %43 = OpTypeSampledImage %42 +%_ptr_UniformConstant_43 = OpTypePointer UniformConstant %43 +%target_samp = OpVariable %_ptr_UniformConstant_43 UniformConstant + %ref_samp = OpVariable %_ptr_UniformConstant_43 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %tgt_coords = OpVariable %_ptr_Function_v2uint Function + %ref_coords = OpVariable %_ptr_Function_v2uint Function + %blockSize = OpVariable %_ptr_Function_v2uint Function + %16 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_0 + %17 = OpLoad %float %16 + %18 = OpConvertFToU %uint %17 + %20 = OpAccessChain %_ptr_Function_uint %tgt_coords %uint_0 + OpStore %20 %18 + %22 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_1 + %23 = OpLoad %float %22 + %24 = OpConvertFToU %uint %23 + %25 = OpAccessChain %_ptr_Function_uint %tgt_coords %uint_0 + OpStore %25 %24 + %28 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_2 + %29 = OpLoad %float %28 + %30 = OpConvertFToU %uint %29 + %31 = OpAccessChain %_ptr_Function_uint %ref_coords %uint_0 + OpStore %31 %30 + %33 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_3 + %34 = OpLoad %float %33 + %35 = OpConvertFToU %uint %34 + %36 = OpAccessChain %_ptr_Function_uint %ref_coords %uint_1 + OpStore %36 %35 + OpStore %blockSize %39 + %46 = OpLoad %43 %target_samp + %47 = OpLoad %v2uint %tgt_coords + %49 = OpLoad %43 %ref_samp + %50 = OpLoad %v2uint %ref_coords + %51 = OpLoad %v2uint %blockSize + %52 = OpImageBlockMatchWindowSADQCOM %v4float %46 %47 %49 %50 %51 + OpStore %fragColor %52 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SPV_QCOM_image_processing extension requires SPIR-V " + "version 1.4 or later.")); +} + +TEST_F(ValidateExtensionCapabilities, SpirvVersionCoopMatConversionQCOM) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixConversionQCOM +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_QCOM_cooperative_matrix_conversion" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%f16 = OpTypeFloat 16 +%uint_0 = OpConstant %uint 0 +%uint_8 = OpConstant %uint 8 +%uint_64 = OpConstant %uint 64 +%_arr_f16_uint_8 = OpTypeArray %f16 %uint_8 +%_arr_f16_uint_64 = OpTypeArray %f16 %uint_64 +%_ptr_Function__arr_f16_uint_64 = OpTypePointer Function %_arr_f16_uint_64 +%main = OpFunction %void None %3 +%5 = OpLabel +%f16vec64Acc = OpVariable %_ptr_Function__arr_f16_uint_64 Function +%83 = OpLoad %_arr_f16_uint_64 %f16vec64Acc +%86 = OpExtractSubArrayQCOM %_arr_f16_uint_8 %83 %uint_0 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_WRONG_VERSION, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SPV_KHR_vulkan_memory_model extension requires SPIR-V " + "version 1.3 or later.")); +} + TEST_F(ValidateExtensionCapabilities, DeclCapabilityFailureBlockMatchWIndowSAD) { const std::string str = R"( @@ -223,8 +351,9 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(str.c_str()); - ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT(getDiagnosticString(), HasSubstr("2nd operand of Decorate")); EXPECT_THAT(getDiagnosticString(), HasSubstr("requires one of these extensions")); @@ -332,8 +461,9 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(str.c_str()); - ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT(getDiagnosticString(), HasSubstr("2nd operand of Decorate")); EXPECT_THAT(getDiagnosticString(), HasSubstr("requires one of these extensions")); @@ -482,7 +612,8 @@ GetParam().cap + R"( OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %builtin - OpDecorate %builtin BuiltIn )" + GetParam().builtin + R"( + OpDecorate %builtin BuiltIn )" + + GetParam().builtin + R"( %void = OpTypeVoid %3 = OpTypeFunction %void %int = OpTypeInt 32 1
diff --git a/test/val/val_fixtures.h b/test/val/val_fixtures.h index db9d045..9c10682 100644 --- a/test/val/val_fixtures.h +++ b/test/val/val_fixtures.h
@@ -56,6 +56,13 @@ // This function overwrites the word at the given index with a new word. void OverwriteAssembledBinary(uint32_t index, uint32_t word); + // Overwrites the ID bound. + void OverwriteIdBound(uint32_t bound) { + // The ID bound is in the header at word index 3. + // SPIR-V section 2.3 Physical Layout of a sPIR-V Module and Instruction. + OverwriteAssembledBinary(3, bound); + } + // Performs validation on the SPIR-V code. spv_result_t ValidateInstructions(spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
diff --git a/test/val/val_function_test.cpp b/test/val/val_function_test.cpp index 119edd3..ea53ca8 100644 --- a/test/val/val_function_test.cpp +++ b/test/val/val_function_test.cpp
@@ -836,6 +836,200 @@ HasSubstr("type does not match Function <id>")); } +TEST_F(ValidateFunctionCall, PointerReturnTypeStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer StorageBuffer %int +%null = OpConstantNull %ptr +%foo_ty = OpTypeFunction %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr None %foo_ty +%foo_entry = OpLabel +OpReturnValue %null +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, functions may only return a storage " + "buffer pointer if the VariablePointersStorageBuffer " + "capability is declared")); +} + +TEST_F(ValidateFunctionCall, PointerReturnTypeStorageBufferUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%null = OpConstantNull %ptr +%foo_ty = OpTypeFunction %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr None %foo_ty +%foo_entry = OpLabel +OpReturnValue %null +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, functions may only return a storage " + "buffer pointer if the VariablePointersStorageBuffer " + "capability is declared")); +} + +TEST_F(ValidateFunctionCall, PointerReturnTypeWorkgroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +%null = OpConstantNull %ptr +%foo_ty = OpTypeFunction %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr None %foo_ty +%foo_entry = OpLabel +OpReturnValue %null +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, functions may only return a workgroup " + "pointer if the VariablePointers capability is declared")); +} + +TEST_F(ValidateFunctionCall, PointerReturnTypeWorkgroupUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%ptr = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr +%foo_ty = OpTypeFunction %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr None %foo_ty +%foo_entry = OpLabel +OpReturnValue %null +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, functions may only return a workgroup " + "pointer if the VariablePointers capability is declared")); +} + +TEST_F(ValidateFunctionCall, PointerReturnTypePrivate) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Private %int +%var = OpVariable %ptr Private +%foo_ty = OpTypeFunction %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr None %foo_ty +%foo_entry = OpLabel +OpReturnValue %var +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Logical addressing, functions may not return a " + "pointer in this storage class")); +} + +TEST_F(ValidateFunctionCall, PointerReturnTypePrivateUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypeUntypedPointerKHR Private +%var = OpUntypedVariableKHR %ptr Private %int +%foo_ty = OpTypeFunction %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr None %foo_ty +%foo_entry = OpLabel +OpReturnValue %var +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Logical addressing, functions may not return a " + "pointer in this storage class")); +} + TEST_F(ValidateFunctionCall, UntypedPointerParameterMismatch) { const std::string spirv = R"( OpCapability Shader
diff --git a/test/val/val_graph_test.cpp b/test/val/val_graph_test.cpp new file mode 100644 index 0000000..72bbde8 --- /dev/null +++ b/test/val/val_graph_test.cpp
@@ -0,0 +1,1362 @@ +// Copyright (c) 2023-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +// TODO(kpet) Tests with spec constants + +namespace spvtools { +namespace val { +namespace { + +using ::testing::ContainsRegex; +using ::testing::HasSubstr; + +using ValidateGraph = spvtest::ValidateBase<std::string>; + +constexpr spv_target_env SPVENV = SPV_ENV_VULKAN_1_3; + +std::string GenerateModule(const std::string& src) { + const std::string boilerplate = R"( + OpCapability Shader + OpCapability VulkanMemoryModel + OpCapability Int8 + OpCapability GraphARM + OpCapability TensorsARM + OpCapability RuntimeDescriptorArray + OpExtension "SPV_ARM_graph" + OpExtension "SPV_ARM_tensors" + OpMemoryModel Logical Vulkan + OpDecorate %var_int8tensor DescriptorSet 0 + OpDecorate %var_int8tensor Binding 0 + OpDecorate %var_int32tensor DescriptorSet 0 + OpDecorate %var_int32tensor Binding 1 + OpDecorate %var_int8tensor_array3 DescriptorSet 0 + OpDecorate %var_int8tensor_array3 Binding 2 + OpDecorate %var_int8tensor_runtime_array DescriptorSet 0 + OpDecorate %var_int8tensor_runtime_array Binding 3 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %int8 = OpTypeInt 8 1 + %int32 = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %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 + %float_1 = OpConstant %float 1.0 + %int8tensor = OpTypeTensorARM %int8 %uint_4 + %int8r3tensor = OpTypeTensorARM %int8 %uint_3 + %int32tensor = OpTypeTensorARM %int32 %uint_4 +%int8tensor_array3 = OpTypeArray %int8tensor %uint_3 +%int32tensor_array3 = OpTypeArray %int32tensor %uint_3 +%int8tensor_runtime_array = OpTypeRuntimeArray %int8tensor +%int32tensor_runtime_array = OpTypeRuntimeArray %int32tensor +%ptr_Input_int8tensor = OpTypePointer Input %int8tensor +%var_int8tensor_wrong_storage_class = OpVariable %ptr_Input_int8tensor Input +%ptr_UniformConstant_int8tensor = OpTypePointer UniformConstant %int8tensor +%ptr_UniformConstant_int32tensor = OpTypePointer UniformConstant %int32tensor +%ptr_UniformConstant_int8tensor_array3 = OpTypePointer UniformConstant %int8tensor_array3 +%ptr_UniformConstant_int8tensor_runtime_array = OpTypePointer UniformConstant %int8tensor_runtime_array + %var_int8tensor = OpVariable %ptr_UniformConstant_int8tensor UniformConstant + %var_int32tensor = OpVariable %ptr_UniformConstant_int32tensor UniformConstant +%var_int8tensor_array3 = OpVariable %ptr_UniformConstant_int8tensor_array3 UniformConstant +%var_int8tensor_runtime_array = OpVariable %ptr_UniformConstant_int8tensor_runtime_array UniformConstant +)"; + return boilerplate + src; +} + +std::string GenerateModuleWithGraphEntryPoint(const std::string& header) { + const std::string src = R"( +%default_graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %default_graph "default_entry_point" %var_int8tensor %var_int8tensor + %default_graph = OpGraphARM %default_graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM + )"; + return GenerateModule(header) + src; +} + +// +// Layout tests +// + +TEST_F(ValidateGraph, InvalidNoGraphEntryPoint) { + std::string spvasm = GenerateModule(""); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("No OpGraphEntryPointARM instruction was found but the " + "GraphARM capability is declared.")); +} + +TEST_F(ValidateGraph, InvalidGraphInGraph) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + %graph = OpGraphARM %graph_type + %graph2 = OpGraphARM %graph_type +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot define a graph in a graph")); +} + +TEST_F(ValidateGraph, InvalidGraphEndOutsideGraph) { + const std::string src = R"( + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GraphEndARM must be preceded by at least one " + "OpGraphSetOutputARM instruction")); +} + +TEST_F(ValidateGraph, InvalidGraphEntryPointInsideGraph) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphEntryPointARM %graph "main" %in %in +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "GraphEntryPointARM cannot appear in the definition of a graph")); +} + +TEST_F(ValidateGraph, InvalidNonGraphInstructionInGraphSection) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + %ftype = OpTypeFunction %void %void + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM + %fn = OpFunction %void None %ftype +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Function cannot appear in the graph definitions section")); +} + +TEST_F(ValidateGraph, InvalidGraphInputOusideGraph) { + const std::string src = R"( + %in = OpGraphInputARM %int8tensor %uint_0 +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGraphInputARM must immediately follow an OpGraphARM " + "or OpGraphInputARM instruction.")); +} + +TEST_F(ValidateGraph, InvalidGraphSetOutputsOusideGraph) { + const std::string src = R"( + OpGraphSetOutputARM %uint_0 %uint_0 +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGraphSetOutputARM must immediately precede an " + "OpGraphEndARM or OpGraphSetOutputARM instruction")); +} + +TEST_F(ValidateGraph, ValidGraphConstantOusideGraph) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + %cst = OpGraphConstantARM %int8tensor 1 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphWithNoInputsNoBody) { + const std::string src = R"( + %cst = OpGraphConstantARM %int8tensor 1 + %graph_type = OpTypeGraphARM 0 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor + %graph = OpGraphARM %graph_type + OpGraphSetOutputARM %cst %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphWithDisallowedBodyInstructions) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + %val = OpCompositeExtract %int8r3tensor %in 0 + %out = OpCompositeInsert %int8tensor %val %in 0 + OpGraphSetOutputARM %out %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpCompositeInsert cannot appear in the graph definitions section")); +} + +TEST_F(ValidateGraph, InvalidInstructionOutsideGraphAfterGraph) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + %cst = OpGraphConstantARM %int8tensor 1 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM + %val = OpCompositeExtract %int8r3tensor %cst 0 +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCompositeExtract must appear in a graph body")); +} + +// +// Type tests +// +TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputNoInputs) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 0 %int8tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorArrayOutputNoInputs) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 0 %int8tensor_array3 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorRuntimeArrayOutputNoInputs) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 0 %int8tensor_runtime_array +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputOneTensorInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputOneTensorArrayInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputOneTensorRuntimeArrayInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, + ValidGraphTypeOneTensorOutputOneTensorInputOneTensorArrayInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor_array3 %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F( + ValidateGraph, + ValidGraphTypeOneTensorOutputOneTensorRuntimeArrayInputOneTensorArrayInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor_array3 %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorArrayOutputOneTensorInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_array3 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphTypeOneTensorRuntimeArrayOutputOneTensorInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_runtime_array +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, + ValidGraphTypeOneTensorRuntimeArrayOutputOneTensorOuputOneTensorInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_runtime_array %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F( + ValidateGraph, + ValidGraphTypeOneTensorRuntimeArrayOutputOneTensorArrayOutputOneTensorOuputOneTensorInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_runtime_array %int32tensor_array3 %int32tensor +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} +TEST_F(ValidateGraph, + ValidGraphTypeOneTensorRuntimeArrayOutputOnteTensorOuputOneTensorInput) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int32tensor_array3 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphTypeWithNotEnoughIOTypes) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 10 %int8tensor %int8tensor +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("2 I/O types were provided but the graph has 10 inputs")); +} + +TEST_F(ValidateGraph, InvalidGraphTypeWithNonGraphInterfaceTypeIO) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor %uint +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("I/O type '.*' is not a Graph Interface Type.*")); +} + +TEST_F(ValidateGraph, InvalidGraphTypeWithOneInputZeroOutputs) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 1 %int8tensor +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("A graph type must have at least one output")); +} + +TEST_F(ValidateGraph, InvalidGraphTypeWithZeroInputsZeroOutputs) { + const std::string src = R"( + %graph_type = OpTypeGraphARM 0 +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("A graph type must have at least one output")); +} + +// +// Constant tests +// +TEST_F(ValidateGraph, ValidGraphConstantTensorUnranked) { + const std::string src = R"( + %tensor_type = OpTypeTensorARM %uint + %cst = OpGraphConstantARM %tensor_type 25 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphConstantTensorRanked) { + const std::string src = R"( + %tensor_type = OpTypeTensorARM %uint %uint_4 + %cst = OpGraphConstantARM %tensor_type 25 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidGraphConstantTensorShaped) { + const std::string src = R"( + %uint_array2 = OpTypeArray %uint %uint_2 + %tensor_shape = OpConstantComposite %uint_array2 %uint_1 %uint_4 + %tensor_type = OpTypeTensorARM %uint %uint_2 %tensor_shape + %cst = OpGraphConstantARM %tensor_type 25 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphConstantDuplicateIDs) { + const std::string src = R"( + %cst = OpGraphConstantARM %int8tensor 25 + %cst2 = OpGraphConstantARM %int32tensor 25 +)"; + std::string spvasm = GenerateModuleWithGraphEntryPoint(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("No two OpGraphConstantARM instructions may have the " + "same GraphConstantID")); +} + +TEST_F(ValidateGraph, InvalidGraphConstantWithNonTensorType) { + const std::string src = R"( + %cst = OpGraphConstantARM %uint 25 +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "GraphConstantARM must have a Result Type that is a tensor type")); +} + +// +// EntryPoint tests +// +TEST_F(ValidateGraph, InvalidModuleWithNoGraphEntryPoint) { + std::string spvasm = GenerateModule(""); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("No OpGraphEntryPointARM instruction was found but the " + "GraphARM capability is declared.")); +} +TEST_F(ValidateGraph, InvalidGraphEntryPointNotAGraph) { + const std::string src = R"( + OpGraphEntryPointARM %uint_0 "longname" +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "GraphEntryPointARM Graph must be a OpGraphARM but found Constant")); +} + +TEST_F(ValidateGraph, InvalidGraphEntryPointInterfaceIDNotOpVariable) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %uint_0 %uint_0 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex( + "GraphEntryPointARM Interface ID '.*' must " + "come from OpVariable with UniformConstant Storage Class.*")); +} + +TEST_F(ValidateGraph, InvalidGraphEntryPointInterfaceIDWrongStorageClass) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_wrong_storage_class %var_int8tensor_wrong_storage_class + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex( + "GraphEntryPointARM Interface ID '.*' must " + "come from OpVariable with UniformConstant Storage Class.*")); +} + +TEST_F(ValidateGraph, InvalidGraphEntryPointNotEnoughInterfaceIDs) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex( + "GraphEntryPointARM Interface list contains 1 IDs but Graph's type " + "'.*' has 2 inputs and outputs.*")); +} + +TEST_F(ValidateGraph, InvalidGraphEntryPointTooManyInterfaceIDs) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex( + "GraphEntryPointARM Interface list contains 3 IDs but Graph's type " + "'.*' has 2 inputs and outputs.*")); +} + +TEST_F(ValidateGraph, + InvalidGraphEntryPointInterfaceIDTypeMismatchesGraphIOType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int32tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("GraphEntryPointARM Interface ID type " + "'.*' must match the type of the " + "corresponding graph I/O '.*'.*")); +} + +// +// Graph tests +// +TEST_F(ValidateGraph, InvalidGraphResultType) { + const std::string src = R"( + OpGraphEntryPointARM %graph "foo" + %graph = OpGraphARM %uint + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GraphARM Result Type must be an OpTypeGraphARM")); +} + +// +// Input tests +// +TEST_F(ValidateGraph, ValidTensorInput) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorArrayInputCompositeExtract) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type +%in_tensors = OpGraphInputARM %int8tensor_array3 %uint_0 + %out = OpCompositeExtract %int8tensor %in_tensors 1 + OpGraphSetOutputARM %out %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorArrayInputWithElementIndex) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type + %out = OpGraphInputARM %int8tensor %uint_0 %uint_1 + OpGraphSetOutputARM %out %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorRuntimeArrayInputCompositeExtract) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor + %graph = OpGraphARM %graph_type +%in_tensors = OpGraphInputARM %int8tensor_runtime_array %uint_0 + %out = OpCompositeExtract %int8tensor %in_tensors 1 + OpGraphSetOutputARM %out %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorRuntimeArrayInputWithElementIndex) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor + %graph = OpGraphARM %graph_type + %out = OpGraphInputARM %int8tensor %uint_0 %uint_1 + OpGraphSetOutputARM %out %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphInputIndexWrongType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %float_1 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GraphInputARM InputIndex must be a 32-bit integer")); +} + +TEST_F(ValidateGraph, InvalidGraphInputElementIndexWrongType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 %float_1 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GraphInputARM ElementIndex must be a 32-bit integer")); +} + +TEST_F(ValidateGraph, InvalidGraphInputIndexDuplicate) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + %in2 = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Two OpGraphInputARM instructions with the same " + "InputIndex must not be part of the same " + "graph definition unless ElementIndex is present in " + "both with different values.")); +} + +TEST_F(ValidateGraph, InvalidGraphInputAndElementIndexDuplicate) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 %uint_1 + %in2 = OpGraphInputARM %int8tensor %uint_0 %uint_1 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Two OpGraphInputARM instructions with the same " + "InputIndex must not be part of the same " + "graph definition unless ElementIndex is present in " + "both with different values.")); +} + +TEST_F(ValidateGraph, + InvalidGraphInputIndexDuplicateWithAndWithoutElementIndex) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor_array3 %uint_0 + %in2 = OpGraphInputARM %int8tensor %uint_0 %uint_1 + OpGraphSetOutputARM %in2 %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Two OpGraphInputARM instructions with the same " + "InputIndex must not be part of the same " + "graph definition unless ElementIndex is present in " + "both with different values.")); +} + +TEST_F(ValidateGraph, ValidGraphInputIndexDuplicateWithDifferentElementIndex) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 %uint_2 + %in2 = OpGraphInputARM %int8tensor %uint_0 %uint_1 + OpGraphSetOutputARM %in2 %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphInputElementIndexWithNonArrayInput) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 %uint_1 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpGraphInputARM ElementIndex not allowed when the graph input " + "selected by " + "InputIndex is not an OpTypeArray or OpTypeRuntimeArray")); +} + +TEST_F(ValidateGraph, InvalidGraphInputElementIndexOutOfRange) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 %uint_3 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpGraphInputARM ElementIndex out of range. The " + "type of the graph input being accessed '.*' is an " + "array of 3 elements but ElementIndex is 3.*")); +} + +TEST_F(ValidateGraph, InvalidGraphInputIndexOutOfRange) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in0 = OpGraphInputARM %int8tensor %uint_0 + %in1 = OpGraphInputARM %int8tensor %uint_1 + %in2 = OpGraphInputARM %int8tensor %uint_2 + OpGraphSetOutputARM %in0 %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("Type '.*' for graph '.*' " + "has 2 inputs but found an OpGraphInputARM " + "instruction with an InputIndex that is 2.*")); +} + +TEST_F(ValidateGraph, InvalidGraphFirstInputTypeDoesNotMatchGraphType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in0 = OpGraphInputARM %int32tensor %uint_0 + %in1 = OpGraphInputARM %int8tensor %uint_1 + OpGraphSetOutputARM %in1 %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("Result Type '.*' of graph input instruction " + "'.*' does not match the type " + "'.*' of input 0 in the graph type.*")); +} + +TEST_F(ValidateGraph, InvalidGraphLastInputTypeDoesNotMatchGraphType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in0 = OpGraphInputARM %int8tensor %uint_0 + %in1 = OpGraphInputARM %int32tensor %uint_1 + OpGraphSetOutputARM %in0 %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("Result Type '.*' of graph input instruction " + "'.*' does not match the type " + "'.*' of input 1 in the graph type.*")); +} + +TEST_F(ValidateGraph, InvalidGraphInputAfterNonGraphInputOrGraph) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 2 %int8tensor_array3 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor_array3 %uint_0 + %val = OpCompositeExtract %int8tensor %in 0 + %in2 = OpGraphInputARM %int8tensor %uint_1 + OpGraphSetOutputARM %val %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGraphInputARM must immediately follow an OpGraphARM " + "or OpGraphInputARM instruction")); +} + +// +// Output tests +// +TEST_F(ValidateGraph, ValidTensorOutput) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorOutputWholeArray) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor_array3 %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorOutputArraySingleElement) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorOutputArrayMultipleElements) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphSetOutputARM %in %uint_0 %uint_2 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorOutputWholeRuntimeArray) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor_runtime_array + OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor_runtime_array + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor_runtime_array %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorOutputRuntimeArraySingleElement) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_runtime_array + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_runtime_array + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, ValidTensorOutputRuntimeArrayMultipleElements) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_runtime_array + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_runtime_array + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphSetOutputARM %in %uint_0 %uint_2 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphOutputIndexWrongType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %float_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GraphSetOutputARM OutputIndex must be a 32-bit integer")); +} + +TEST_F(ValidateGraph, InvalidGraphOutputElementIndexWrongType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %float_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GraphSetOutputARM ElementIndex must be a 32-bit integer")); +} + +TEST_F(ValidateGraph, InvalidGraphOutputIndexDuplicate) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Two OpGraphSetOutputARM instructions with the same " + "OutputIndex must not be part of the same " + "graph definition unless ElementIndex is present in " + "both with different values.")); +} + +TEST_F(ValidateGraph, InvalidGraphOutputAndElementIndexDuplicate) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Two OpGraphSetOutputARM instructions with the same " + "OutputIndex must not be part of the same " + "graph definition unless ElementIndex is present in " + "both with different values.")); +} + +TEST_F(ValidateGraph, + InvalidGraphOutputIndexDuplicateWithAndWithoutElementIndex) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor_array3 %uint_0 + %in_single = OpCompositeExtract %int8tensor %in 0 + OpGraphSetOutputARM %in %uint_0 + OpGraphSetOutputARM %in_single %uint_0 %uint_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Two OpGraphSetOutputARM instructions with the same " + "OutputIndex must not be part of the same " + "graph definition unless ElementIndex is present in " + "both with different values.")); +} + +TEST_F(ValidateGraph, ValidGraphOutputIndexDuplicateWithDifferentElementIndex) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_runtime_array + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_runtime_array + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_1 + OpGraphSetOutputARM %in %uint_0 %uint_4 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateGraph, InvalidGraphOutputElementIndexWithNonArrayOutput) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpGraphSetOutputARM ElementIndex not allowed when the graph " + "output selected by " + "OutputIndex is not an OpTypeArray or OpTypeRuntimeArray")); +} + +TEST_F(ValidateGraph, InvalidGraphOutputElementIndexOutOfRange) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3 + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3 + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 %uint_3 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex("OpGraphSetOutputARM ElementIndex out of range. The " + "type of the graph output being accessed '.*' is an " + "array of 3 elements but ElementIndex is 3.*")); +} + +TEST_F(ValidateGraph, InvalidGraphOutputNotBeforeEndOrOutput) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor_array3 %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor_array3 %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor_array3 %uint_0 + OpGraphSetOutputARM %in %uint_0 + %val = OpCompositeExtract %int8tensor %in 0 + OpGraphSetOutputARM %val %uint_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "CompositeExtract cannot appear after a graph output instruction")); +} + +TEST_F(ValidateGraph, InvalidGraphOutputIndexOutOfRange) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphSetOutputARM %in %uint_2 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GraphSetOutputARM setting OutputIndex 2 but graph " + "only has 2 outputs.")); +} + +TEST_F(ValidateGraph, InvalidGraphFirstOutputValueTypeDoesNotMatchOutputType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int32tensor %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int32tensor %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int32tensor %uint_0 + OpGraphSetOutputARM %in %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex( + "The type '.*' of Value provided to the graph output instruction " + "'.*' does not match the type '.*' of output 0 in the graph type.*")); +} + +TEST_F(ValidateGraph, InvalidGraphLastOutputValueTypeDoesNotMatchOutputType) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int32tensor %int32tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int32tensor %var_int32tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int32tensor %uint_0 + OpGraphSetOutputARM %in %uint_1 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex( + "The type '.*' of Value provided to the graph output instruction " + "'.*' does not match the type '.*' of output 1 in the graph type.*")); +} + +TEST_F(ValidateGraph, InvalidGraphNoSetOuputBeforeEnd) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 + OpGraphEndARM +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GraphEndARM must be preceded by at least one " + "OpGraphSetOutputARM instruction")); +} + +// +// End tests +// +TEST_F(ValidateGraph, InvalidGraphNoEnd) { + const std::string src = R"( +%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor + OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor + %graph = OpGraphARM %graph_type + %in = OpGraphInputARM %int8tensor %uint_0 +)"; + std::string spvasm = GenerateModule(src); + + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing OpGraphEndARM at end of module")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_group_test.cpp b/test/val/val_group_test.cpp new file mode 100644 index 0000000..4a78f54 --- /dev/null +++ b/test/val/val_group_test.cpp
@@ -0,0 +1,448 @@ +// Copyright 2026 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateGroup = spvtest::ValidateBase<bool>; + +std::string GenerateShaderCode(const std::string& body, bool is_64_bit = true) { + std::ostringstream ss; + ss << R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Groups +OpCapability Float64 +OpCapability Int64 +)"; + if (is_64_bit) { + ss << "OpMemoryModel Physical64 OpenCL"; + } else { + ss << "OpMemoryModel Physical32 OpenCL"; + } + ss << R"( +OpEntryPoint Kernel %main "main" +%bool = OpTypeBool +%float = OpTypeFloat 32 +%float64 = OpTypeFloat 64 +%uint = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%null_uint = OpConstantNull %uint +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint64_1 = OpConstant %uint64 1 +%float_2 = OpConstant %float 2 +%uint_array = OpTypeArray %uint %uint_2 +%void = OpTypeVoid +%event = OpTypeEvent +%null_event = OpConstantNull %event + +%workgroup_float_ptr = OpTypePointer Workgroup %float +%workgroup_float_var = OpVariable %workgroup_float_ptr Workgroup +%workgroup_bool_ptr = OpTypePointer Workgroup %bool +%workgroup_bool_var = OpVariable %workgroup_bool_ptr Workgroup +%cross_float_ptr = OpTypePointer CrossWorkgroup %float +%cross_float_var = OpVariable %cross_float_ptr CrossWorkgroup +%cross_uint_ptr = OpTypePointer CrossWorkgroup %uint +%cross_uint_var = OpVariable %cross_uint_ptr CrossWorkgroup +%uniform_float_ptr = OpTypePointer UniformConstant %float +%uniform_float_var = OpVariable %uniform_float_ptr UniformConstant +%func_event_ptr = OpTypePointer Function %event + +%fn = OpTypeFunction %void +%true = OpConstantTrue %bool +%main = OpFunction %void None %fn +%label = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + return ss.str(); +} + +TEST_F(ValidateGroup, AllAnyGood) { + const std::string ss = R"( + %x = OpGroupAll %bool %uint_2 %true + %y = OpGroupAny %bool %uint_2 %true + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateGroup, FloatGood) { + const std::string ss = R"( + %a = OpGroupFAdd %float %uint_2 Reduce %float_2 + %b = OpGroupFMin %float %uint_2 Reduce %float_2 + %c = OpGroupFMax %float %uint_2 Reduce %float_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateGroup, IntGood) { + const std::string ss = R"( + %a = OpGroupIAdd %uint %uint_2 Reduce %uint_2 + %b = OpGroupSMin %uint %uint_2 Reduce %uint_2 + %c = OpGroupSMax %uint %uint_2 Reduce %uint_2 + %d = OpGroupUMin %uint %uint_2 Reduce %uint_2 + %e = OpGroupUMax %uint %uint_2 Reduce %uint_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateGroup, BroadcastGood) { + const std::string ss = R"( + %a = OpGroupBroadcast %uint %uint_2 %uint_2 %uint_0 + %b = OpGroupBroadcast %float %uint_2 %float_2 %uint_0 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateGroup, AllResult) { + const std::string ss = R"( + %x = OpGroupAll %uint %uint_2 %true + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a boolean scalar type")); +} + +TEST_F(ValidateGroup, AllPredicate) { + const std::string ss = R"( + %x = OpGroupAll %bool %uint_2 %uint_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Predicate must be a boolean scalar type")); +} + +TEST_F(ValidateGroup, AnyResult) { + const std::string ss = R"( + %x = OpGroupAny %uint %uint_2 %true + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a boolean scalar type")); +} + +TEST_F(ValidateGroup, AnyPredicate) { + const std::string ss = R"( + %x = OpGroupAny %bool %uint_2 %uint_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Predicate must be a boolean scalar type")); +} + +TEST_F(ValidateGroup, FAddWithInt) { + const std::string ss = R"( + %a = OpGroupFAdd %uint %uint_2 Reduce %uint_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a scalar or vector of float type")); +} + +TEST_F(ValidateGroup, FMaxWidthMismatch) { + const std::string ss = R"( + %a = OpGroupFAdd %float64 %uint_2 Reduce %float_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of X must match the Result type")); +} + +TEST_F(ValidateGroup, IAddWithFloat) { + const std::string ss = R"( + %a = OpGroupIAdd %float %uint_2 Reduce %float_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a scalar or vector of integer type")); +} + +TEST_F(ValidateGroup, UMinWithArray) { + const std::string ss = R"( + %a = OpGroupUMin %uint_array %uint_2 Reduce %float_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a scalar or vector of integer type")); +} + +TEST_F(ValidateGroup, SMaxWidthMismatch) { + const std::string ss = R"( + %c = OpGroupSMax %uint64 %uint_2 Reduce %uint_2 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of X must match the Result type")); +} + +TEST_F(ValidateGroup, BroadcastArray) { + const std::string ss = R"( + %a = OpGroupBroadcast %uint_array %uint_2 %uint_2 %uint_0 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result must be a scalar or vector of integer, " + "floating-point, or boolean type")); +} + +TEST_F(ValidateGroup, BroadcastMismatch) { + const std::string ss = R"( + %b = OpGroupBroadcast %uint %uint_2 %float_2 %uint_0 + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of Value must match the Result type")); +} + +TEST_F(ValidateGroup, AsyncCopyWaitEventsGood) { + const std::string ss = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int64 + OpCapability Int8 + OpCapability Linkage + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %async_example "async_example" + OpExecutionMode %async_example ContractionOff + OpDecorate %26 Alignment 4 + OpDecorate %29 Alignment 8 + OpDecorate %async_example_local_data Alignment 4 + %float = OpTypeFloat 32 +%_ptr_CrossWorkgroup_float = OpTypePointer CrossWorkgroup %float + %void = OpTypeVoid + %5 = OpTypeFunction %void %_ptr_CrossWorkgroup_float +%spirv_Event = OpTypeEvent +%_ptr_Workgroup_float = OpTypePointer Workgroup %float + %ulong = OpTypeInt 64 0 + %uint = OpTypeInt 32 0 +%_ptr_Function_spirv_Event = OpTypePointer Function %spirv_Event + %uint_64 = OpConstant %uint 64 +%_arr_float_uint_64 = OpTypeArray %float %uint_64 +%_ptr_Workgroup__arr_float_uint_64 = OpTypePointer Workgroup %_arr_float_uint_64 + %ulong_1 = OpConstant %ulong 1 + %ulong_64 = OpConstant %ulong 64 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uchar = OpTypeInt 8 0 +%_ptr_Function_uchar = OpTypePointer Function %uchar +%async_example_local_data = OpVariable %_ptr_Workgroup__arr_float_uint_64 Workgroup + %24 = OpConstantNull %spirv_Event +%async_example = OpFunction %void None %5 + %26 = OpFunctionParameter %_ptr_CrossWorkgroup_float + %54 = OpLabel + %29 = OpVariable %_ptr_Function_spirv_Event Function + %30 = OpBitcast %_ptr_Workgroup_float %async_example_local_data + %31 = OpBitcast %_ptr_Function_uchar %29 + %32 = OpGroupAsyncCopy %spirv_Event %uint_2 %30 %26 %ulong_64 %ulong_1 %24 + OpStore %29 %32 Aligned 8 + OpGroupWaitEvents %uint_2 %uint_1 %29 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(ss); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateGroup, AsyncCopyResultType) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %uint %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The result type must be OpTypeEvent")); +} + +TEST_F(ValidateGroup, AsyncCopyDestinationPointer) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %null_uint %cross_float_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Destination to be a pointer")); +} + +TEST_F(ValidateGroup, AsyncCopyDestinationUniform) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %uniform_float_var %cross_float_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Destination to be a pointer with storage " + "class Workgroup or CrossWorkgroup")); +} + +TEST_F(ValidateGroup, AsyncCopyDestinationBool) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_bool_var %cross_float_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Destination to be a pointer to scalar or " + "vector of floating-point type or integer type")); +} + +TEST_F(ValidateGroup, AsyncCopyDestinationSourceTypes) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_uint_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Destination and Source to be the same type")); +} + +TEST_F(ValidateGroup, AsyncCopyBothWorkgroup) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %workgroup_float_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If Destination storage class is Workgroup, then the " + "Source storage class must be CrossWorkgroup.")); +} + +TEST_F(ValidateGroup, AsyncCopyBothCrossWorkgroup) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %cross_float_var %cross_float_var %uint64_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If Destination storage class is CrossWorkgroup, then " + "the Source storage class must be Workgroup")); +} + +TEST_F(ValidateGroup, AsyncCopyEventType) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint64_1 %null_uint + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Event to be type OpTypeEvent")); +} + +TEST_F(ValidateGroup, AsyncCopyNumElement32Bit) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NumElements must be a 64-bit int scalar when " + "Addressing Model is Physical64")); +} + +TEST_F(ValidateGroup, AsyncCopyStride32Bit) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Stride must be a 64-bit int scalar when Addressing " + "Model is Physical64")); +} + +TEST_F(ValidateGroup, AsyncCopyNumElement64Bit) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss, false)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NumElements must be a 32-bit int scalar when " + "Addressing Model is Physical32")); +} + +TEST_F(ValidateGroup, AsyncCopyStride64Bit) { + const std::string ss = R"( + %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint_1 %uint64_1 %null_event + )"; + CompileSuccessfully(GenerateShaderCode(ss, false)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Stride must be a 32-bit int scalar when Addressing " + "Model is Physical32")); +} + +TEST_F(ValidateGroup, GroupWaitEventsNumEvents) { + const std::string ss = R"( + %a = OpVariable %func_event_ptr Function + OpGroupWaitEvents %uint_2 %uint64_1 %a + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Num Events to be a 32-bit int scalar")); +} + +TEST_F(ValidateGroup, GroupWaitEventsEventList) { + const std::string ss = R"( + OpGroupWaitEvents %uint_2 %uint_1 %null_uint + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Events List to be a pointer")); +} + +TEST_F(ValidateGroup, GroupWaitEventsEventListType) { + const std::string ss = R"( + OpGroupWaitEvents %uint_2 %uint_1 %uniform_float_var + )"; + CompileSuccessfully(GenerateShaderCode(ss)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Events List to be a pointer to OpTypeEvent")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index 1e05018..824ac8d 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2015-2016 The Khronos Group Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -77,8 +79,7 @@ OpExtension "SPV_KHR_variable_pointers" )"; -std::string kGLSL450MemoryModel = - kOpCapabilitySetup + kOpVariablePtrSetUp + R"( +std::string kGLSL450MemoryModel = kOpCapabilitySetup + kOpVariablePtrSetUp + R"( OpMemoryModel Logical GLSL450 )"; @@ -707,6 +708,24 @@ "'2[%_ptr_UniformConstant_float]' is not a scalar type."))); } +TEST_P(ValidateIdWithMessage, OpTypeVectorComponentTypeCanBePointerType) { + std::string spirv = R"( +OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpCapability Int64 +OpCapability GenericPointer +OpCapability MaskedGatherScatterINTEL +OpExtension "SPV_INTEL_masked_gather_scatter" +OpMemoryModel Physical64 OpenCL + +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Generic %2 +%4 = OpTypeVector %3 4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeFloat 32 @@ -742,7 +761,8 @@ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(make_message( - "Having 8 components for TypeVector requires the Vector16 " + "Having 8 components for TypeVector requires the Vector16 or " + "LongVectorEXT " "capability\n %v8float = OpTypeVector %float 8\n"))); } @@ -757,6 +777,7 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr(make_message( "Having 16 components for TypeVector requires the Vector16 " + "or LongVectorEXT " "capability\n %v16float = OpTypeVector %float 16\n"))); } @@ -1141,9 +1162,9 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04667")); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr(make_message("OpTypeStruct must not contain an opaque type"))); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(make_message( + "OpTypeStruct must not contain an invalid opaque type"))); } TEST_P(ValidateIdWithMessage, OpTypePointerGood) { @@ -1602,21 +1623,6 @@ HasSubstr(make_message("Operand '1[%uint]' cannot be a " "type"))); } -TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) { - std::string spirv = kGLSL450MemoryModel + R"( -%1 = OpTypeInt 32 0 -%2 = OpConstant %1 4 -%3 = OpTypeArray %1 %2 -%4 = OpTypePointer Uniform %1 -%5 = OpVariable %4 Uniform -%6 = OpConstantComposite %3 %2 %2 %2 %5)"; - CompileSuccessfully(spirv.c_str()); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr(make_message( - "OpConstantComposite Constituent <id> '5[%5]' is not a " - "constant or undef."))); -} TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( %1 = OpTypeInt 32 0 @@ -1736,7 +1742,7 @@ %4 = OpConstantNull %3 %5 = OpTypeFloat 32 %6 = OpConstantNull %5 - %7 = OpTypePointer UniformConstant %3 + %7 = OpTypePointer Workgroup %3 %8 = OpConstantNull %7 %9 = OpTypeEvent %10 = OpConstantNull %9 @@ -1935,25 +1941,6 @@ "'2[%v4float]'s vector element type."))); } -// Invalid: Constituent is not a constant -TEST_P(ValidateIdWithMessage, - OpSpecConstantCompositeVectorConstituentNotConstantBad) { - std::string spirv = kGLSL450MemoryModel + R"( -%1 = OpTypeFloat 32 -%2 = OpTypeVector %1 4 -%3 = OpTypeInt 32 0 -%4 = OpSpecConstant %1 3.14 -%5 = OpTypePointer Uniform %1 -%6 = OpVariable %5 Uniform -%7 = OpSpecConstantComposite %2 %6 %4 %4 %4)"; - CompileSuccessfully(spirv.c_str()); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr(make_message( - "OpSpecConstantComposite Constituent <id> '6[%6]' is " - "not a constant or undef."))); -} - // Invalid: Vector contains a mix of Undef-int and Float. TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentUndefTypeBad) { @@ -2068,26 +2055,6 @@ "count."))); } -// Invalid: Composite contains a non-const/undef component -TEST_P(ValidateIdWithMessage, - OpSpecConstantCompositeMatrixConstituentNotConstBad) { - std::string spirv = kGLSL450MemoryModel + R"( - %1 = OpTypeFloat 32 - %2 = OpConstant %1 0.0 - %3 = OpTypeVector %1 4 - %4 = OpTypeMatrix %3 4 - %5 = OpSpecConstantComposite %3 %2 %2 %2 %2 - %6 = OpTypePointer Uniform %1 - %7 = OpVariable %6 Uniform - %8 = OpSpecConstantComposite %4 %5 %5 %5 %7)"; - CompileSuccessfully(spirv.c_str()); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr(make_message( - "OpSpecConstantComposite Constituent <id> '7[%7]' is " - "not a constant or undef."))); -} - // Invalid: Composite contains a column that is *not* a vector (it's an array) TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( @@ -2198,23 +2165,6 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -// Invalid: Array uses a type as operand. -TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) { - std::string spirv = kGLSL450MemoryModel + R"( -%1 = OpTypeInt 32 0 -%2 = OpConstant %1 4 -%3 = OpTypeArray %1 %2 -%4 = OpTypePointer Uniform %1 -%5 = OpVariable %4 Uniform -%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)"; - CompileSuccessfully(spirv.c_str()); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr(make_message( - "OpSpecConstantComposite Constituent <id> '5[%5]' is " - "not a constant or undef."))); -} - // Invalid: Array has a mix of Int and Float components. TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( @@ -2296,25 +2246,6 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -// Invalid: Composite contains non-const/undef component. -TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) { - std::string spirv = kGLSL450MemoryModel + R"( -%1 = OpTypeInt 32 0 -%2 = OpTypeInt 64 0 -%3 = OpTypeStruct %1 %1 %2 -%4 = OpSpecConstant %1 42 -%5 = OpUndef %2 -%6 = OpTypePointer Uniform %1 -%7 = OpVariable %6 Uniform -%8 = OpSpecConstantComposite %3 %4 %7 %5)"; - CompileSuccessfully(spirv.c_str()); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr(make_message( - "OpSpecConstantComposite Constituent <id> '7[%7]' is " - "not a constant or undef."))); -} - // Invalid: Struct component type does not match expected specialization type. // Second component was expected to be Int32, but got Int64. TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) { @@ -2372,12 +2303,18 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_P(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) { - std::string spirv = kGLSL450MemoryModel + R"( -%1 = OpTypeInt 32 0 -%2 = OpTypePointer Uniform %1 -%3 = OpVariable %2 Uniform -%4 = OpTypePointer Private %2 ; pointer to pointer -%5 = OpVariable %4 Private %3 + std::string spirv = kOpenCLMemoryModel64 + R"( +%2 = OpTypeInt 32 0 +%3 = OpTypePointer CrossWorkgroup %2 +%4 = OpVariable %3 CrossWorkgroup +%5 = OpTypePointer Function %3 ; pointer to pointer +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%6 = OpVariable %5 Function %4 +OpReturn +OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); @@ -2428,10 +2365,10 @@ } TEST_P(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) { - std::string spirv = kGLSL450MemoryModel + R"( + std::string spirv = kOpenCLMemoryModel64 + R"( %int = OpTypeInt 32 0 -%ptrint = OpTypePointer Uniform %int -%mvar = OpVariable %ptrint Uniform +%ptrint = OpTypePointer CrossWorkgroup %int +%mvar = OpVariable %ptrint CrossWorkgroup %ptrptrint = OpTypePointer Function %ptrint %void = OpTypeVoid %fnty = OpTypeFunction %void @@ -2469,7 +2406,8 @@ "be used with non-externally visible shader Storage Classes: " "Workgroup, CrossWorkgroup, Private, Function, Input, Output, " "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, " - "CallableDataKHR, IncomingCallableDataKHR, or UniformConstant"))); + "CallableDataKHR, IncomingCallableDataKHR, NodePayloadAMDX, or " + "UniformConstant"))); } TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) { @@ -2513,11 +2451,11 @@ TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) { std::string spirv = kGLSL450MemoryModel + R"( %bool = OpTypeBool -%boolptr = OpTypePointer Uniform %bool +%boolptr = OpTypePointer Workgroup %bool %int = OpTypeInt 32 0 %block = OpTypeStruct %boolptr %int -%_ptr_Uniform_block = OpTypePointer Uniform %block -%var = OpVariable %_ptr_Uniform_block Uniform +%_ptr_Private_block = OpTypePointer Private %block +%var = OpVariable %_ptr_Private_block Private %void = OpTypeVoid %fnty = OpTypeFunction %void %main = OpFunction %void None %fnty @@ -2646,7 +2584,8 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr(make_message( - "In Logical addressing, variables may not allocate a pointer type"))); + "In Logical addressing, variables can only allocate a workgroup " + "pointer if the VariablePointers capability is declared"))); } TEST_P(ValidateIdWithMessage, @@ -2724,8 +2663,8 @@ OpMemoryModel Logical GLSL450 %void = OpTypeVoid %int = OpTypeInt 32 0 -%_ptr_workgroup_int = OpTypePointer Workgroup %int -%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int +%_ptr_storagebuffer_int = OpTypePointer StorageBuffer %int +%_ptr_function_ptr = OpTypePointer Function %_ptr_storagebuffer_int %voidfn = OpTypeFunction %void %func = OpFunction %void None %voidfn %entry = OpLabel @@ -2772,6 +2711,11 @@ %_ptr_workgroup_int = OpTypePointer Workgroup %int %_ptr_uniform_ptr = OpTypePointer Uniform %_ptr_workgroup_int %var = OpVariable %_ptr_uniform_ptr Uniform +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpReturn +OpFunctionEnd )"; CompileSuccessfully(spirv); @@ -4012,6 +3956,7 @@ OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4021,8 +3966,7 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%float_entry = )" + - instr + +%float_entry = )" + instr + R"( %float %my_matrix )" + elem + R"(%int_0 %int_1 OpReturn @@ -4032,6 +3976,7 @@ const std::string expected_err = "The Result Type of " + instr + " <id> '36[%36]' must be " "OpTypePointer. Found OpTypeFloat."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4042,12 +3987,13 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%float_entry = )" + - instr + " %_ptr_Private_float %void " + elem + +%float_entry = )" + instr + + " %_ptr_Private_float %void " + elem + R"(%int_0 %int_1 OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%void]' cannot be a " @@ -4059,13 +4005,13 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_float %_ptr_Private_float )" + +%entry = )" + instr + R"( %_ptr_Private_float %_ptr_Private_float )" + elem + R"(%int_0 %int_1 OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -4078,8 +4024,8 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Function_float %my_matrix )" + elem + +%entry = )" + instr + R"( %_ptr_Function_float %my_matrix )" + + elem + R"(%int_0 %int_1 OpReturn OpFunctionEnd @@ -4087,6 +4033,7 @@ const std::string expected_err = "The result pointer storage class and base pointer storage class in " + instr + " do not match."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4099,8 +4046,8 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_float %my_float_var )" + elem + +%entry = )" + instr + R"( %_ptr_Private_float %my_float_var )" + + elem + R"(%int_0 OpReturn OpFunctionEnd @@ -4108,6 +4055,7 @@ const std::string expected_err = instr + " reached non-composite type while " "indexes still remain to be traversed."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4119,12 +4067,13 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_float %my_float_var )" + elem + +%entry = )" + instr + R"( %_ptr_Private_float %my_float_var )" + + elem + R"( OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4135,18 +4084,55 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + elem + +%entry = )" + instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + + elem + R"( OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("result type (OpTypeMatrix) does not match the type that " - "results from indexing into the base <id> (OpTypeFloat).")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("result type <id> '6[%mat4v3float]' (OpTypeMatrix) " + "does not match the type that results from indexing " + "into the base <id> '4[%float]' (OpTypeFloat)")); +} + +TEST_P(AccessChainInstructionTest, AccessChainDifferentIntTypes) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_uint_uint_32 ArrayStride 4 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 + %SSBO = OpTypeStruct %_arr_uint_uint_32 +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %4 + %6 = OpLabel + %18 = OpAccessChain %ptr_ssbo_int %_ %int_0 %int_0 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The types must be the exact same Id, so the two types " + "referenced are slighlty different")); } // Valid: 255 indexes passed to the access chain instruction. Limit is 255. @@ -4190,6 +4176,7 @@ OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4210,6 +4197,7 @@ )"; const std::string expected_err = "The number of indexes in " + instr + " may not exceed 255. Found 256 indexes."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4257,6 +4245,7 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; spvValidatorOptionsSetUniversalLimit( options_, spv_validator_limit_max_access_chain_indexes, 10u); CompileSuccessfully(spirv.str()); @@ -4279,6 +4268,7 @@ )"; const std::string expected_err = "The number of indexes in " + instr + " may not exceed 10. Found 11 indexes."; + getValidatorOptions()->relax_logical_pointer = true; spvValidatorOptionsSetUniversalLimit( options_, spv_validator_limit_max_access_chain_indexes, 10u); CompileSuccessfully(spirv.str()); @@ -4292,14 +4282,15 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_float %my_matrix )" + elem + +%entry = )" + instr + R"( %_ptr_Private_float %my_matrix )" + + elem + R"(%float_0 %int_1 OpReturn OpFunctionEnd )"; const std::string expected_err = "Indexes passed to " + instr + " must be of type integer."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4311,18 +4302,18 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%f = )" + - instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + +%f = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" + + elem + R"(%int_0 %spec_int %int_2 OpReturn OpFunctionEnd )"; - const std::string expected_err = - "The <id> passed to " + instr + - " to index into a structure must be an OpConstant."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("The <id> passed to " + instr)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("into a structure must be an OpConstant")); } // Invalid: Indexing up to a vec4 granularity, but result type expected float. @@ -4331,16 +4322,18 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + +%entry = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" + + elem + R"(%int_0 %int_1 %int_2 OpReturn OpFunctionEnd )"; - const std::string expected_err = instr + - " result type (OpTypeFloat) does not match " - "the type that results from indexing into " - "the base <id> (OpTypeVector)."; + const std::string expected_err = + instr + + " result type <id> '4[%float]' (OpTypeFloat) does not match the type " + "that results from indexing into the base <id> '18[%v4float]' " + "(OpTypeVector)."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4351,8 +4344,8 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + +%entry = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" + + elem + R"(%int_0 %int_2 %int_2 OpReturn OpFunctionEnd @@ -4360,6 +4353,7 @@ const std::string expected_err = instr + " reached non-composite type while " "indexes still remain to be traversed."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4370,16 +4364,17 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + +%entry = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" + + elem + R"(%int_3 %int_2 %int_2 OpReturn OpFunctionEnd )"; - const std::string expected_err = "Index is out of bounds: " + instr + + const std::string expected_err = "is out of bounds: " + instr + " cannot find index 3 into the structure " "<id> '25[%_struct_25]'. This structure " "has 3 members. Largest valid index is 2."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4414,6 +4409,7 @@ OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv.str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4426,12 +4422,13 @@ " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 "; std::string spirv = kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup + R"( -%runtime_arr_entry = )" + instr + - R"( %_ptr_Uniform_float %blockName_var )" + elem + +%runtime_arr_entry = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + R"(%int_2 %int_0 OpReturn OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -4450,6 +4447,7 @@ const std::string expected_err = instr + " reached non-composite type while indexes still remain to be traversed."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4461,8 +4459,8 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_float %my_matrix )" + elem + +%entry = )" + instr + R"( %_ptr_Private_float %my_matrix )" + + elem + R"(%int_0 %int_1 %int_0 OpReturn OpFunctionEnd @@ -4470,6 +4468,7 @@ const std::string expected_err = instr + " reached non-composite type while " "indexes still remain to be traversed."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4481,16 +4480,18 @@ const std::string instr = GetParam(); const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( -%entry = )" + - instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + elem + +%entry = )" + instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + + elem + R"(%int_0 %int_1 OpReturn OpFunctionEnd )"; - const std::string expected_err = instr + - " result type (OpTypeMatrix) does not match " - "the type that results from indexing into " - "the base <id> (OpTypeFloat)."; + const std::string expected_err = + instr + + " result type <id> '6[%mat4v3float]' (OpTypeMatrix) does not match the " + "type that results from indexing into the base <id> '4[%float]' " + "(OpTypeFloat)."; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); @@ -4935,6 +4936,40 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_P(ValidateIdWithMessage, OpVectorIdShuffleFloatGood) { + std::string spirv = "OpCapability LongVectorEXT\n" + kOpCapabilitySetup + + kOpVariablePtrSetUp + + "OpExtension \"SPV_EXT_long_vector\"" + R"( +OpMemoryModel Logical GLSL450 +%float = OpTypeFloat 32 +%uint = OpTypeInt 32 0 +%u2 = OpConstant %uint 2 +%u3 = OpConstant %uint 3 +%u4 = OpConstant %uint 4 +%vec2 = OpTypeVectorIdEXT %float %u2 +%vec3 = OpTypeVectorIdEXT %float %u3 +%vec4 = OpTypeVectorIdEXT %float %u4 +%ptr_vec2 = OpTypePointer Function %vec2 +%ptr_vec3 = OpTypePointer Function %vec3 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %vec2 %float_2 %float_1 +%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 +%3 = OpTypeFunction %vec4 +%4 = OpFunction %vec4 None %3 +%5 = OpLabel +%var = OpVariable %ptr_vec2 Function %1 +%var2 = OpVariable %ptr_vec3 Function %2 +%6 = OpLoad %vec2 %var +%7 = OpLoad %vec3 %var2 +%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff + OpReturnValue %8 + OpFunctionEnd)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_P(ValidateIdWithMessage, OpVectorShuffleScalarResultType) { std::string spirv = kGLSL450MemoryModel + R"( %float = OpTypeFloat 32 @@ -4955,7 +4990,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(make_message( - "Result Type of OpVectorShuffle must be OpTypeVector."))); + "Result Type of OpVectorShuffle must be a vector type."))); } TEST_P(ValidateIdWithMessage, OpVectorShuffleComponentCount) { @@ -5002,7 +5037,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr(make_message("The type of Vector 1 must be OpTypeVector."))); + HasSubstr(make_message("The type of Vector 1 must be a vector type."))); } TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2Type) { @@ -5025,7 +5060,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr(make_message("The type of Vector 2 must be OpTypeVector."))); + HasSubstr(make_message("The type of Vector 2 must be a vector type."))); } TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) { @@ -5762,7 +5797,7 @@ %2 = OpFunction %8 None %11 %4 = OpFunctionParameter %10 %15 = OpLabel -%16 = OpLoad %6 %3 Aligned 0 +%16 = OpLoad %6 %3 Aligned 1 %17 = OpCompositeExtract %5 %16 0 %18 = OpInBoundsPtrAccessChain %13 %4 %17 %12 OpStore %18 %14 Aligned 4 @@ -5796,7 +5831,7 @@ %2 = OpFunction %8 None %12 %4 = OpFunctionParameter %11 %17 = OpLabel -%18 = OpLoad %6 %3 Aligned 0 +%18 = OpLoad %6 %3 Aligned 1 %19 = OpCompositeExtract %5 %18 0 %20 = OpBitwiseAnd %5 %19 %13 %21 = OpPtrAccessChain %15 %4 %20 %14 @@ -5960,9 +5995,10 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr(make_message("SpecId decoration on target <id> " - "'1[%uint_3]' must be a scalar specialization " - "constant"))); + HasSubstr(make_message( + "SpecId decoration on target <id> " + "'1[%uint_3]' must be OpSpecConstantTrue, OpSpecConstantFalse, " + "OpSpecConstant, or OpSpecConstantDataKHR"))); } TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { @@ -5984,7 +6020,8 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' " - "must be a scalar specialization constant"))); + "must be OpSpecConstantTrue, OpSpecConstantFalse, " + "OpSpecConstant, or OpSpecConstantDataKHR"))); } TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { @@ -6005,7 +6042,8 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' " - "must be a scalar specialization constant"))); + "must be OpSpecConstantTrue, OpSpecConstantFalse, " + "OpSpecConstant, or OpSpecConstantDataKHR"))); } TEST_P(ValidateIdWithMessage, SpecIdTargetGood) { @@ -7174,6 +7212,126 @@ HasSubstr(make_message("ID '11[%11]' has not been defined"))); } +TEST_P(ValidateIdWithMessage, OpAliasScopeDeclINTELDoesNotRequireType) { + std::string spirv = R"( + OpCapability Shader + OpCapability MemoryAccessAliasingINTEL + OpExtension "SPV_INTEL_memory_access_aliasing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft +%alias_domain = OpAliasDomainDeclINTEL +%alias_scope = OpAliasScopeDeclINTEL %alias_domain +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%main = OpFunction %void None %func_type +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateIdWithMessage, OpAliasScopeListDeclINTELDoesNotRequireType) { + std::string spirv = R"( + OpCapability Shader + OpCapability MemoryAccessAliasingINTEL + OpExtension "SPV_INTEL_memory_access_aliasing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft +%alias_domain = OpAliasDomainDeclINTEL +%alias_scope1 = OpAliasScopeDeclINTEL %alias_domain +%alias_scope2 = OpAliasScopeDeclINTEL %alias_domain +%alias_list = OpAliasScopeListDeclINTEL %alias_scope1 %alias_scope2 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%main = OpFunction %void None %func_type +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Test that would have failed before the fix - showing what happens when +// these instructions reference operands without types +TEST_P(ValidateIdWithMessage, OpAliasScopeINTELWithNonTypedOperands) { + std::string spirv = R"( + OpCapability Shader + OpCapability MemoryAccessAliasingINTEL + OpExtension "SPV_INTEL_memory_access_aliasing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft +%alias_domain = OpAliasDomainDeclINTEL +%alias_scope = OpAliasScopeDeclINTEL %alias_domain +%alias_list = OpAliasScopeListDeclINTEL %alias_scope +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%main = OpFunction %void None %func_type +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateIdWithMessage, + OpLoadStoreWithAliasScopeINTELMaskMemoryOperands) { + std::string spirv = R"( + OpCapability Shader + OpCapability MemoryAccessAliasingINTEL + OpExtension "SPV_INTEL_memory_access_aliasing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft +%alias_domain = OpAliasDomainDeclINTEL +%alias_scope = OpAliasScopeDeclINTEL %alias_domain +%alias_list = OpAliasScopeListDeclINTEL %alias_scope +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%ptr_uint = OpTypePointer Function %uint +%func_type = OpTypeFunction %void +%main = OpFunction %void None %func_type +%entry = OpLabel +%var = OpVariable %ptr_uint Function +%val = OpLoad %uint %var Aligned|AliasScopeINTELMask 4 %alias_list +OpStore %var %val Aligned|NoAliasINTELMask 4 %alias_list +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateIdWithMessage, OpDecorateIdAfterAliasScopeListDeclINTEL) { + std::string spirv = R"( + OpCapability Shader + OpCapability MemoryAccessAliasingINTEL + OpExtension "SPV_INTEL_memory_access_aliasing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft +%alias_domain = OpAliasDomainDeclINTEL +%alias_scope1 = OpAliasScopeDeclINTEL %alias_domain +%alias_scope2 = OpAliasScopeDeclINTEL %alias_domain +%alias_list = OpAliasScopeListDeclINTEL %alias_scope1 %alias_scope2 +OpDecorateId %main NoAliasINTEL %alias_list +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%main = OpFunction %void None %func_type +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + INSTANTIATE_TEST_SUITE_P(, ValidateIdWithMessage, ::testing::Bool()); } // namespace
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 93fab04..15880c9 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp
@@ -20,6 +20,7 @@ #include <string> #include "gmock/gmock.h" +#include "spirv-tools/libspirv.h" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h" @@ -49,7 +50,9 @@ OpCapability MinLod OpCapability Sampled1D OpCapability ImageQuery +OpCapability Int16 OpCapability Int64 +OpCapability Float16 OpCapability Float64 OpCapability SparseResidency OpCapability ImageBuffer @@ -82,7 +85,7 @@ %uniform_sampler %private_image_u32_buffer_0002_r32ui %private_image_u32_spd_0002 -%private_image_f32_buffer_0002_r32ui +%private_image_f32_buffer_0002_r32f %input_flat_u32 )"; @@ -115,6 +118,8 @@ OpDecorate %uniform_image_f32_2d_0002 Binding 3 OpDecorate %uniform_image_s32_2d_0002 DescriptorSet 1 OpDecorate %uniform_image_s32_2d_0002 Binding 4 +OpDecorate %uniform_image_u32_3d_0001 DescriptorSet 1 +OpDecorate %uniform_image_u32_3d_0001 Binding 5 OpDecorate %uniform_image_f32_spd_0002 DescriptorSet 2 OpDecorate %uniform_image_f32_spd_0002 Binding 0 OpDecorate %uniform_image_f32_3d_0111 DescriptorSet 2 @@ -123,6 +128,8 @@ OpDecorate %uniform_image_f32_cube_0101 Binding 2 OpDecorate %uniform_image_f32_cube_0102_rgba32f DescriptorSet 2 OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3 +OpDecorate %uniform_image_f32_3d_0001 DescriptorSet 2 +OpDecorate %uniform_image_f32_3d_0001 Binding 4 OpDecorate %uniform_sampler DescriptorSet 3 OpDecorate %uniform_sampler Binding 0 OpDecorate %input_flat_u32 Flat @@ -134,12 +141,15 @@ %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool +%f16 = OpTypeFloat 16 %f32 = OpTypeFloat 32 %f64 = OpTypeFloat 64 +%u16 = OpTypeInt 16 0 %u32 = OpTypeInt 32 0 %s32 = OpTypeInt 32 1 %u64 = OpTypeInt 64 0 %s64 = OpTypeInt 64 1 +%f16vec2 = OpTypeVector %f16 2 %s32vec2 = OpTypeVector %s32 2 %u32vec2 = OpTypeVector %u32 2 %f32vec2 = OpTypeVector %f32 2 @@ -151,6 +161,8 @@ %f32vec4 = OpTypeVector %f32 4 %boolvec4 = OpTypeVector %bool 4 +%f16_0 = OpConstant %f16 0 + %f32_0 = OpConstant %f32 0 %f32_1 = OpConstant %f32 1 %f32_0_5 = OpConstant %f32 0.5 @@ -167,6 +179,8 @@ %s32_4 = OpConstant %s32 4 %s32_m1 = OpConstant %s32 -1 +%u16_0 = OpConstant %u16 0 + %u32_0 = OpConstant %u32 0 %u32_1 = OpConstant %u32 1 %u32_2 = OpConstant %u32 2 @@ -182,6 +196,7 @@ %u32vec2arr3 = OpTypeArray %u32vec2 %u32_3 %u32arr4 = OpTypeArray %u32 %u32_4 %u32vec3arr4 = OpTypeArray %u32vec3 %u32_4 +%f16vec2arr4 = OpTypeArray %f16vec2 %u32_4 %struct_u32_f32vec4 = OpTypeStruct %u32 %f32vec4 %struct_u64_f32vec4 = OpTypeStruct %u64 %f32vec4 @@ -195,6 +210,7 @@ %struct_u32_f32vec4_u32 = OpTypeStruct %u32 %f32vec4 %u32 %struct_u32_u32arr4 = OpTypeStruct %u32 %u32arr4 +%u32vec2_00 = OpConstantComposite %u32vec2 %u32_0 %u32_0 %u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 %u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 %u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 @@ -209,6 +225,8 @@ %s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 %s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 +%f16vec2_00 = OpConstantComposite %f16vec2 %f16_0 %f16_0 + %f32vec2_00 = OpConstantComposite %f32vec2 %f32_0 %f32_0 %f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 %f32vec2_10 = OpConstantComposite %f32vec2 %f32_1 %f32_0 @@ -226,6 +244,7 @@ %const_offsets3x2 = OpConstantComposite %u32vec2arr3 %u32vec2_01 %u32vec2_12 %u32vec2_01 %const_offsets4xu = OpConstantComposite %u32arr4 %u32_0 %u32_0 %u32_0 %u32_0 %const_offsets4x3 = OpConstantComposite %u32vec3arr4 %u32vec3_012 %u32vec3_012 %u32vec3_012 %u32vec3_012 +%const_offsets4f16 = OpConstantComposite %f16vec2arr4 %f16vec2_00 %f16vec2_00 %f16vec2_00 %f16vec2_00 %type_image_f32_1d_0001 = OpTypeImage %f32 1D 0 0 0 1 Unknown %ptr_image_f32_1d_0001 = OpTypePointer UniformConstant %type_image_f32_1d_0001 @@ -311,10 +330,10 @@ %ptr_image_u32_spd_0002 = OpTypePointer Private %type_image_u32_spd_0002 %private_image_u32_spd_0002 = OpVariable %ptr_image_u32_spd_0002 Private -%type_image_f32_buffer_0002_r32ui = OpTypeImage %f32 Buffer 0 0 0 2 R32ui +%type_image_f32_buffer_0002_r32f = OpTypeImage %f32 Buffer 0 0 0 2 R32f %ptr_Image_f32 = OpTypePointer Image %f32 -%ptr_image_f32_buffer_0002_r32ui = OpTypePointer Private %type_image_f32_buffer_0002_r32ui -%private_image_f32_buffer_0002_r32ui = OpVariable %ptr_image_f32_buffer_0002_r32ui Private +%ptr_image_f32_buffer_0002_r32f = OpTypePointer Private %type_image_f32_buffer_0002_r32f +%private_image_f32_buffer_0002_r32f = OpVariable %ptr_image_f32_buffer_0002_r32f Private %ptr_input_flat_u32 = OpTypePointer Input %u32 %input_flat_u32 = OpVariable %ptr_input_flat_u32 Input @@ -367,6 +386,7 @@ OpCapability ImageGatherExtended OpCapability InputAttachment OpCapability SampledRect +OpCapability Int16 )"; ss << capabilities_and_extensions; @@ -376,6 +396,7 @@ %func = OpTypeFunction %void %bool = OpTypeBool %f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 %u32 = OpTypeInt 32 0 %u32vec2 = OpTypeVector %u32 2 %f32vec2 = OpTypeVector %f32 2 @@ -390,6 +411,8 @@ %f32_0_25 = OpConstant %f32 0.25 %f32_0_75 = OpConstant %f32 0.75 +%u16_0 = OpConstant %u16 0 + %u32_0 = OpConstant %u32 0 %u32_1 = OpConstant %u32 1 %u32_2 = OpConstant %u32 2 @@ -1074,6 +1097,258 @@ "Image Dim TileImageDataEXT cannot be used with ImageSparseRead")); } +TEST_F(ValidateImage, ColorAttachmentReadEXTWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002 +%res1 = OpColorAttachmentReadEXT %bool %img +)"; + + const std::string decl = R"( +%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown +%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002 +%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageColorReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Result Type to be int or float scalar or vector type")); +} + +TEST_F(ValidateImage, ColorAttachmentReadEXTWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002 +%res1 = OpColorAttachmentReadEXT %f64 %img +)"; + + const std::string decl = R"( +%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown +%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002 +%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageColorReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, ColorAttachmentReadEXTWrongAttachment) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_0002 +%res1 = OpColorAttachmentReadEXT %f32 %img +)"; + + const std::string decl = R"( +%ptr_image_f32_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002 +%uniform_image_f32_0002 = OpVariable %ptr_image_f32_0002 UniformConstant +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageColorReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image 'Dim' must be TileImageDataEXT")); +} + +TEST_F(ValidateImage, ColorAttachmentReadEXTWrongSample) { + const std::string body = R"( +%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002 +%res1 = OpColorAttachmentReadEXT %f32 %img %f32_0 +)"; + + const std::string decl = R"( +%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown +%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002 +%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageColorReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample to be a 32-bit int scalar")); +} + +TEST_F(ValidateImage, ColorAttachmentReadEXTWrongExecutionModel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002 +%res1 = OpColorAttachmentReadEXT %f32 %img +)"; + + const std::string decl = R"( +%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown +%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002 +%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageColorReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ColorAttachmentReadEXT requires Fragment execution model")); +} + +TEST_F(ValidateImage, DepthAttachmentReadEXTWrongResultType) { + const std::string body = R"( +%res1 = OpDepthAttachmentReadEXT %s32 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageDepthReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", "") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Result Type to be a 32-bit floating-point type scalar")); +} + +TEST_F(ValidateImage, DepthAttachmentReadEXTWrongSample) { + const std::string body = R"( +%res1 = OpDepthAttachmentReadEXT %f32 %f32_0 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageDepthReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", "") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample to be a 32-bit int scalar")); +} + +TEST_F(ValidateImage, DepthAttachmentReadEXTWrongExecutionModel) { + const std::string body = R"( +%res1 = OpDepthAttachmentReadEXT %f32 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageDepthReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", "") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("DepthAttachmentReadEXT requires Fragment execution model")); +} + +TEST_F(ValidateImage, StencilAttachmentReadEXTWrongResultType) { + const std::string body = R"( +%res1 = OpStencilAttachmentReadEXT %f32 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageStencilReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", "") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a 32-bit integer type scalar")); +} + +TEST_F(ValidateImage, StencilAttachmentReadEXTWrongSample) { + const std::string body = R"( +%res1 = OpStencilAttachmentReadEXT %u32 %f32_0 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageStencilReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", "") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample to be a 32-bit int scalar")); +} + +TEST_F(ValidateImage, StencilAttachmentReadEXTWrongExecutionModel) { + const std::string body = R"( +%res1 = OpStencilAttachmentReadEXT %u32 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability TileImageStencilReadAccessEXT +OpExtension "SPV_EXT_shader_tile_image" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex", "", + SPV_ENV_UNIVERSAL_1_5, "GLSL450", "") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("StencilAttachmentReadEXT requires Fragment execution model")); +} + TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) { const std::string code = GetKernelHeader() + R"( %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly @@ -1412,14 +1687,14 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) { const std::string body = R"( -%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %type_image_f32_buffer_0002_r32ui %u32_0 %u32_0 +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %type_image_f32_buffer_0002_r32f %u32_0 %u32_0 %sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand '148[%148]' cannot be a " + HasSubstr("Operand '157[%157]' cannot be a " "type")); } @@ -1465,14 +1740,15 @@ TEST_F(ValidateImage, ImageTexelPointerImageCoordTypeBad) { const std::string body = R"( -%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32ui %f32_0 %f32_0 +%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32f %f32_0 %f32_0 %sum = OpAtomicIAdd %f32 %texel_ptr %f32_1 %f32_0 %f32_1 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be integer scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit integer scalar or vector")); } TEST_F(ValidateImage, ImageTexelPointerImageCoordSizeBad) { @@ -1533,7 +1809,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -1629,8 +1906,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleImplicitLodCoordinateSizeTooSmall) { @@ -1667,7 +1945,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -1792,7 +2071,22 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + HasSubstr("Expected Coordinate to be a 32-bit integer or float " + "scalar or vector")); +} + +TEST_F(ValidateImage, SampleExplicitLodWrongCoordinateType16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f16vec2_00 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit scalar or vector")); } TEST_F(ValidateImage, SampleExplicitLodCoordinateSizeTooSmall) { @@ -1867,9 +2161,25 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand Lod to be a 32-bit float scalar when " + "used with ExplicitLod")); +} + +TEST_F(ValidateImage, LodWithHalf) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod %f16_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand Lod to be float scalar when " - "used with ExplicitLod")); + HasSubstr("Expected Image Operand Lod to be a 32-bit float " + "scalar when used with ExplicitLod")); } TEST_F(ValidateImage, LodWrongDim) { @@ -1886,6 +2196,18 @@ "2D, 3D or Cube")); } +TEST_F(ValidateImage, LodWrongMultisample) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 Lod|Sample %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod requires 'MS' parameter to be 0")); +} + TEST_F(ValidateImage, MinLodIncompatible) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1943,8 +2265,24 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand Bias to be float scalar")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand Bias to be a 32-bit float scalar")); +} + +TEST_F(ValidateImage, SampleImplicitLodBias16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Bias %f16_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand Bias to be a 32-bit float scalar")); } TEST_F(ValidateImage, SampleImplicitLodBiasWrongDim) { @@ -1972,9 +2310,10 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected both Image Operand Grad ids to be float " - "scalars or vectors")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected both Image Operand Grad ids to be 32-bit float " + "scalars or vectors")); } TEST_F(ValidateImage, SampleExplicitLodGradDyWrongType) { @@ -1987,9 +2326,25 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected both Image Operand Grad ids to be 32-bit float " + "scalars or vectors")); +} + +TEST_F(ValidateImage, SampleExplicitLodGrad16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f16vec2_00 %f32vec2_00 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected both Image Operand Grad ids to be float " - "scalars or vectors")); + HasSubstr("Expected both Image Operand Grad ids to be 32-bit " + "float scalars or vectors")); } TEST_F(ValidateImage, SampleExplicitLodGradDxWrongSize) { @@ -2050,10 +2405,24 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Expected Image Operand ConstOffset to be int scalar or vector")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffset to be a 32-bit int " + "scalar or vector")); +} + +TEST_F(ValidateImage, SampleImplicitLodConstOffset16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_00 ConstOffset %u16_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffset to be a 32-bit int " + "scalar or vector")); } TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongSize) { @@ -2114,7 +2483,24 @@ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Expected Image Operand Offset to be int scalar or vector")); + HasSubstr( + "Expected Image Operand Offset to be a 32-bit int scalar or vector")); +} + +TEST_F(ValidateImage, SampleImplicitLodOffset16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %u16_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand Offset to be a 32-bit int scalar or vector")); } TEST_F(ValidateImage, SampleImplicitLodOffsetWrongSize) { @@ -2144,13 +2530,26 @@ CompileSuccessfully( GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-Offset-04663")); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-Offset-10213")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Image Operand Offset can only be used with " "OpImage*Gather operations")); } +TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetMaintenance8) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + spvValidatorOptionsSetAllowOffsetTextureOperand(getValidatorOptions(), true); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongBeforeLegalization) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -2208,8 +2607,24 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand MinLod to be float scalar")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand MinLod to be a 32-bit float scalar")); +} + +TEST_F(ValidateImage, SampleImplicitLodMinLod16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 MinLod %f16_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand MinLod to be a 32-bit float scalar")); } TEST_F(ValidateImage, SampleImplicitLodMinLodWrongDim) { @@ -2246,7 +2661,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -2263,6 +2679,20 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateImage, GradWrongMultisample) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Grad %f32vec2_01 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleProjExplicitLodWrongResultType) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -2355,8 +2785,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleProjExplicitLodCoordinateSizeTooSmall) { @@ -2394,7 +2825,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -2490,8 +2922,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleProjImplicitLodCoordinateSizeTooSmall) { @@ -2529,7 +2962,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -2615,8 +3049,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleDrefImplicitLodCoordinateSizeTooSmall) { @@ -2685,7 +3120,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -2771,8 +3207,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleDrefExplicitLodCoordinateSizeTooSmall) { @@ -2824,7 +3261,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -2910,8 +3348,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleProjDrefImplicitLodCoordinateSizeTooSmall) { @@ -2962,7 +3401,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -3048,8 +3488,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, SampleProjDrefExplicitLodCoordinateSizeTooSmall) { @@ -3080,7 +3521,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -3097,7 +3539,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -3210,8 +3653,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be int scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit integer scalar or vector")); } TEST_F(ValidateImage, FetchCoordinateSizeTooSmall) { @@ -3235,9 +3679,26 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand Lod to be int scalar when used " - "with OpImageFetch")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand Lod to be a 32-bit int scalar when used " + "with OpImageFetch")); +} + +TEST_F(ValidateImage, FetchLod16Int) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 Lod %u16_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand Lod to be a 32-bit int scalar when used " + "with OpImageFetch")); } TEST_F(ValidateImage, FetchMultisampledMissingSample) { @@ -3271,7 +3732,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -3367,8 +3829,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, GatherCoordinateSizeTooSmall) { @@ -3508,7 +3971,7 @@ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Image Operand ConstOffsets array components " - "to be int vectors of size 2")); + "to be a 32-bit int vectors of size 2")); } TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) { @@ -3523,7 +3986,22 @@ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Image Operand ConstOffsets array components " - "to be int vectors of size 2")); + "to be a 32-bit int vectors of size 2")); +} + +TEST_F(ValidateImage, GatherConstOffsetsArrayVector16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets4f16 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffsets array components " + "to be a 32-bit int vectors of size 2")); } TEST_F(ValidateImage, GatherConstOffsetsArrayNotConst) { @@ -3575,7 +4053,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -3675,7 +4154,7 @@ TEST_F(ValidateImage, ReadSuccess4) { const std::string body = R"( %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 -%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +%res1 = OpImageRead %f32vec4 %img %u32vec2_00 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -3842,7 +4321,7 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateImage, ReadWrongCoordinateType) { +TEST_F(ValidateImage, ReadFloatCoordinateType) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %f32vec2_00 @@ -3850,9 +4329,20 @@ const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 +%res1 = OpImageRead %u32vec4 %img %f16vec2_00 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be int scalar or vector")); + HasSubstr("Expected Coordinate to be a 32-bit scalar or vector")); } TEST_F(ValidateImage, ReadCoordinateSizeTooSmall) { @@ -4016,7 +4506,7 @@ HasSubstr("Expected Image 'Sampled' parameter to be 0 or 2")); } -TEST_F(ValidateImage, WriteWrongCoordinateType) { +TEST_F(ValidateImage, WriteFloatCoordinateType) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %f32vec2_00 %u32vec4_0123 @@ -4024,9 +4514,20 @@ const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, WriteWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 +OpImageWrite %img %f16vec2_00 %u32vec4_0123 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be int scalar or vector")); + HasSubstr("Expected Coordinate to be a 32-bit scalar or vector")); } TEST_F(ValidateImage, WriteCoordinateSizeTooSmall) { @@ -4115,8 +4616,34 @@ declarations) .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand Sample to be int scalar")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand Sample to be a 32-bit int scalar")); +} + +TEST_F(ValidateImage, WriteSample16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0012 %uniform_image_f32_2d_0012 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u16_0 +)"; + + const std::string extra = R"( + OpCapability StorageImageWriteWithoutFormat + OpCapability StorageImageMultisample + )"; + const std::string declarations = R"( +%type_image_f32_2d_0012 = OpTypeImage %f32 2D 0 0 1 2 Unknown +%ptr_image_f32_2d_0012 = OpTypePointer UniformConstant %type_image_f32_2d_0012 +%uniform_image_f32_2d_0012 = OpVariable %ptr_image_f32_2d_0012 UniformConstant + )"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_0, "GLSL450", + declarations) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand Sample to be a 32-bit int scalar")); } TEST_F(ValidateImage, WriteSampleNotMultisampled) { @@ -4421,7 +4948,19 @@ CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Level of Detail to be int scalar")); + HasSubstr("Expected Level of Detail to be a 32-bit int scalar")); +} + +TEST_F(ValidateImage, QuerySizeLodWrong16Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySizeLod %u32vec2 %img %u16_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Level of Detail to be a 32-bit int scalar")); } TEST_F(ValidateImage, QuerySizeSuccess) { @@ -4520,13 +5059,27 @@ %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler %res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh -%res2 = OpImageQueryLod %f32vec2 %simg %u32vec2_01 )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateImage, QueryLodNonFloatKernel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); +} + TEST_F(ValidateImage, QueryLodSuccessShader) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -4604,8 +5157,9 @@ CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Coordinate to be float scalar or vector")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector")); } TEST_F(ValidateImage, QueryLodCoordinateSizeTooSmall) { @@ -5011,7 +5565,7 @@ TEST_F(ValidateImage, ReadSubpassDataWrongExecutionModel) { const std::string body = R"( %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 -%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +%res1 = OpImageRead %f32vec4 %img %u32vec2_00 )"; const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; @@ -5041,7 +5595,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5169,7 +5724,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5263,7 +5819,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5480,7 +6037,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5621,7 +6179,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5638,7 +6197,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5657,7 +6217,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( @@ -5679,7 +6240,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), @@ -5700,7 +6262,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5719,7 +6282,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), @@ -5740,7 +6304,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), @@ -5761,7 +6326,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( @@ -5784,7 +6350,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5801,7 +6368,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( @@ -5824,7 +6392,8 @@ )"; CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") - .c_str()); + .c_str(), + SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } @@ -5967,7 +6536,7 @@ EXPECT_THAT(getDiagnosticString(), Eq("")); } -TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14Good) { +TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14) { // Zeroed int sampled type const std::string body = R"( %img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 @@ -5978,8 +6547,11 @@ CompileSuccessfully( GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), SPV_ENV_UNIVERSAL_1_4); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT(getDiagnosticString(), Eq("")); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Using ZeroExtend, but result type is a signed integer type.")); } TEST_F(ValidateImage, ZeroExtendScalarVectorUIntTexelV14Good) { @@ -5996,7 +6568,7 @@ EXPECT_THAT(getDiagnosticString(), Eq("")); } -TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) { +TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14) { const std::string body = R"( %img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 %res1 = OpImageRead %s32vec4 %img %u32vec2_01 ZeroExtend @@ -6006,8 +6578,11 @@ CompileSuccessfully( GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), SPV_ENV_UNIVERSAL_1_4); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT(getDiagnosticString(), Eq("")); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Using ZeroExtend, but result type is a signed integer type.")); } TEST_F(ValidateImage, ReadLodAMDSuccess1) { @@ -6407,20 +6982,13 @@ } TEST_F(ValidateImage, ImageTexelPointerR32fSuccessVulkan) { - const std::string& declarations = R"( -%type_image_f32_buffer_0002_r32f = OpTypeImage %f32 Buffer 0 0 0 2 R32f -%ptr_image_f32_buffer_0002_r32f = OpTypePointer Private %type_image_f32_buffer_0002_r32f -%private_image_f32_buffer_0002_r32f = OpVariable %ptr_image_f32_buffer_0002_r32f Private -)"; - const std::string body = R"( %texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32f %u32_0 %u32_0 )"; spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully( - GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450", declarations) - .c_str(), + GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450").c_str(), env); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); } @@ -6447,19 +7015,19 @@ AnyVUID("VUID-StandaloneSpirv-OpImageTexelPointer-04658")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected the Image Format in Image to be R64i, R64ui, " - "R32f, R32i, or R32ui for Vulkan environment")); + "R32f, R32i, or R32ui for Vulkan environment using " + "OpImageTexelPointer")); } TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) { const std::string& declarations = R"( -%type_image_s32_buffer_0002_rgba16f = OpTypeImage %s32 Buffer 0 0 0 2 Rgba16f -%ptr_Image_s32 = OpTypePointer Image %s32 -%ptr_image_s32_buffer_0002_rgba16f = OpTypePointer Private %type_image_s32_buffer_0002_rgba16f -%private_image_s32_buffer_0002_rgba16f = OpVariable %ptr_image_s32_buffer_0002_rgba16f Private +%type_image_f32_buffer_0002_rgba16f = OpTypeImage %f32 Buffer 0 0 0 2 Rgba16f +%ptr_image_f32_buffer_0002_rgba16f = OpTypePointer Private %type_image_f32_buffer_0002_rgba16f +%private_image_f32_buffer_0002_rgba16f = OpVariable %ptr_image_f32_buffer_0002_rgba16f Private )"; const std::string body = R"( -%texel_ptr = OpImageTexelPointer %ptr_Image_s32 %private_image_s32_buffer_0002_rgba16f %u32_0 %u32_0 +%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_rgba16f %u32_0 %u32_0 )"; spv_target_env env = SPV_ENV_VULKAN_1_0; @@ -6472,7 +7040,8 @@ AnyVUID("VUID-StandaloneSpirv-OpImageTexelPointer-04658")); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected the Image Format in Image to be R64i, R64ui, " - "R32f, R32i, or R32ui for Vulkan environment")); + "R32f, R32i, or R32ui for Vulkan environment using " + "OpImageTexelPointer")); } TEST_F(ValidateImage, ImageExecutionModeLimitationNoMode) { @@ -10897,6 +11466,543 @@ HasSubstr("Expected Result Type to be a pointer")); } +TEST_F(ValidateImage, TileImageNotFragment) { + const std::string body = R"( + OpCapability Shader + OpCapability TileImageColorReadAccessEXT + OpExtension "SPV_EXT_shader_tile_image" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %ptr = OpTypePointer TileImageEXT %v4float + %var = OpVariable %ptr TileImageEXT + %main = OpFunction %void None %func + %label = OpLabel + %val = OpLoad %v4float %var + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-08720")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "TileImageEXT Storage Class is limited to Fragment execution model")); +} + +TEST_F(ValidateImage, SubpassDataNonZero) { + const std::string body = R"( + OpCapability Shader + OpCapability InputAttachment + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outColor %inputColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %outColor Location 0 + OpDecorate %inputColor Binding 0 + OpDecorate %inputColor DescriptorSet 0 + OpDecorate %inputColor InputAttachmentIndex 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %outColor = OpVariable %_ptr_Output_v4float Output + %11 = OpTypeImage %float SubpassData 0 0 0 2 Unknown +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %inputColor = OpVariable %_ptr_UniformConstant_11 UniformConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %v2int = OpTypeVector %int 2 + %v2int_1 = OpConstantComposite %v2int %int_1 %int_1 + %main = OpFunction %void None %4 + %6 = OpLabel + %14 = OpLoad %11 %inputColor + %19 = OpImageRead %v4float %14 %v2int_1 + OpStore %outColor %19 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-SubpassData-04660")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate for a SubpassData image to be a " + "OpConstantComposite of (0,0) or OpConstantNull")); +} + +TEST_F(ValidateImage, SubpassDataNonConstant) { + const std::string body = R"( + OpCapability Shader + OpCapability InputAttachment + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_ %outColor %inputColor + OpExecutionMode %main OriginUpperLeft + OpDecorate %UBO Block + OpMemberDecorate %UBO 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + OpDecorate %outColor Location 0 + OpDecorate %inputColor Binding 0 + OpDecorate %inputColor DescriptorSet 0 + OpDecorate %inputColor InputAttachmentIndex 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint + %UBO = OpTypeStruct %v2uint +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO + %_ = OpVariable %_ptr_Uniform_UBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v2uint = OpTypePointer Uniform %v2uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %outColor = OpVariable %_ptr_Output_v4float Output + %23 = OpTypeImage %float SubpassData 0 0 0 2 Unknown +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 + %inputColor = OpVariable %_ptr_UniformConstant_23 UniformConstant + %v2int = OpTypeVector %int 2 + %main = OpFunction %void None %4 + %6 = OpLabel + %x = OpVariable %_ptr_Function_v2uint Function + %17 = OpAccessChain %_ptr_Uniform_v2uint %_ %int_0 + %18 = OpLoad %v2uint %17 + %26 = OpLoad %23 %inputColor + %29 = OpImageRead %v4float %26 %18 + OpStore %outColor %29 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-SubpassData-04660")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate for a SubpassData image to be a " + "OpConstantComposite of (0,0) or OpConstantNull")); +} + +// https://gitlab.khronos.org/spirv/SPIR-V/-/issues/766 +TEST_F(ValidateImage, WriteSignedExtendGood) { + const std::string body = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %ii2D %ic2D + OpExecutionMode %main OriginUpperLeft + OpDecorate %ii2D Binding 0 + OpDecorate %ii2D DescriptorSet 0 + OpDecorate %ic2D Flat + OpDecorate %ic2D Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %8 = OpTypeImage %int 2D 0 0 0 2 R32i +%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8 + %ii2D = OpVariable %_ptr_UniformConstant_8 UniformConstant + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %14 = OpConstantComposite %v2int %int_0 %int_0 + %v4int = OpTypeVector %int 4 + %16 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 +%_ptr_Input_v2int = OpTypePointer Input %v2int + %ic2D = OpVariable %_ptr_Input_v2int Input + %main = OpFunction %void None %4 + %6 = OpLabel + %11 = OpLoad %8 %ii2D + OpImageWrite %11 %14 %16 SignExtend + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageNotFloat) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %f32 2D 0 0 0 2 R32i +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Format type does not match Sample Type operand (float)")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageNotInt) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %s32 2D 0 0 0 2 R32f +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Format type does not match Sample Type operand (integer)")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageNot64Width) { + const std::string code = GetShaderHeader( + "OpCapability Int64ImageEXT\nOpExtension " + "\"SPV_EXT_shader_image_int64\"\n") + + R"( +%img_type = OpTypeImage %s64 2D 0 0 0 2 R32i +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Format width does not match Sample Type operand " + "(bit width of 64)")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageNot32Width) { + const std::string code = GetShaderHeader( + "OpCapability Int64ImageEXT\nOpExtension " + "\"SPV_EXT_shader_image_int64\"\n") + + R"( +%img_type = OpTypeImage %s32 2D 0 0 0 2 R64i +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Format width does not match Sample Type operand " + "(bit width of 32)")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageNot64Signedness) { + const std::string code = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Int64ImageEXT + OpExtension "SPV_EXT_shader_image_int64" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %image_var + OpExecutionMode %main OriginUpperLeft + OpDecorate %image_var Location 0 + %void = OpTypeVoid + %func = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %s64 = OpTypeInt 64 1 + %u32_0 = OpConstant %u32 0 + %u32_1 = OpConstant %u32 1 + %u32vec2 = OpTypeVector %u32 2 + %s64vec4 = OpTypeVector %s64 4 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + %img_type = OpTypeImage %s64 2D 0 0 0 1 R64ui +%ptr_image = OpTypePointer Input %img_type +%image_var = OpVariable %ptr_image Input + %main = OpFunction %void None %func + %label = OpLabel + %img = OpLoad %img_type %image_var + %res1 = OpImageFetch %s64vec4 %img %u32vec2_01 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Format signedness (unsigned) does not match " + "Sample Type operand")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageNot32Signedness) { + const std::string code = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %image_var + OpExecutionMode %main OriginUpperLeft + OpDecorate %image_var Location 0 + %void = OpTypeVoid + %func = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_0 = OpConstant %u32 0 + %u32_1 = OpConstant %u32 1 + %u32vec2 = OpTypeVector %u32 2 + %u32vec4 = OpTypeVector %u32 4 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + %img_type = OpTypeImage %u32 2D 0 0 0 1 R32i +%ptr_image = OpTypePointer Input %img_type +%image_var = OpVariable %ptr_image Input + %main = OpFunction %void None %func + %label = OpLabel + %img = OpLoad %img_type %image_var + %res1 = OpImageFetch %u32vec4 %img %u32vec2_01 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Format signedness (signed) does not match " + "Sample Type operand")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageSignExtendOverride) { + const std::string code = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %uniform_image + OpExecutionMode %main OriginUpperLeft + OpDecorate %uniform_image DescriptorSet 0 + OpDecorate %uniform_image Binding 0 + %void = OpTypeVoid + %func = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_0 = OpConstant %u32 0 + %u32_1 = OpConstant %u32 1 + %u32vec2 = OpTypeVector %u32 2 + %u32vec4 = OpTypeVector %u32 4 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + %img_type = OpTypeImage %u32 2D 0 0 0 1 R32i +%ptr_image = OpTypePointer UniformConstant %img_type +%uniform_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %label = OpLabel + %img = OpLoad %img_type %uniform_image + %res1 = OpImageFetch %u32vec4 %img %u32vec2_01 SignExtend + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageUintToSint) { + const std::string code = R"( + OpCapability Shader + OpCapability StorageImageExtendedFormats + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %image_ptr + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %image_ptr DescriptorSet 0 + OpDecorate %image_ptr Binding 0 + OpDecorate %image_ptr NonReadable +%type_void = OpTypeVoid +%type_u32 = OpTypeInt 32 0 +%type_i32 = OpTypeInt 32 1 +%type_vec3_u32 = OpTypeVector %type_u32 3 +%type_vec4_u32 = OpTypeVector %type_u32 4 +%type_vec2_i32 = OpTypeVector %type_i32 2 +%type_fn_void = OpTypeFunction %type_void +%type_ptr_fn = OpTypePointer Function %type_vec4_u32 +%type_image = OpTypeImage %type_u32 2D 0 0 0 2 Rgba32ui +%type_ptr_image = OpTypePointer UniformConstant %type_image +%image_ptr = OpVariable %type_ptr_image UniformConstant +%const_i32_0 = OpConstant %type_i32 0 +%const_vec2_i32_00 = OpConstantComposite %type_vec2_i32 %const_i32_0 %const_i32_0 +%main = OpFunction %type_void None %type_fn_void +%label = OpLabel +%store_location = OpVariable %type_ptr_fn Function +%image = OpLoad %type_image %image_ptr +%value = OpImageRead %type_vec4_u32 %image %const_vec2_i32_00 SignExtend + OpStore %store_location %value + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Format signedness (unsigned) does not match Sample Type " + "operand (SignExtend makes the access as signed)")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageSintToUint) { + const std::string code = R"( + OpCapability Shader + OpCapability StorageImageExtendedFormats + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %image_ptr + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %image_ptr DescriptorSet 0 + OpDecorate %image_ptr Binding 0 + OpDecorate %image_ptr NonReadable +%type_void = OpTypeVoid +%type_u32 = OpTypeInt 32 0 +%type_i32 = OpTypeInt 32 1 +%type_vec3_u32 = OpTypeVector %type_u32 3 +%type_vec4_u32 = OpTypeVector %type_u32 4 +%type_vec2_i32 = OpTypeVector %type_i32 2 +%type_fn_void = OpTypeFunction %type_void +%type_ptr_fn = OpTypePointer Function %type_vec4_u32 +%type_image = OpTypeImage %type_u32 2D 0 0 0 2 Rgba32i +%type_ptr_image = OpTypePointer UniformConstant %type_image +%image_ptr = OpVariable %type_ptr_image UniformConstant +%const_i32_0 = OpConstant %type_i32 0 +%const_vec2_i32_00 = OpConstantComposite %type_vec2_i32 %const_i32_0 %const_i32_0 +%main = OpFunction %type_void None %type_fn_void +%label = OpLabel +%store_location = OpVariable %type_ptr_fn Function +%image = OpLoad %type_image %image_ptr +%value = OpImageRead %type_vec4_u32 %image %const_vec2_i32_00 ZeroExtend + OpStore %store_location %value + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Format signedness (signed) does not match Sample Type " + "operand (ZeroExtend makes the access as unsigned)")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageZeroExtendSigned) { + const std::string code = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %uniform_image + OpExecutionMode %main OriginUpperLeft + OpDecorate %uniform_image DescriptorSet 0 + OpDecorate %uniform_image Binding 0 + %void = OpTypeVoid + %func = OpTypeFunction %void + %i32 = OpTypeInt 32 1 + %u32 = OpTypeInt 32 0 + %u32_0 = OpConstant %u32 0 + %u32_1 = OpConstant %u32 1 + %u32vec2 = OpTypeVector %u32 2 + %i32vec4 = OpTypeVector %i32 4 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + %img_type = OpTypeImage %i32 2D 0 0 0 2 R32ui +%ptr_image = OpTypePointer UniformConstant %img_type +%uniform_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %label = OpLabel + %img = OpLoad %img_type %uniform_image + %res1 = OpImageRead %i32vec4 %img %u32vec2_01 ZeroExtend + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Using ZeroExtend, but result type is a signed integer type")); +} + +TEST_F(ValidateImage, TypeImageVulkanStorageZeroExtendRedundant) { + // use ZeroExtend when sample type is already unsigned but still has signed + // format + const std::string code = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %uniform_image + OpExecutionMode %main OriginUpperLeft + OpDecorate %uniform_image DescriptorSet 0 + OpDecorate %uniform_image Binding 0 + %void = OpTypeVoid + %func = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %u32_0 = OpConstant %u32 0 + %u32_1 = OpConstant %u32 1 + %u32vec2 = OpTypeVector %u32 2 + %u32vec4 = OpTypeVector %u32 4 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + %img_type = OpTypeImage %u32 2D 0 0 0 1 R32i +%ptr_image = OpTypePointer UniformConstant %img_type +%uniform_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %label = OpLabel + %img = OpLoad %img_type %uniform_image + %res1 = OpImageFetch %u32vec4 %img %u32vec2_01 ZeroExtend + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Image-04965")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Format signedness (signed) does not match Sample Type " + "operand (ZeroExtend makes the access as unsigned)")); +} + +// TODO - Need to validate in ValidateImageOperands() +TEST_F(ValidateImage, DISABLED_TypeImageVulkanStorageZeroExtendFloat) { + // use ZeroExtend on Float image + const std::string code = R"( + OpCapability Shader + OpCapability StorageImageWriteWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %var + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + OpDecorate %var NonReadable + %void = OpTypeVoid + %func = OpTypeFunction %void + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %image = OpTypeImage %float 2D 0 0 0 2 Unknown + %ptr = OpTypePointer UniformConstant %image + %var = OpVariable %ptr UniformConstant + %v2int = OpTypeVector %int 2 + %int_1 = OpConstant %int 1 + %coord = OpConstantComposite %v2int %int_1 %int_1 + %v3float = OpTypeVector %float 3 + %float_1 = OpConstant %float 1 +%texelU3 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %image %var + OpImageWrite %load %coord %texelU3 ZeroExtend + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Using ZeroExtend, but result type is a signed integer type.")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index 50f4557..72c179b 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp
@@ -160,6 +160,7 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = true; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( @@ -215,9 +216,9 @@ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Non-unique OpEntryPoint interface '2[%var]' is disallowed")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.4 or later, non-unique OpEntryPoint " + "interface '2[%var]' is disallowed")); } TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p3) { @@ -623,6 +624,185 @@ "at location 1")); } +TEST_F(ValidateInterfacesTest, + VulkanLocationsGeometryStreamsDifferentStreamsSameLocation) { + const std::string text = R"( +OpCapability Shader +OpCapability Geometry +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %var1 %var2 +OpExecutionMode %main Triangles +OpExecutionMode %main OutputPoints +OpExecutionMode %main OutputVertices 1 +OpDecorate %var1 Location 1 +OpDecorate %var1 Stream 0 +OpDecorate %var2 Location 1 +OpDecorate %var2 Stream 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%ptr_output_float = OpTypePointer Output %float +%var1 = OpVariable %ptr_output_float Output +%var2 = OpVariable %ptr_output_float 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_F(ValidateInterfacesTest, + VulkanLocationsGeometryStreamsSameStreamSameLocationConflict) { + const std::string text = R"( +OpCapability Shader +OpCapability Geometry +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %var1 %var2 +OpExecutionMode %main Triangles +OpExecutionMode %main OutputPoints +OpExecutionMode %main OutputVertices 1 +OpDecorate %var1 Location 1 +OpDecorate %var1 Stream 1 +OpDecorate %var2 Location 1 +OpDecorate %var2 Stream 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%ptr_output_float = OpTypePointer Output %float +%var1 = OpVariable %ptr_output_float Output +%var2 = OpVariable %ptr_output_float Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-08722")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry-point has conflicting output location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, + VulkanLocationsGeometryStreamsComponentSharingAcrossStreams) { + // Same location and component on different streams must not conflict. + const std::string text = R"( +OpCapability Shader +OpCapability Geometry +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %var1 %var2 +OpExecutionMode %main Triangles +OpExecutionMode %main OutputPoints +OpExecutionMode %main OutputVertices 1 +OpDecorate %var1 Location 1 +OpDecorate %var1 Component 0 +OpDecorate %var1 Stream 0 +OpDecorate %var2 Location 1 +OpDecorate %var2 Component 0 +OpDecorate %var2 Stream 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%ptr_output_float = OpTypePointer Output %float +%var1 = OpVariable %ptr_output_float Output +%var2 = OpVariable %ptr_output_float 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_F(ValidateInterfacesTest, + VulkanLocationsGeometryNoStreamsCapStillConflicts) { + // Without the GeometryStreams capability the per-stream relaxation must not + // apply: a Geometry shader with two outputs at the same location still + // conflicts, same as any other stage. + const std::string text = R"( +OpCapability Shader +OpCapability Geometry +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %var1 %var2 +OpExecutionMode %main Triangles +OpExecutionMode %main OutputPoints +OpExecutionMode %main OutputVertices 1 +OpDecorate %var1 Location 1 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%ptr_output_float = OpTypePointer Output %float +%var1 = OpVariable %ptr_output_float Output +%var2 = OpVariable %ptr_output_float Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-08722")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry-point has conflicting output location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, + VulkanLocationsGeometryStreamsInputStillConflicts) { + // The per-stream relaxation only applies to the Output storage class. + // Input variables in a Geometry+GeometryStreams entry point must still + // have unique locations. + const std::string text = R"( +OpCapability Shader +OpCapability Geometry +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %var1 %var2 +OpExecutionMode %main InputPoints +OpExecutionMode %main OutputPoints +OpExecutionMode %main OutputVertices 1 +OpDecorate %var1 Location 1 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%arr_float = OpTypeArray %float %uint_1 +%ptr_input_arr = OpTypePointer Input %arr_float +%var1 = OpVariable %ptr_input_arr Input +%var2 = OpVariable %ptr_input_arr Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-08721")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1")); +} + TEST_F(ValidateInterfacesTest, VulkanPatchAndNonPatchOverlap) { const std::string text = R"( OpCapability Tessellation @@ -1639,7 +1819,7 @@ "Interface struct has no Block decoration but has BuiltIn members.")); } -TEST_F(ValidateInterfacesTest, InvalidLocationTypePointer) { +TEST_F(ValidateInterfacesTest, InvalidLocationTypeSampler) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical Simple @@ -1648,14 +1828,13 @@ %void = OpTypeVoid %5 = OpTypeFunction %void %float = OpTypeFloat 32 -%_ptr_Private_void = OpTypePointer Private %void + %sampler = OpTypeSampler %uint = OpTypeInt 32 0 %uint_4278132784 = OpConstant %uint 4278132784 -%_arr__ptr_Private_void_uint_4278132784 = OpTypeArray %_ptr_Private_void %uint_4278132784 -%_ptr_Output__arr__ptr_Private_void_uint_4278132784 = OpTypePointer Output %_arr__ptr_Private_void_uint_4278132784 - %2 = OpVariable %_ptr_Output__arr__ptr_Private_void_uint_4278132784 Output -%_ptr_Output__ptr_Private_void = OpTypePointer Output %_ptr_Private_void - %3 = OpVariable %_ptr_Output__arr__ptr_Private_void_uint_4278132784 Output +%_arr__sampler_uint_4278132784 = OpTypeArray %sampler %uint_4278132784 +%_ptr_Output__arr__sampler_uint_4278132784 = OpTypePointer Output %_arr__sampler_uint_4278132784 + %2 = OpVariable %_ptr_Output__arr__sampler_uint_4278132784 Output + %3 = OpVariable %_ptr_Output__arr__sampler_uint_4278132784 Output %1 = OpFunction %void None %5 %15 = OpLabel OpReturn @@ -1668,7 +1847,7 @@ HasSubstr("Invalid type to assign a location")); } -TEST_F(ValidateInterfacesTest, ValidLocationTypePhysicalStorageBufferPointer) { +TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointer) { const std::string text = R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses @@ -1677,10 +1856,10 @@ OpDecorate %var Location 0 OpDecorate %var RestrictPointer %void = OpTypeVoid -%int = OpTypeInt 32 0 -%ptr = OpTypePointer PhysicalStorageBuffer %int -%ptr2 = OpTypePointer Input %ptr -%var = OpVariable %ptr2 Input +%uint = OpTypeInt 32 0 +%psb_ptr = OpTypePointer PhysicalStorageBuffer %uint +%in_ptr = OpTypePointer Input %psb_ptr +%var = OpVariable %in_ptr Input %void_fn = OpTypeFunction %void %main = OpFunction %void None %void_fn %entry = OpLabel @@ -1688,7 +1867,139 @@ OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-09557")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Input/Output interface variable id <2> contains a " + "PhysicalStorageBuffer pointer, which is not allowed")); +} + +TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerArray) { + const std::string text = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Vertex %main "main" %var + OpDecorate %var Location 0 + OpDecorate %var RestrictPointer + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %psb_ptr = OpTypePointer PhysicalStorageBuffer %uint + %array = OpTypeArray %psb_ptr %uint_3 + %in_ptr = OpTypePointer Input %array + %var = OpVariable %in_ptr Input + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-09557")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Input/Output interface variable id <2> contains a " + "PhysicalStorageBuffer pointer, which is not allowed")); +} +TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerStruct) { + const std::string text = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Vertex %main "main" %var + OpDecorate %var Location 0 + OpDecorate %var RestrictPointer + %void = OpTypeVoid + %int = OpTypeInt 32 1 + OpTypeForwardPointer %psb_ptr PhysicalStorageBuffer + %struct_0 = OpTypeStruct %int %psb_ptr + %struct_1 = OpTypeStruct %int %int + %psb_ptr = OpTypePointer PhysicalStorageBuffer %struct_1 + %in_ptr = OpTypePointer Input %struct_0 + %var = OpVariable %in_ptr Input + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-09557")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Input/Output interface variable id <2> contains a " + "PhysicalStorageBuffer pointer, which is not allowed")); +} + +TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerArrayOfStruct) { + const std::string text = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Vertex %main "main" %var + OpDecorate %var Location 0 + OpDecorate %var RestrictPointer + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + OpTypeForwardPointer %psb_ptr PhysicalStorageBuffer + %array_1 = OpTypeArray %psb_ptr %uint_3 + %struct_0 = OpTypeStruct %int %array_1 + %struct_1 = OpTypeStruct %int %int + %psb_ptr = OpTypePointer PhysicalStorageBuffer %struct_1 + %array_0 = OpTypeArray %struct_0 %uint_3 + %in_ptr = OpTypePointer Input %array_0 + %var = OpVariable %in_ptr Input + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-09557")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Input/Output interface variable id <2> contains a " + "PhysicalStorageBuffer pointer, which is not allowed")); +} + +TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerNestedStruct) { + const std::string text = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Vertex %main "main" %var + OpDecorate %var Location 0 + OpDecorate %var RestrictPointer + %void = OpTypeVoid + %int = OpTypeInt 32 1 + OpTypeForwardPointer %psb_ptr PhysicalStorageBuffer + %struct_0 = OpTypeStruct %int %psb_ptr + %struct_1 = OpTypeStruct %int %int + %psb_ptr = OpTypePointer PhysicalStorageBuffer %struct_1 + %struct_2 = OpTypeStruct %int %struct_0 + %in_ptr = OpTypePointer Input %struct_2 + %var = OpVariable %in_ptr Input + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-09557")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Input/Output interface variable id <2> contains a " + "PhysicalStorageBuffer pointer, which is not allowed")); } TEST_F(ValidateInterfacesTest, UntypedVariableInputMissing) { @@ -1807,6 +2118,164 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } +TEST_F(ValidateInterfacesTest, + InvalidBfloat16VariableWithInputOutputStorageClass) { + const std::string text = R"( +OpCapability Shader +OpCapability BFloat16TypeKHR +OpExtension "SPV_KHR_bfloat16" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%bfloat16 = OpTypeFloat 16 BFloat16KHR +%in_ptr = OpTypePointer Input %bfloat16 +%out_ptr = OpTypePointer Output %bfloat16 +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Bfloat16 OpVariable <id> '2[%2]' must not be declared " + "with a Storage Class of Input or Output.\n")); +} + +TEST_F(ValidateInterfacesTest, + InvalidFP8E4M3VariableWithInputOutputStorageClass) { + const std::string text = R"( +OpCapability Shader +OpCapability Float8EXT +OpExtension "SPV_EXT_float8" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT +%in_ptr = OpTypePointer Input %fp8e4m3 +%out_ptr = OpTypePointer Output %fp8e4m3 +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeFloat-10823")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("FP8 E4M3/E5M2 OpVariable <id> '2[%2]' must not be declared " + "with a Storage Class of Input or Output.\n")); +} + +TEST_F(ValidateInterfacesTest, + InvalidFP8E5M2VariableWithInputOutputStorageClass) { + const std::string text = R"( +OpCapability Shader +OpCapability Float8EXT +OpExtension "SPV_EXT_float8" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT +%in_ptr = OpTypePointer Input %fp8e5m2 +%out_ptr = OpTypePointer Output %fp8e5m2 +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeFloat-10823")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("FP8 E4M3/E5M2 OpVariable <id> '2[%2]' must not be declared " + "with a Storage Class of Input or Output.\n")); +} + +TEST_F(ValidateInterfacesTest, VectorIdFragmentInputOutputPass) { + const std::string text = R"( +OpCapability Shader +OpCapability LongVectorEXT +OpExtension "SPV_EXT_long_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in %out +OpExecutionMode %main OriginUpperLeft +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u4 = OpConstant %u32 4 +%f32vec = OpTypeVectorIdEXT %f32 %u4 +%in_ptr = OpTypePointer Input %f32vec +%out_ptr = OpTypePointer Output %f32vec +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateInterfacesTest, VectorIdVertexInputOutputPass) { + const std::string text = R"( +OpCapability Shader +OpCapability LongVectorEXT +OpExtension "SPV_EXT_long_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %in %out +OpDecorate %in Location 0 +OpDecorate %out Location 0 +%void = OpTypeVoid +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u4 = OpConstant %u32 4 +%f32vec = OpTypeVectorIdEXT %f32 %u4 +%in_ptr = OpTypePointer Input %f32vec +%out_ptr = OpTypePointer Output %f32vec +%in = OpVariable %in_ptr Input +%out = OpVariable %out_ptr Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_invalid_type_test.cpp b/test/val/val_invalid_type_test.cpp new file mode 100644 index 0000000..5f8cf73 --- /dev/null +++ b/test/val/val_invalid_type_test.cpp
@@ -0,0 +1,296 @@ +// Copyright (c) 2025 Google Inc. +// Copyright (c) 2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for invalid types. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; +using ::testing::Values; + +using ValidateInvalidType = spvtest::ValidateBase<bool>; + +std::string GenerateBFloatCode(const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability BFloat16TypeKHR +OpCapability AtomicFloat16AddEXT +OpCapability GroupNonUniformShuffle +OpExtension "SPV_EXT_shader_atomic_float16_add" +OpExtension "SPV_KHR_bfloat16" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpName %main "main" +%bool = OpTypeBool +%void = OpTypeVoid +%bfloat16 = OpTypeFloat 16 BFloat16KHR +%func = OpTypeFunction %void +%u32 = OpTypeInt 32 0 +%u1 = OpConstant %u32 1 +%u0 = OpConstant %u32 0 +%u3 = OpConstant %u32 3 +%bf16_1 = OpConstant %bfloat16 1 +%_ptr_Function_bfloat16 = OpTypePointer Function %bfloat16 +%v2bfloat16 = OpTypeVector %bfloat16 2 +%_ptr_Function_v2bfloat16 = OpTypePointer Function %v2bfloat16 +%bf16_ptr = OpTypePointer Workgroup %bfloat16 +%bf16_var = OpVariable %bf16_ptr Workgroup +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + main_body + suffix; +} + +TEST_F(ValidateInvalidType, Bfloat16InvalidArithmeticInstruction) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_bfloat16 Function +%v2 = OpVariable %_ptr_Function_bfloat16 Function +%12 = OpLoad %bfloat16 %v1 +%14 = OpLoad %bfloat16 %v2 +%15 = OpFMul %bfloat16 %12 %14 +)"; + + CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FMul doesn't support BFloat16 type.")); +} + +TEST_F(ValidateInvalidType, Bfloat16InvalidRelationalInstruction) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_bfloat16 Function +%v2 = OpVariable %_ptr_Function_bfloat16 Function +%12 = OpLoad %bfloat16 %v1 +%14 = OpLoad %bfloat16 %v2 +%15 = OpFOrdEqual %bool %12 %14 +)"; + + CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FOrdEqual doesn't support BFloat16 type.")); +} + +TEST_F(ValidateInvalidType, Bfloat16InvalidAtomicInstruction) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %bfloat16 %bf16_var %u1 %u0 %bf16_1 +)"; + + CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT doesn't support BFloat16 type.")); +} + +TEST_F(ValidateInvalidType, Bfloat16InvalidGroupNonUniformShuffle) { + const std::string body = R"( +%val1 = OpGroupNonUniformShuffle %bfloat16 %u3 %bf16_1 %u0 +)"; + + CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GroupNonUniformShuffle doesn't support BFloat16 type.")); +} + +std::string GenerateFP8Code(const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability Float8EXT +OpCapability AtomicFloat16AddEXT +OpCapability GroupNonUniformShuffle +OpExtension "SPV_EXT_shader_atomic_float16_add" +OpExtension "SPV_EXT_float8" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpName %main "main" +%bool = OpTypeBool +%void = OpTypeVoid +%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT +%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT +%func = OpTypeFunction %void +%u32 = OpTypeInt 32 0 +%u1 = OpConstant %u32 1 +%u0 = OpConstant %u32 0 +%u3 = OpConstant %u32 3 +%fp8e4m3_1 = OpConstant %fp8e4m3 1 +%fp8e5m2_1 = OpConstant %fp8e5m2 1 +%_ptr_Function_fp8e4m3 = OpTypePointer Function %fp8e4m3 +%_ptr_Function_fp8e5m2 = OpTypePointer Function %fp8e5m2 +%v2fp8e4m3 = OpTypeVector %fp8e4m3 2 +%v2fp8e5m2 = OpTypeVector %fp8e5m2 2 +%_ptr_Function_v2fp8e4m3 = OpTypePointer Function %v2fp8e4m3 +%_ptr_Function_v2fp8e5m2 = OpTypePointer Function %v2fp8e5m2 +%fp8e4m3_ptr = OpTypePointer Workgroup %fp8e4m3 +%fp8e5m2_ptr = OpTypePointer Workgroup %fp8e5m2 +%fp8e4m3_var = OpVariable %fp8e4m3_ptr Workgroup +%fp8e5m2_var = OpVariable %fp8e5m2_ptr Workgroup +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + main_body + suffix; +} + +TEST_F(ValidateInvalidType, FP8E4M3InvalidArithmeticInstruction) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_fp8e4m3 Function +%v2 = OpVariable %_ptr_Function_fp8e4m3 Function +%12 = OpLoad %fp8e4m3 %v1 +%14 = OpLoad %fp8e4m3 %v2 +%15 = OpFMul %fp8e4m3 %12 %14 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FMul doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E5M2InvalidArithmeticInstruction) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_fp8e5m2 Function +%v2 = OpVariable %_ptr_Function_fp8e5m2 Function +%12 = OpLoad %fp8e5m2 %v1 +%14 = OpLoad %fp8e5m2 %v2 +%15 = OpFMul %fp8e5m2 %12 %14 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FMul doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E4M3InvalidRelationalInstruction) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_fp8e4m3 Function +%v2 = OpVariable %_ptr_Function_fp8e4m3 Function +%12 = OpLoad %fp8e4m3 %v1 +%14 = OpLoad %fp8e4m3 %v2 +%15 = OpFOrdEqual %bool %12 %14 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FOrdEqual doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E5M2InvalidRelationalInstruction) { + const std::string body = R"( +%v1 = OpVariable %_ptr_Function_fp8e5m2 Function +%v2 = OpVariable %_ptr_Function_fp8e5m2 Function +%12 = OpLoad %fp8e5m2 %v1 +%14 = OpLoad %fp8e5m2 %v2 +%15 = OpFOrdEqual %bool %12 %14 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FOrdEqual doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E4M3InvalidAtomicInstruction) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %fp8e4m3 %fp8e4m3_var %u1 %u0 %fp8e4m3_1 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E5M2InvalidAtomicInstruction) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %fp8e5m2 %fp8e5m2_var %u1 %u0 %fp8e5m2_1 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E4M3InvalidGroupNonUniformShuffle) { + const std::string body = R"( +%val1 = OpGroupNonUniformShuffle %fp8e4m3 %u3 %fp8e4m3_1 %u0 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GroupNonUniformShuffle doesn't support FP8 E4M3/E5M2 types.")); +} + +TEST_F(ValidateInvalidType, FP8E5M2InvalidGroupNonUniformShuffle) { + const std::string body = R"( +%val1 = OpGroupNonUniformShuffle %fp8e5m2 %u3 %fp8e5m2_1 %u0 +)"; + + CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("GroupNonUniformShuffle doesn't support FP8 E4M3/E5M2 types.")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp index e809abf..4fd7fe4 100644 --- a/test/val/val_layout_test.cpp +++ b/test/val/val_layout_test.cpp
@@ -460,9 +460,11 @@ OpMemoryModel Logical GLSL450)"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("No OpEntryPoint instruction was found. This is only " - "allowed if the Linkage capability is being used.")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "No OpEntryPoint instruction was found. This is only " + "allowed if the Linkage or GraphARM capability is being used.")); } // Invalid. A function may not be a target of both OpEntryPoint and @@ -631,7 +633,8 @@ ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("ModuleProcessed cannot appear in a function declaration")); + HasSubstr( + "ModuleProcessed cannot appear in the graph definitions section")); } TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) { @@ -653,7 +656,8 @@ ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("ModuleProcessed cannot appear in a function declaration")); + HasSubstr( + "ModuleProcessed cannot appear in the graph definitions section")); } // TODO(umar): Test optional instructions
diff --git a/test/val/val_logical_pointers_test.cpp b/test/val/val_logical_pointers_test.cpp new file mode 100644 index 0000000..3b09b26 --- /dev/null +++ b/test/val/val_logical_pointers_test.cpp
@@ -0,0 +1,2456 @@ +// Copyright (c) 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "test/unit_spirv.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 ::testing::ValuesIn; + +using ValidateLogicalPointersTest = spvtest::ValidateBase<bool>; + +enum class MatrixTrace : uint32_t { + kNotAMatrix, + kColumn, + kComponent, +}; + +const MatrixTrace traces[] = {MatrixTrace::kNotAMatrix, MatrixTrace::kColumn, + MatrixTrace::kComponent}; + +using MatrixTraceTypedTest = spvtest::ValidateBase<MatrixTrace>; + +TEST_P(MatrixTraceTypedTest, PhiLoopOp1) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +OpBranch %loop +%loop = OpLabel +%phi = OpPhi )" + type + R"( %gep %entry %copy %continue +OpLoopMerge %merge %continue None +OpBranchConditional %bool_cond %merge %continue +%continue = OpLabel +%copy = OpCopyObject )" + type + + R"( %phi +OpBranch %loop +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, PhiLoopOp2) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +OpBranch %loop +%loop = OpLabel +%phi = OpPhi )" + type + R"( %copy %continue %gep %entry +OpLoopMerge %merge %continue None +OpBranchConditional %bool_cond %merge %continue +%continue = OpLabel +%copy = OpCopyObject )" + type + + R"( %phi +OpBranch %loop +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, SelectOp1) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject )" + type + + R"( %gep +%sel = OpSelect )" + type + R"( %bool_cond %copy %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, SelectOp2) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject )" + type + + R"( %gep +%sel = OpSelect )" + type + R"( %bool_cond %null %copy +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionVariable) { + const auto trace_type = GetParam(); + std::string gep, type, ld_type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_func_wg_v2float"; + ld_type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_func_wg_float"; + ld_type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index"; + break; + default: + type = "%ptr_func_wg_float"; + ld_type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var_float"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_float = OpTypePointer Workgroup %float +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2 +%ptr_func_wg_float = OpTypePointer Function %ptr_wg_float +%ptr_func_wg_v2float = OpTypePointer Function %ptr_wg_v2float +%var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup +%var_float = OpVariable %ptr_wg_float Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%func_var = OpVariable )" + type + + R"( Function +%gep = )" + gep + R"( +OpStore %func_var %gep +%ld = OpLoad )" + ld_type + R"( %func_var +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, PrivateVariable) { + const auto trace_type = GetParam(); + std::string gep, type, ld_type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_priv_wg_v2float"; + ld_type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_priv_wg_float"; + ld_type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index"; + break; + default: + type = "%ptr_priv_wg_float"; + ld_type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var_float"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_float = OpTypePointer Workgroup %float +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2 +%ptr_priv_wg_float = OpTypePointer Private %ptr_wg_float +%ptr_priv_wg_v2float = OpTypePointer Private %ptr_wg_v2float +%var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup +%var_float = OpVariable %ptr_wg_float Workgroup +%priv_var = OpVariable )" + type + + R"( Private +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +OpStore %priv_var %gep +%ld = OpLoad )" + ld_type + R"( %priv_var +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionVariableAggregate) { + const auto trace_type = GetParam(); + std::string gep, var_type, gep_type, ld_type; + switch (trace_type) { + case MatrixTrace::kColumn: + var_type = "%ptr_func_wg_struct_v2float"; + gep_type = "%ptr_func_wg_v2float"; + ld_type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index"; + break; + case MatrixTrace::kComponent: + var_type = "%ptr_func_wg_struct_float"; + gep_type = "%ptr_func_wg_float"; + ld_type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index"; + break; + default: + var_type = "%ptr_func_wg_struct_float"; + gep_type = "%ptr_func_wg_float"; + ld_type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var_float"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_float = OpTypePointer Workgroup %float +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2 +%func_array_float = OpTypeArray %ptr_wg_float %int_1 +%func_array_v2float = OpTypeArray %ptr_wg_v2float %int_1 +%func_struct_float = OpTypeStruct %func_array_float +%func_struct_v2float = OpTypeStruct %func_array_v2float +%ptr_func_wg_struct_float = OpTypePointer Function %func_struct_float +%ptr_func_wg_struct_v2float = OpTypePointer Function %func_struct_v2float +%ptr_func_wg_float = OpTypePointer Function %ptr_wg_float +%ptr_func_wg_v2float = OpTypePointer Function %ptr_wg_v2float +%var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup +%var_float = OpVariable %ptr_wg_float Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%func_var = OpVariable )" + var_type + + R"( Function +%gep = )" + gep + R"( +%store_gep = OpAccessChain )" + + gep_type + R"( %func_var %int_0 %index +%store_gep_copy = OpCopyObject )" + + gep_type + R"( %store_gep +OpStore %store_gep_copy %gep +%ld_gep = OpAccessChain )" + gep_type + + R"( %func_var %int_0 %index +%ld = OpLoad )" + ld_type + R"( %ld_gep +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionCallParam1) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%foo_fn = OpTypeFunction %void )" + + type + R"( %bool +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject )" + type + + R"( %gep +%call = OpFunctionCall %void %foo %copy %bool_cond +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %foo_fn +%ptr_param = OpFunctionParameter )" + + type + R"( +%bool_param = OpFunctionParameter %bool +%foo_entry = OpLabel +%sel = OpSelect )" + type + R"( %bool_param %ptr_param %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionCallParam2) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%foo_fn = OpTypeFunction %void %bool )" + + type + R"( +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject )" + type + + R"( %gep +%call = OpFunctionCall %void %foo %bool_cond %copy +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %foo_fn +%bool_param = OpFunctionParameter %bool +%ptr_param = OpFunctionParameter )" + + type + R"( +%foo_entry = OpLabel +%sel = OpSelect )" + type + R"( %bool_param %ptr_param %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionCall) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%foo_ty = OpTypeFunction )" + + type + R"( +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall )" + type + + R"( %foo +OpReturn +OpFunctionEnd +%foo = OpFunction )" + type + + R"( None %foo_ty +%foo_entry = OpLabel +%gep = )" + gep + R"( +OpReturnValue %gep +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionCallMultiReturn1) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%foo_ty = OpTypeFunction )" + + type + R"( +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall )" + type + + R"( %foo +OpReturn +OpFunctionEnd +%foo = OpFunction )" + type + + R"( None %foo_ty +%foo_entry = OpLabel +%gep = )" + gep + R"( +OpSelectionMerge %merge None +OpBranchConditional %bool_cond %then %merge +%then = OpLabel +OpReturnValue %gep +%merge = OpLabel +OpReturnValue %null +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceTypedTest, FunctionCallMultiReturn2) { + const auto trace_type = GetParam(); + std::string gep, type; + switch (trace_type) { + case MatrixTrace::kColumn: + type = "%ptr_wg_v2float"; + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + type = "%ptr_wg_float"; + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull )" + type + + R"( +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%foo_ty = OpTypeFunction )" + + type + R"( +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall )" + type + + R"( %foo +OpReturn +OpFunctionEnd +%foo = OpFunction )" + type + + R"( None %foo_ty +%foo_entry = OpLabel +%gep = )" + gep + R"( +OpSelectionMerge %merge None +OpBranchConditional %bool_cond %then %merge +%then = OpLabel +OpReturnValue %null +%merge = OpLabel +OpReturnValue %gep +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +INSTANTIATE_TEST_SUITE_P(ValidateLogicalPointersMatrixTraceTyped, + MatrixTraceTypedTest, ValuesIn(traces)); + +using MatrixTraceUntypedTest = spvtest::ValidateBase<MatrixTrace>; + +TEST_P(MatrixTraceUntypedTest, PhiLoopOp1) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %ptr_wg %gep %entry %copy %continue +OpLoopMerge %merge %continue None +OpBranchConditional %bool_cond %merge %continue +%continue = OpLabel +%copy = OpCopyObject %ptr_wg %phi +OpBranch %loop +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} +TEST_P(MatrixTraceUntypedTest, PhiLoopOp2) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %ptr_wg %copy %continue %gep %entry +OpLoopMerge %merge %continue None +OpBranchConditional %bool_cond %merge %continue +%continue = OpLabel +%copy = OpCopyObject %ptr_wg %phi +OpBranch %loop +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, SelectOp1) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject %ptr_wg %gep +%sel = OpSelect %ptr_wg %bool_cond %copy %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, SelectOp2) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject %ptr_wg %gep +%sel = OpSelect %ptr_wg %bool_cond %null %copy +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionVariable) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index"; + break; + case MatrixTrace::kComponent: + gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%ptr_func_wg = OpTypePointer Function %ptr_wg +%var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2 +%var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%func_var = OpVariable %ptr_func_wg Function +%gep = )" + gep + R"( +OpStore %func_var %gep +%ld = OpLoad %ptr_wg %func_var +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, PrivateVariable) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index"; + break; + case MatrixTrace::kComponent: + gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%ptr_priv_wg = OpTypePointer Private %ptr_wg +%var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2 +%var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float +%priv_var = OpVariable %ptr_priv_wg Private +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +OpStore %priv_var %gep +%ld = OpLoad %ptr_wg %priv_var +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionVariableAggregate) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index"; + break; + case MatrixTrace::kComponent: + gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%func_array = OpTypeArray %ptr_wg %int_1 +%func_struct = OpTypeStruct %func_array +%ptr_func_wg_struct = OpTypePointer Function %func_struct +%ptr_func_wg = OpTypePointer Function %ptr_wg +%var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2 +%var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%func_var = OpVariable %ptr_func_wg_struct Function +%gep = )" + gep + R"( +%store_gep = OpAccessChain %ptr_func_wg %func_var %int_0 %index +%store_gep_copy = OpCopyObject %ptr_func_wg %store_gep +OpStore %store_gep_copy %gep +%ld_gep = OpAccessChain %ptr_func_wg %func_var %int_0 %index +%ld = OpLoad %ptr_wg %ld_gep +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionCallParam1) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%foo_fn = OpTypeFunction %void %ptr_wg %bool +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject %ptr_wg %gep +%call = OpFunctionCall %void %foo %copy %bool_cond +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %foo_fn +%ptr_param = OpFunctionParameter %ptr_wg +%bool_param = OpFunctionParameter %bool +%foo_entry = OpLabel +%sel = OpSelect %ptr_wg %bool_param %ptr_param %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionCallParam2) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%foo_fn = OpTypeFunction %void %bool %ptr_wg +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpCopyObject %ptr_wg %gep +%call = OpFunctionCall %void %foo %bool_cond %copy +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %foo_fn +%bool_param = OpFunctionParameter %bool +%ptr_param = OpFunctionParameter %ptr_wg +%foo_entry = OpLabel +%sel = OpSelect %ptr_wg %bool_param %ptr_param %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionCall) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%foo_ty = OpTypeFunction %ptr_wg +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr_wg %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr_wg None %foo_ty +%foo_entry = OpLabel +%gep = )" + gep + R"( +OpReturnValue %gep +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionCallMultiReturn1) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%foo_ty = OpTypeFunction %ptr_wg +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr_wg %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr_wg None %foo_ty +%foo_entry = OpLabel +%gep = )" + gep + R"( +OpSelectionMerge %merge None +OpBranchConditional %bool_cond %then %merge +%then = OpLabel +OpReturnValue %gep +%merge = OpLabel +OpReturnValue %null +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, FunctionCallMultiReturn2) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = + "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index " + "%index"; + break; + default: + gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%null = OpConstantNull %ptr_wg +%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct +%void_fn = OpTypeFunction %void +%foo_ty = OpTypeFunction %ptr_wg +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %ptr_wg %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %ptr_wg None %foo_ty +%foo_entry = OpLabel +%gep = )" + gep + R"( +OpSelectionMerge %merge None +OpBranchConditional %bool_cond %then %merge +%then = OpLabel +OpReturnValue %null +%merge = OpLabel +OpReturnValue %gep +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +TEST_P(MatrixTraceUntypedTest, MixedTypes) { + const auto trace_type = GetParam(); + std::string gep; + switch (trace_type) { + case MatrixTrace::kColumn: + gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index"; + break; + case MatrixTrace::kComponent: + gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index"; + break; + default: + gep = "OpAccessChain %ptr_wg_float %var %int_1"; + break; + } + + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%index = OpUndef %int +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%array = OpTypeArray %mat2x2 %int_2 +%struct = OpTypeStruct %array %float +%ptr_wg = OpTypeUntypedPointerKHR Workgroup +%ptr_wg_struct = OpTypePointer Workgroup %struct +%ptr_wg_v2float = OpTypePointer Workgroup %v2float +%ptr_wg_float = OpTypePointer Workgroup %float +%null = OpConstantNull %ptr_wg +%var = OpVariable %ptr_wg_struct Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = )" + gep + R"( +%copy = OpUntypedAccessChainKHR %ptr_wg %struct %gep +%sel = OpSelect %ptr_wg %bool_cond %copy %null +OpReturn +OpFunctionEnd +)"; + + const auto expected = trace_type == MatrixTrace::kNotAMatrix + ? SPV_SUCCESS + : SPV_ERROR_INVALID_DATA; + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + if (expected) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to a column or a " + "component of a column of a matrix")); + } +} + +INSTANTIATE_TEST_SUITE_P(ValidateLogicalPointersMatrixTraceUntyped, + MatrixTraceUntypedTest, ValuesIn(traces)); + +TEST_F(ValidateLogicalPointersTest, SelectDifferentBuffersTyped) { + const std::string spirv = R"( +OpCapability VariablePointersStorageBuffer +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%ptr_struct = OpTypePointer StorageBuffer %struct +%v1 = OpVariable %ptr_struct StorageBuffer +%v2 = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%select = OpSelect %ptr_struct %bool_cond %v1 %v2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointers must point into the same structure " + "(or OpConstantNull)")); +} + +TEST_F(ValidateLogicalPointersTest, SelectDifferentBuffersUntyped) { + const std::string spirv = R"( +OpCapability VariablePointersStorageBuffer +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct +%v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%select = OpSelect %ptr %bool_cond %v1 %v2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointers must point into the same structure " + "(or OpConstantNull)")); +} + +TEST_F(ValidateLogicalPointersTest, PhiDifferentBuffersTyped) { + const std::string spirv = R"( +OpCapability VariablePointersStorageBuffer +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%ptr_struct = OpTypePointer StorageBuffer %struct +%v1 = OpVariable %ptr_struct StorageBuffer +%v2 = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %bool_cond %then %merge +%then = OpLabel +OpBranch %merge +%merge = OpLabel +%phi = OpPhi %ptr_struct %v1 %entry %v2 %then +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointers must point into the same structure " + "(or OpConstantNull)")); +} + +TEST_F(ValidateLogicalPointersTest, PhiDifferentBuffersUntyped) { + const std::string spirv = R"( +OpCapability VariablePointersStorageBuffer +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct +%v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %bool_cond %then %merge +%then = OpLabel +OpBranch %merge +%merge = OpLabel +%phi = OpPhi %ptr %v1 %entry %v2 %then +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointers must point into the same structure " + "(or OpConstantNull)")); +} + +TEST_F(ValidateLogicalPointersTest, BlockArrayTyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%struct = OpTypeStruct %int +%array = OpTypeArray %struct %int_4 +%ptr_array = OpTypePointer StorageBuffer %array +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_array StorageBuffer +%null = OpConstantNull %ptr_array +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%select = OpSelect %ptr_array %bool_cond %null %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to an array of Block- " + "or BufferBlock-decorated structs")); +} + +TEST_F(ValidateLogicalPointersTest, BlockArrayUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%bool_cond = OpUndef %bool +%struct = OpTypeStruct %int +%array = OpTypeArray %struct %int_4 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %array +%null = OpConstantNull %ptr +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%select = OpSelect %ptr %bool_cond %null %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to an array of Block- " + "or BufferBlock-decorated structs")); +} + +TEST_F(ValidateLogicalPointersTest, UntypedMatrixLoad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%struct = OpTypeStruct %mat2x2 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %struct %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateLogicalPointersTest, UntypedMatrixLoadVariablePointer) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%struct = OpTypeStruct %mat2x2 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %struct +%null = OpConstantNull %ptr +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%sel = OpSelect %ptr %cond %var %null +%ld = OpLoad %struct %sel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to an object that is " + "or contains a matrix")); +} + +TEST_F(ValidateLogicalPointersTest, UntypedMatrixStore) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%zero = OpConstantNull %mat2x2 +%struct = OpTypeStruct %mat2x2 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpStore %var %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateLogicalPointersTest, UntypedMatrixStoreVariablePointer) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%zero = OpConstantNull %mat2x2 +%struct = OpTypeStruct %mat2x2 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %struct +%null = OpConstantNull %ptr +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%sel = OpSelect %ptr %cond %var %null +OpStore %sel %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable pointer must not point to an object that is " + "or contains a matrix")); +} + +TEST_F(ValidateLogicalPointersTest, LogicalPointerOperandFailure) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Addresses +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%v2int = OpTypeVector %int 2 +%ptr_v2int = OpTypePointer StorageBuffer %v2int +%var = OpVariable %ptr_v2int StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpInBoundsPtrAccessChain %ptr_v2int %var %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Instruction may not have a logical pointer operand")); +} + +TEST_F(ValidateLogicalPointersTest, + LogicalPointerOperandVariablePointerStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpMemoryModel Logical GLSL450 +OpDecorate %ptr_int ArrayStride 4 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%ptr_array = OpTypePointer StorageBuffer %array +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_array StorageBuffer +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ptr_gep = OpPtrAccessChain %ptr_int %gep %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Instruction may only have a logical pointer operand in the " + "StorageBuffer or " + "Workgroup storage classes with appropriate variable pointers " + "capability")); +} + +TEST_F(ValidateLogicalPointersTest, + LogicalPointerOperandVariablePointerWorkgroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%ptr_array = OpTypePointer Workgroup %array +%ptr_int = OpTypePointer Workgroup %int +%var = OpVariable %ptr_array Workgroup +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ptr_gep = OpPtrAccessChain %ptr_int %gep %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Instruction may only have a logical pointer operand in the " + "StorageBuffer or " + "Workgroup storage classes with appropriate variable pointers " + "capability")); +} + +TEST_F(ValidateLogicalPointersTest, + LogicalPointerReturnVariablePointerStorageBuffer) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%int = OpTypeInt 32 0 +%ptr = OpTypePointer StorageBuffer %int +%null = OpConstantNull %ptr +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Instruction may only return a logical pointer in the " + "StorageBuffer or " + "Workgroup storage classes with appropriate variable pointers " + "capability")); +} + +TEST_F(ValidateLogicalPointersTest, + LogicalPointerReturnVariablePointerWorkgroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +%null = OpConstantNull %ptr +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Instruction may only return a logical pointer in the " + "StorageBuffer or " + "Workgroup storage classes with appropriate variable pointers " + "capability")); +} + +TEST_F(ValidateLogicalPointersTest, ArrayLengthInvalidVariablePointer) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +%struct = OpTypeStruct %array +%ptr = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr StorageBuffer +%null = OpConstantNull %ptr +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%sel = OpSelect %ptr %cond %null %var +%len = OpArrayLength %int %sel 0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer operand must not be a variable pointer")); +} + +TEST_F(ValidateLogicalPointersTest, ArrayLengthUntypedInvalidVariablePointer) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %int +%struct = OpTypeStruct %array +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %struct +%null = OpConstantNull %ptr +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%sel = OpSelect %ptr %cond %null %var +%len = OpUntypedArrayLengthKHR %int %struct %sel 0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer operand must not be a variable pointer")); +} + +TEST_F(ValidateLogicalPointersTest, FunctionParameterOperandOfFunctionCall) { + const std::string spirv = R"( + OpCapability ClipDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 " " + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %32 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %35 = OpTypeFunction %float %_ptr_Function_float +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%float_1_35631564en19_0 = OpConstant %float 1.35631564e-19 + %2 = OpFunction %void None %32 + %8447 = OpLabel + OpUnreachable + OpFunctionEnd + %9 = OpFunction %float None %35 + %10 = OpFunctionParameter %_ptr_Function_float + %65535 = OpLabel + %217 = OpVariable %_ptr_Function_float Function + %218 = OpLoad %float %10 + %2097407 = OpFunctionCall %float %9 %10 + %231 = OpLoad %float %217 + %232 = OpFDiv %float %218 %231 + %233 = OpExtInst %float %1 SmoothStep %float_1_35631564en19 %float_1_35631564en19_0 %232 + OpReturnValue %233 + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicalPointersTest, RecursiveCalls) { + const std::string spirv = R"( + OpCapability ClipDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 " " + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %32 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %35 = OpTypeFunction %float %_ptr_Function_float +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%float_1_35631564en19_0 = OpConstant %float 1.35631564e-19 + %2 = OpFunction %void None %32 + %8447 = OpLabel + OpUnreachable + OpFunctionEnd + %9 = OpFunction %float None %35 + %10 = OpFunctionParameter %_ptr_Function_float + %65535 = OpLabel + %217 = OpVariable %_ptr_Function_float Function + %218 = OpLoad %float %10 + %2097407 = OpFunctionCall %float %20 %10 + %231 = OpLoad %float %217 + %232 = OpFDiv %float %218 %231 + %233 = OpExtInst %float %1 SmoothStep %float_1_35631564en19 %float_1_35631564en19_0 %232 + OpReturnValue %233 + OpFunctionEnd + %20 = OpFunction %float None %35 + %21 = OpFunctionParameter %_ptr_Function_float + %22 = OpLabel + %23 = OpFunctionCall %float %9 %21 + OpReturnValue %float_1_35631564en19 + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp index c140672..07b502c 100644 --- a/test/val/val_logicals_test.cpp +++ b/test/val/val_logicals_test.cpp
@@ -631,38 +631,54 @@ } TEST_F(ValidateLogicals, OpSelectPointerWithCapability1) { - const std::string body = R"( -%x = OpVariable %f32vec4ptr Function -%y = OpVariable %f32vec4ptr Function -OpStore %x %f32vec4_0123 -OpStore %y %f32vec4_1234 -%val1 = OpSelect %f32vec4ptr %true %x %y -)"; - - const std::string extra_cap_ext = R"( + const std::string spirv = R"( +OpCapability Shader OpCapability VariablePointers OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%bool = OpTypeBool +%undef = OpUndef %bool +%ptr = OpTypePointer Workgroup %int +%var = OpVariable %ptr Workgroup +%null = OpConstantNull %ptr +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%sel = OpSelect %ptr %undef %var %null +OpReturn +OpFunctionEnd )"; - CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str()); + CompileSuccessfully(spirv); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateLogicals, OpSelectPointerWithCapability2) { - const std::string body = R"( -%x = OpVariable %f32vec4ptr Function -%y = OpVariable %f32vec4ptr Function -OpStore %x %f32vec4_0123 -OpStore %y %f32vec4_1234 -%val1 = OpSelect %f32vec4ptr %true %x %y -)"; - - const std::string extra_cap_ext = R"( + const std::string spirv = R"( +OpCapability Shader OpCapability VariablePointersStorageBuffer OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%bool = OpTypeBool +%undef = OpUndef %bool +%ptr = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr StorageBuffer +%null = OpConstantNull %ptr +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%sel = OpSelect %ptr %undef %var %null +OpReturn +OpFunctionEnd )"; - CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str()); + CompileSuccessfully(spirv); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); }
diff --git a/test/val/val_memory_semantics_test.cpp b/test/val/val_memory_semantics_test.cpp new file mode 100644 index 0000000..96f812d --- /dev/null +++ b/test/val/val_memory_semantics_test.cpp
@@ -0,0 +1,688 @@ +// Copyright (c) 2025 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 "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; +using ::testing::internal::ParamGenerator; + +// clang-format off +#define MEMORY_SEMANTICS_OPERANDS \ + OpMemoryBarrier, \ + OpControlBarrier, \ + OpAtomicLoad, \ + OpAtomicStore, \ + OpAtomicExchange, \ + OpAtomicIIncrement, \ + OpAtomicIDecrement, \ + OpAtomicIAdd, \ + OpAtomicISub, \ + OpAtomicSMin, \ + OpAtomicSMax, \ + OpAtomicUMin, \ + OpAtomicUMax, \ + OpAtomicAnd, \ + OpAtomicOr, \ + OpAtomicXor, \ + OpAtomicCompareExchangeEqual, \ + OpAtomicCompareExchangeUnequal + +enum Operand { MEMORY_SEMANTICS_OPERANDS }; +const Operand Operands[] = { MEMORY_SEMANTICS_OPERANDS }; +const size_t OperandsCount = sizeof(Operands) / sizeof(Operand); +#undef MEMORY_SEMANTICS_OPERANDS +// clang-format on + +enum Value { + None = uint32_t(spv::MemorySemanticsMask::MaskNone), + Acquire = uint32_t(spv::MemorySemanticsMask::Acquire), + Release = uint32_t(spv::MemorySemanticsMask::Release), + AcqRel = uint32_t(spv::MemorySemanticsMask::AcquireRelease), + SeqCst = uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent), + Uniform = uint32_t(spv::MemorySemanticsMask::UniformMemory), + Subgroup = uint32_t(spv::MemorySemanticsMask::SubgroupMemory), + Workgroup = uint32_t(spv::MemorySemanticsMask::WorkgroupMemory), + CrossWorkgroup = uint32_t(spv::MemorySemanticsMask::CrossWorkgroupMemory), + AtomicCounter = uint32_t(spv::MemorySemanticsMask::AtomicCounterMemory), + Image = uint32_t(spv::MemorySemanticsMask::ImageMemory), + Output = uint32_t(spv::MemorySemanticsMask::OutputMemory), + Available = uint32_t(spv::MemorySemanticsMask::MakeAvailable), + Visible = uint32_t(spv::MemorySemanticsMask::MakeVisible), + Volatile = uint32_t(spv::MemorySemanticsMask::Volatile) +}; + +struct TestResult { + explicit TestResult(spv_result_t in_result = SPV_SUCCESS, + const char* in_vuid = nullptr, + const char* in_error = nullptr) + : result(in_result), vuid(in_vuid), error(in_error) {} + spv_result_t result; + const char* vuid; + const char* error; +}; + +template <typename T, typename... Ts> +ParamGenerator<T> ValuesInExcept(const T* items, const size_t count, + const Ts... skip) { + std::vector<T> filtered; + std::initializer_list<T> excluded = {skip...}; + std::copy_if(items, items + count, std::back_inserter(filtered), + [&excluded](const T& value) { + return std::all_of( + excluded.begin(), excluded.end(), + [&value](const T& other) { return value != other; }); + }); + return ValuesIn(filtered); +} + +std::string GenerateInstruction(const Operand operand) { + switch (operand) { + case OpMemoryBarrier: + return "OpMemoryBarrier %scope %semantics"; + case OpControlBarrier: + return "OpControlBarrier %uint_2 %scope %semantics"; + case OpAtomicLoad: + return "%result = OpAtomicLoad %uint %var %scope %semantics"; + case OpAtomicStore: + return "OpAtomicStore %var %scope %semantics %uint_1"; + case OpAtomicExchange: + return "%result = OpAtomicExchange %uint %var %scope %semantics %uint_1"; + case OpAtomicCompareExchangeEqual: + return "%result = OpAtomicCompareExchange %uint %var %scope %semantics " + "%semantics_min %uint_1 %uint_0"; + case OpAtomicCompareExchangeUnequal: + return "%result = OpAtomicCompareExchange %uint %var %scope " + "%semantics_max %semantics %uint_1 %uint_0"; + case OpAtomicIIncrement: + return "%result = OpAtomicIIncrement %uint %var %scope %semantics"; + case OpAtomicIDecrement: + return "%result = OpAtomicIDecrement %uint %var %scope %semantics"; + case OpAtomicIAdd: + return "%result = OpAtomicIAdd %uint %var %scope %semantics %uint_1"; + case OpAtomicISub: + return "%result = OpAtomicISub %uint %var %scope %semantics %uint_1"; + case OpAtomicSMin: + return "%result = OpAtomicSMin %uint %var %scope %semantics %uint_1"; + case OpAtomicUMin: + return "%result = OpAtomicUMin %uint %var %scope %semantics %uint_1"; + case OpAtomicSMax: + return "%result = OpAtomicSMax %uint %var %scope %semantics %uint_1"; + case OpAtomicUMax: + return "%result = OpAtomicUMax %uint %var %scope %semantics %uint_1"; + case OpAtomicAnd: + return "%result = OpAtomicAnd %uint %var %scope %semantics %uint_1"; + case OpAtomicOr: + return "%result = OpAtomicOr %uint %var %scope %semantics %uint_1"; + case OpAtomicXor: + return "%result = OpAtomicXor %uint %var %scope %semantics %uint_1"; + default: + return ""; + } +} + +std::string GenerateVulkanCode(const std::string instruction, + const uint32_t semantics, + const uint32_t semantics2 = 0) { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical Vulkan +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 32 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%func = OpTypeFunction %void +%uint_ptr = OpTypePointer Workgroup %uint +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_4 = OpConstant %uint 4 +%scope = OpConstant %uint 5 +%semantics = OpConstant %uint )" + << semantics << R"( +%semantics2 = OpConstant %uint )" + << semantics2 << R"( +%semantics_min = OpConstant %uint )" + << (semantics & Volatile) << R"( +%semantics_max = OpConstant %uint )" + << (32712 | (semantics & Volatile)) << R"( +%var = OpVariable %uint_ptr Workgroup +%main = OpFunction %void None %func +%label = OpLabel +)" << instruction + << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +using VulkanMemorySemantics = + spvtest::ValidateBase<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t, + uint32_t, Operand, TestResult>>; +using VulkanUnequalMemorySemantics = spvtest::ValidateBase< + std::tuple<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, + uint32_t, uint32_t, bool, uint32_t, TestResult>>; + +INSTANTIATE_TEST_SUITE_P( + ErrorMultipleMemoryOrderBits, VulkanMemorySemantics, + Combine( + Values(Acquire | Release, Acquire | AcqRel, Release | AcqRel, + Acquire | SeqCst, Release | SeqCst, AcqRel | SeqCst), + Values(None, Uniform | Workgroup | Image | Output), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesIn(Operands), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10865", + "Memory Semantics must have at most one non-relaxed memory order " + "bit set")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorSequentiallyConsistentMemoryOrder, VulkanMemorySemantics, + Combine( + Values(SeqCst), Values(None, Uniform | Workgroup | Image | Output), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesIn(Operands), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10866", + "Memory Semantics with SequentiallyConsistent memory order must " + "not be used in the Vulkan API")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorAtomicStoreWithAcquireMemoryOrder, VulkanMemorySemantics, + Combine(Values(Acquire, AcqRel), + Values(None, Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Visible, Available, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpAtomicStore), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10867", + "MemorySemantics must not use Acquire or AcquireRelease " + "memory order with AtomicStore")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorAtomicLoadWithReleaseMemoryOrder, VulkanMemorySemantics, + Combine(Values(Release, AcqRel), + Values(None, Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpAtomicLoad), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10868", + "MemorySemantics must not use Release or AcquireRelease " + "memory order with AtomicLoad")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMemoryBarrierWithRelaxedMemoryOrder, VulkanMemorySemantics, + Combine(Values(None), + Values(None, Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpMemoryBarrier), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10869", + "MemorySemantics must not use Relaxed memory order with " + "MemoryBarrier")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorNonRelaxedSemanticsWithoutStorageClass, VulkanMemorySemantics, + Combine(Values(Acquire, Release, AcqRel), Values(None), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpAtomicLoad, + OpAtomicStore), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10870", + "Memory Semantics with a non-relaxed memory order (Acquire, " + "Release, or AcquireRelease) must have at least one " + "Vulkan-supported storage class semantics bit set " + "(UniformMemory, WorkgroupMemory, ImageMemory, " + "or OutputMemory)")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorNonRelaxedSemanticsWithoutStorageClassLoad, VulkanMemorySemantics, + Combine(Values(Acquire), Values(None), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpAtomicLoad), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10870", + "Memory Semantics with a non-relaxed memory order (Acquire, " + "Release, or AcquireRelease) must have at least one " + "Vulkan-supported storage class semantics bit set " + "(UniformMemory, WorkgroupMemory, ImageMemory, or " + "OutputMemory)")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorNonRelaxedSemanticsWithoutStorageClassStore, VulkanMemorySemantics, + Combine(Values(Release), Values(None), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpAtomicStore), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10870", + "Memory Semantics with a non-relaxed memory order (Acquire, " + "Release, or AcquireRelease) must have at least one " + "Vulkan-supported storage class semantics bit set " + "(UniformMemory, WorkgroupMemory, ImageMemory, or " + "OutputMemory)")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorRelaxedSemanticsWithStorageClass, VulkanMemorySemantics, + Combine( + Values(None), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10871", + "Memory Semantics with at least one Vulkan-supported storage class " + "semantics bit set (UniformMemory, WorkgroupMemory, ImageMemory, " + "or OutputMemory) must use a non-relaxed memory order (Acquire, " + "Release, or AcquireRelease)")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMakeAvailableWithRelaxedMemoryOrder, VulkanMemorySemantics, + Combine(Values(None), Values(None), Values(Available, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10872", + "Memory Semantics with MakeAvailable bit set must use Release " + "or AcquireRelease memory order")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMakeAvailableWithAcquireMemoryOrder, VulkanMemorySemantics, + Combine(Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(Available, Available | Visible), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpAtomicStore), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10872", + "Memory Semantics with MakeAvailable bit set must use Release " + "or AcquireRelease memory order")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMakeVisibleWithRelaxedMemoryOrder, VulkanMemorySemantics, + Combine(Values(None), Values(None), Values(Visible), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10873", + "Memory Semantics with MakeVisible bit set must use Acquire " + "or AcquireRelease memory order")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMakeVisibleWithReleaseMemoryOrder, VulkanMemorySemantics, + Combine(Values(Release), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(Visible, Available | Visible), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpAtomicLoad), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10873", + "Memory Semantics with MakeVisible bit set must use Acquire " + "or AcquireRelease memory order")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorVolatileBarrierWithRelaxedSemantics, VulkanMemorySemantics, + Combine(Values(None), Values(None), Values(None), Values(Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpControlBarrier), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10874", + "Memory Semantics with Volatile bit set must not be used with " + "barrier instructions")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorVolatileBarrierWithNonRelaxedSemantics, VulkanMemorySemantics, + Combine(Values(Acquire, Acquire | Visible, Release, Release | Available, + AcqRel, AcqRel | Visible, AcqRel | Available, + AcqRel | Available | Visible), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None), Values(Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpControlBarrier, OpMemoryBarrier), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-MemorySemantics-10874", + "Memory Semantics with Volatile bit set must not be used with " + "barrier instructions")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorCompareExchangeUnequalSemanticsWithRelease, VulkanMemorySemantics, + Combine(Values(Release, AcqRel, AcqRel | Visible), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Available), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpAtomicCompareExchangeUnequal), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10875", + "AtomicCompareExchange Unequal Memory Semantics must not use " + "Release or AcquireRelease memory order")))); + +INSTANTIATE_TEST_SUITE_P( + SuccessAtomicsRelaxed, VulkanMemorySemantics, + Combine(Values(None), Values(None), Values(None), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier, + OpControlBarrier), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SuccessAtomicsAcquire, VulkanMemorySemantics, + Combine(Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Visible), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier, + OpControlBarrier, OpAtomicStore), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SuccessAtomicsRelease, VulkanMemorySemantics, + Combine(Values(Release), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Available), Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier, + OpControlBarrier, OpAtomicLoad, + OpAtomicCompareExchangeUnequal), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SuccessAtomicsAcqRel, VulkanMemorySemantics, + Combine(Values(AcqRel), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Available, Visible, Available | Visible), + Values(None, Volatile), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier, + OpControlBarrier, OpAtomicLoad, OpAtomicStore, + OpAtomicCompareExchangeUnequal), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SuccessBarriersRelaxed, VulkanMemorySemantics, + Combine(Values(None), Values(None), Values(None), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpControlBarrier), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SuccessBarriersNonRelaxed, VulkanMemorySemantics, + Combine(Values(Acquire, Acquire | Visible, Release, Release | Available, + AcqRel, AcqRel | Available, AcqRel | Visible, + AcqRel | Available | Visible), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(OpControlBarrier, OpMemoryBarrier), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMemoryOrderTooWeak, VulkanUnequalMemorySemantics, + Combine(Values(None), Values(None), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None, Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(true, false), Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10876", + "AtomicCompareExchange Unequal Memory Semantics must not use a " + "stronger memory order than the corresponding Equal Memory " + "Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMissingStorageClassSemanticsFlags, VulkanUnequalMemorySemantics, + Combine( + Values(Acquire, Acquire | Visible, Release, Release | Available, AcqRel, + AcqRel | Visible, AcqRel | Available, + AcqRel | Available | Visible), + Values(Uniform | Workgroup), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), Values(Uniform | Image, Output), Values(None, Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(true, false), Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10877", + "AtomicCompareExchange Unequal Memory Semantics must not have any " + "Vulkan-supported storage class semantics bit set (UniformMemory, " + "WorkgroupMemory, ImageMemory, or OutputMemory) unless this bit is " + "also set in the corresponding Equal Memory Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMissingMakeVisibleFlag, VulkanUnequalMemorySemantics, + Combine(Values(Acquire, Release, Release | Available, AcqRel, + AcqRel | Available), + Values(Uniform | Workgroup | Image | Output), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(true, false), Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10878", + "AtomicCompareExchange Unequal Memory Semantics must not have " + "MakeVisible bit set unless this bit is also set in the " + "corresponding Equal Memory Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMismatchingVolatileFlagsRelaxedAndRelaxed, + VulkanUnequalMemorySemantics, + Combine( + Values(None), Values(None), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(None), + Values(None), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(false), + Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10879", + "AtomicCompareExchange Unequal Memory Semantics must have Volatile " + "bit set if and only if this bit is also set in the corresponding " + "Equal Memory Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMismatchingVolatileFlagsNonRelaxedAndRelaxed, + VulkanUnequalMemorySemantics, + Combine( + Values(Acquire, Acquire | Visible, Release, Release | Available, AcqRel, + AcqRel | Visible, AcqRel | Available, + AcqRel | Available | Visible), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None), Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(None), Values(None), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(false), + Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10879", + "AtomicCompareExchange Unequal Memory Semantics must have Volatile " + "bit set if and only if this bit is also set in the corresponding " + "Equal Memory Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMismatchingVolatileFlagsNonRelaxedAndAcquire, + VulkanUnequalMemorySemantics, + Combine( + Values(Acquire, Acquire | Visible, Release, Release | Available, AcqRel, + AcqRel | Visible, AcqRel | Available, + AcqRel | Available | Visible), + Values(Uniform | Workgroup | Image | Output), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None), Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(false), Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10879", + "AtomicCompareExchange Unequal Memory Semantics must have Volatile " + "bit set if and only if this bit is also set in the corresponding " + "Equal Memory Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + ErrorMismatchingVolatileFlagsNonRelaxedAndAcquireVisible, + VulkanUnequalMemorySemantics, + Combine( + Values(Acquire, AcqRel, AcqRel | Available), + Values(Uniform | Workgroup | Image | Output), Values(Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(false), + Values(None, Volatile), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "VUID-StandaloneSpirv-UnequalMemorySemantics-10879", + "AtomicCompareExchange Unequal Memory Semantics must have Volatile " + "bit set if and only if this bit is also set in the corresponding " + "Equal Memory Semantics")))); + +INSTANTIATE_TEST_SUITE_P( + SuccessNonRelaxedAndAcquire, VulkanUnequalMemorySemantics, + Combine(Values(Acquire, Acquire | Visible, Release, Release | Available, + AcqRel, AcqRel | Visible, AcqRel | Available, + AcqRel | Available | Visible), + Values(Uniform | Workgroup | Image | Output), Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(None), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(true), Values(None, Volatile), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SuccessNonRelaxedAndAcquireVisible, VulkanUnequalMemorySemantics, + Combine(Values(Acquire, AcqRel, AcqRel | Available), + Values(Uniform | Workgroup | Image | Output), Values(Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(Acquire), + Values(Uniform, Workgroup, Image, Output, + Uniform | Workgroup | Image | Output), + Values(Visible), + Values(None, Subgroup | CrossWorkgroup | AtomicCounter), + Values(true), Values(None, Volatile), Values(TestResult()))); + +TEST_P(VulkanMemorySemantics, Case) { + const uint32_t semantics = std::get<0>(GetParam()) | std::get<1>(GetParam()) | + std::get<2>(GetParam()) | std::get<3>(GetParam()) | + std::get<4>(GetParam()); + const Operand operand = std::get<5>(GetParam()); + const TestResult& result = std::get<6>(GetParam()); + const std::string instruction = GenerateInstruction(operand); + + CompileSuccessfully(GenerateVulkanCode(instruction, semantics), + SPV_ENV_VULKAN_1_4); + ASSERT_EQ(result.result, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + if (result.vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(result.vuid)); + } + if (result.error) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(result.error)); + } +} + +TEST_P(VulkanUnequalMemorySemantics, Case) { + const uint32_t equal_volatile = std::get<9>(GetParam()); + const uint32_t unequal_volatile = + std::get<8>(GetParam()) ? equal_volatile : Volatile ^ equal_volatile; + const uint32_t equal = std::get<0>(GetParam()) | std::get<1>(GetParam()) | + std::get<2>(GetParam()) | std::get<3>(GetParam()) | + equal_volatile; + const uint32_t unequal = std::get<4>(GetParam()) | std::get<5>(GetParam()) | + std::get<6>(GetParam()) | std::get<7>(GetParam()) | + unequal_volatile; + const TestResult& result = std::get<10>(GetParam()); + const std::string instruction = + "%result = OpAtomicCompareExchange %uint %var %scope %semantics " + "%semantics2 %uint_1 %uint_0"; + + CompileSuccessfully(GenerateVulkanCode(instruction, equal, unequal), + SPV_ENV_VULKAN_1_4); + ASSERT_EQ(result.result, ValidateInstructions(SPV_ENV_VULKAN_1_4)); + if (result.vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(result.vuid)); + } + if (result.error) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(result.error)); + } +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 0a918c9..eecc2a1 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2018 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -597,10 +599,39 @@ getDiagnosticString(), HasSubstr( "The Result Type of OpArrayLength <id> '10[%10]' must be OpTypeInt " - "with width 32 and signedness 0.\n %10 = OpArrayLength %float %9 " + "with width 32 or 64 and signedness 0.\n %10 = OpArrayLength %float " + "%9 " "0\n")); } +TEST_F(ValidateMemory, ArrayLenResultUnsizedPointer) { + std::string spirv = R"( + OpCapability ClipDistance + OpMemoryModel Logical GLSL450 + OpName %65312 "arrayLen" + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %_struct_5 = OpTypeStruct %v2float +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %6 = OpVariable %_ptr_Uniform__struct_5 Uniform +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %2105376 = OpFunction %void None %8 + %8224 = OpLabel + %65312 = OpArrayLength %_ptr_Uniform_v2float %6 538976288 + OpUnreachable + OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The Result Type of OpArrayLength <id> '1[%arrayLen]' must be " + "OpTypeInt with width 32 or 64 and signedness 0.")); +} + TEST_F(ValidateMemory, ArrayLenResultNot32bits) { std::string spirv = R"( OpCapability Shader @@ -630,7 +661,8 @@ getDiagnosticString(), HasSubstr( "The Result Type of OpArrayLength <id> '11[%11]' must be OpTypeInt " - "with width 32 and signedness 0.\n %11 = OpArrayLength %ushort %10 " + "with width 32 or 64 and signedness 0.\n %11 = OpArrayLength " + "%ushort %10 " "0\n")); } @@ -662,7 +694,8 @@ getDiagnosticString(), HasSubstr( "The Result Type of OpArrayLength <id> '11[%11]' must be OpTypeInt " - "with width 32 and signedness 0.\n %11 = OpArrayLength %int %10 " + "with width 32 or 64 and signedness 0.\n %11 = OpArrayLength %int " + "%10 " "0\n")); } @@ -1816,6 +1849,68 @@ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); } +TEST_F(ValidateMemory, PSBStoreAlignedZero) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +OpStore %val2 %uint_1 Aligned 0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Memory accesses Aligned operand value 0 is not a power of two")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedNonPoT) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +OpStore %val2 %uint_1 Aligned 3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Memory accesses Aligned operand value 3 is not a power of two.")); +} + TEST_F(ValidateMemory, PSBCopyMemoryAlignedSuccess) { const std::string body = R"( OpCapability PhysicalStorageBufferAddresses @@ -1826,7 +1921,6 @@ OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %val1 AliasedPointer -%int = OpTypeInt 32 0 %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1838,8 +1932,8 @@ %val1 = OpVariable %pptr_f Function %val2 = OpLoad %ptr %val1 %val3 = OpLoad %ptr %val1 -OpCopyMemory %val2 %val3 Aligned 4 -OpCopyMemory %val3 %val2 Aligned 4 Aligned 4 +OpCopyMemory %val2 %val3 Aligned 8 +OpCopyMemory %val3 %val2 Aligned 8 Aligned 8 OpReturn OpFunctionEnd )"; @@ -1858,7 +1952,6 @@ OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %val1 AliasedPointer -%int = OpTypeInt 32 0 %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1870,7 +1963,7 @@ %val1 = OpVariable %pptr_f Function %val2 = OpLoad %ptr %val1 %val3 = OpLoad %ptr %val1 -OpCopyMemory %val2 %val3 Volatile Aligned 4 +OpCopyMemory %val2 %val3 Volatile Aligned 8 OpReturn OpFunctionEnd )"; @@ -1894,7 +1987,6 @@ OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %val1 AliasedPointer -%int = OpTypeInt 32 0 %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1906,7 +1998,7 @@ %val1 = OpVariable %pptr_f Function %val2 = OpLoad %ptr %val1 %val3 = OpLoad %ptr %val1 -OpCopyMemory %val2 %val3 Aligned 4 Volatile +OpCopyMemory %val2 %val3 Aligned 8 Volatile OpReturn OpFunctionEnd )"; @@ -1930,7 +2022,6 @@ OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %val1 AliasedPointer -%int = OpTypeInt 32 0 %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1984,6 +2075,562 @@ HasSubstr("PhysicalStorageBuffer must not be used with OpVariable")); } +TEST_F(ValidateMemory, PSBStoreAlignedOneWithUvec4) { + const std::string body = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %Ptr Block + OpMemberDecorate %Ptr 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Ptr PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_Ptr + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %Ptr = OpTypeStruct %v4uint +%_ptr_PhysicalStorageBuffer_Ptr = OpTypePointer PhysicalStorageBuffer %Ptr +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_Ptr + %uint_0 = OpConstant %uint 0 + %20 = OpConstantComposite %v4uint %uint_0 %uint_0 %uint_0 %uint_0 +%_ptr_PhysicalStorageBuffer_v4uint = OpTypePointer PhysicalStorageBuffer %v4uint + %main = OpFunction %void None %4 + %6 = OpLabel + %17 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0 + %18 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %17 + %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_v4uint %18 %int_0 + OpStore %22 %20 Aligned 1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 1 is too small, " + "the largest scalar type is 4 bytes")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedOneWithUint32) { + const std::string body = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %B Block + OpMemberDecorate %B 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint +%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B + %uint_0 = OpConstant %uint 0 +%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint + %main = OpFunction %void None %4 + %6 = OpLabel + %16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B %_ %int_0 + %17 = OpLoad %_ptr_PhysicalStorageBuffer_B %16 + %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %17 %int_0 + OpStore %20 %uint_0 Aligned 2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 2 is too small, " + "the largest scalar type is 4 bytes")); +} + +// https://github.com/KhronosGroup/glslang/issues/4024 +TEST_F(ValidateMemory, PSBStoreAlignedPointerNot8) { + const std::string body = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %B Block + OpMemberDecorate %B 0 Offset 0 + OpDecorate %A Block + OpMemberDecorate %A 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_A PhysicalStorageBuffer + %B = OpTypeStruct %_ptr_PhysicalStorageBuffer_A + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %uint +%_ptr_PhysicalStorageBuffer_A = OpTypePointer PhysicalStorageBuffer %A +%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B +%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_A = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_A + %uint_0 = OpConstant %uint 0 +%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint + %main = OpFunction %void None %4 + %6 = OpLabel + %18 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B %_ %int_0 + %19 = OpLoad %_ptr_PhysicalStorageBuffer_B %18 + %21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_A %19 %int_0 + %22 = OpLoad %_ptr_PhysicalStorageBuffer_A %21 Aligned 4 + %25 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %22 %int_0 + OpStore %25 %uint_0 Aligned 4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 4 is too small, " + "the largest scalar type is 8 bytes")); +} + +// https://godbolt.org/z/sbGv6a7os +TEST_F(ValidateMemory, PSBStoreAlignedStructCopyWithDeepDouble) { + const std::string body = R"( + OpCapability Shader + OpCapability Float64 + OpCapability PhysicalStorageBufferAddresses + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %A 0 Offset 0 + OpMemberDecorate %A 1 Offset 8 + OpMemberDecorate %B 0 Offset 0 + OpMemberDecorate %B 1 Offset 16 + OpMemberDecorate %C 0 Offset 0 + OpMemberDecorate %C 1 Offset 32 + OpDecorate %Ptr Block + OpMemberDecorate %Ptr 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Ptr PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_Ptr + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %double = OpTypeFloat 64 + %A = OpTypeStruct %uint %double + %B = OpTypeStruct %v3float %A + %C = OpTypeStruct %B %uint + %Ptr = OpTypeStruct %C +%_ptr_PhysicalStorageBuffer_Ptr = OpTypePointer PhysicalStorageBuffer %Ptr +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_Ptr + %A_0 = OpTypeStruct %uint %double + %B_0 = OpTypeStruct %v3float %A_0 + %C_0 = OpTypeStruct %B_0 %uint +%_ptr_Function_C_0 = OpTypePointer Function %C_0 +%_ptr_PhysicalStorageBuffer_C = OpTypePointer PhysicalStorageBuffer %C + %main = OpFunction %void None %4 + %6 = OpLabel + %newC = OpVariable %_ptr_Function_C_0 Function + %22 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0 + %23 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %22 + %29 = OpLoad %C_0 %newC + %31 = OpAccessChain %_ptr_PhysicalStorageBuffer_C %23 %int_0 + %32 = OpCopyLogical %C %29 + OpStore %31 %32 Aligned 4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 4 is too small, " + "the largest scalar type is 8 bytes")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedPtrAccessChain) { + const std::string body = R"( + OpCapability PhysicalStorageBufferAddresses + OpCapability Int64 + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + OpExtension "SPV_KHR_physical_storage_buffer" + %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %computeMain "main" %globalParams + OpExecutionMode %computeMain LocalSize 1 1 1 + OpDecorate %_ptr_PhysicalStorageBuffer_ulong ArrayStride 8 + OpDecorate %GlobalParams_std140 Block + OpMemberDecorate %GlobalParams_std140 0 Offset 0 + OpDecorate %globalParams Binding 0 + OpDecorate %globalParams DescriptorSet 0 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %12 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 +%_ptr_PhysicalStorageBuffer_ulong = OpTypePointer PhysicalStorageBuffer %ulong +%GlobalParams_std140 = OpTypeStruct %_ptr_PhysicalStorageBuffer_ulong +%_ptr_Uniform_GlobalParams_std140 = OpTypePointer Uniform %GlobalParams_std140 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBuffer_ulong = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_ulong + %ulong_1 = OpConstant %ulong 1 +%globalParams = OpVariable %_ptr_Uniform_GlobalParams_std140 Uniform +%computeMain = OpFunction %void None %12 + %13 = OpLabel + %36 = OpInBoundsAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_ulong %globalParams %int_0 + %37 = OpLoad %_ptr_PhysicalStorageBuffer_ulong %36 + %38 = OpPtrAccessChain %_ptr_PhysicalStorageBuffer_ulong %37 %int_0 + OpStore %38 %ulong_1 Aligned 4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 4 is too small, " + "the largest scalar type is 8 bytes")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedUntypedStorageBuffer) { + const std::string body = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability PhysicalStorageBufferAddresses + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %B Block + OpMemberDecorate %B 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_0 = OpConstant %uint 0 + %B = OpTypeStruct %uint +%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B +%untyped_ptr = OpTypeUntypedPointerKHR StorageBuffer + %_ = OpUntypedVariableKHR %untyped_ptr StorageBuffer %SSBO +%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint + %main = OpFunction %void None %4 + %6 = OpLabel + %16 = OpUntypedAccessChainKHR %untyped_ptr %SSBO %_ %int_0 + %17 = OpLoad %_ptr_PhysicalStorageBuffer_B %16 + %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %17 %int_0 + OpStore %20 %uint_0 Aligned 2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 2 is too small, " + "the largest scalar type is 4 bytes")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedUntypedPhysicalStorageBuffer) { + const std::string body = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability PhysicalStorageBufferAddresses + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %B Block + OpMemberDecorate %B 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_0 = OpConstant %uint 0 + %B = OpTypeStruct %uint +%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B +%untyped_ptr = OpTypeUntypedPointerKHR StorageBuffer + %_ = OpUntypedVariableKHR %untyped_ptr StorageBuffer %SSBO +%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint + %main = OpFunction %void None %4 + %6 = OpLabel + %16 = OpUntypedAccessChainKHR %untyped_ptr %SSBO %_ %int_0 + %17 = OpLoad %_ptr_PhysicalStorageBuffer_B %16 + %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %17 %int_0 + OpStore %20 %uint_0 Aligned 2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 2 is too small, " + "the largest scalar type is 4 bytes")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedVariousTypeSuccess) { + const std::string body = R"( + OpCapability Shader + OpCapability Float64 + OpCapability Int8 + OpCapability StorageBuffer8BitAccess + OpCapability PhysicalStorageBufferAddresses + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %Ptr Block + OpMemberDecorate %Ptr 0 Offset 0 + OpMemberDecorate %Ptr 1 Offset 4 + OpMemberDecorate %Ptr 2 Offset 8 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Ptr PhysicalStorageBuffer + %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_Ptr + %uchar = OpTypeInt 8 0 + %uint = OpTypeInt 32 0 + %double = OpTypeFloat 64 + %Ptr = OpTypeStruct %uchar %uint %double +%_ptr_PhysicalStorageBuffer_Ptr = OpTypePointer PhysicalStorageBuffer %Ptr +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_Ptr + %uchar_0 = OpConstant %uchar 0 +%_ptr_PhysicalStorageBuffer_uchar = OpTypePointer PhysicalStorageBuffer %uchar + %int_1 = OpConstant %int 1 + %uint_0 = OpConstant %uint 0 +%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint + %int_2 = OpConstant %int 2 + %double_0 = OpConstant %double 0 +%_ptr_PhysicalStorageBuffer_double = OpTypePointer PhysicalStorageBuffer %double + %main = OpFunction %void None %4 + %6 = OpLabel + %18 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0 + %19 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %18 + %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_uchar %19 %int_0 + OpStore %22 %uchar_0 Aligned 1 + %23 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0 + %24 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %23 + %28 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %24 %int_1 + OpStore %28 %uint_0 Aligned 4 + %29 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0 + %30 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %29 + %34 = OpAccessChain %_ptr_PhysicalStorageBuffer_double %30 %int_2 + OpStore %34 %double_0 Aligned 8 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +// https://github.com/KhronosGroup/SPIRV-Tools/issues/6322 +TEST_F(ValidateMemory, PSBStoreAlignedUntypedBitcaseSuccess) { + const std::string body = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VulkanMemoryModel +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Int8 +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_untyped_pointers" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 Vulkan +OpEntryPoint GLCompute %main "main" %id +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %id BuiltIn GlobalInvocationId +OpDecorate %untyped_phys_ptr ArrayStride 1 +OpDecorate %data_buffer Block +OpMemberDecorate %data_buffer 0 Offset 0 +OpDecorate %phys_ptrs_struct Block +OpMemberDecorate %phys_ptrs_struct 0 Offset 0 +OpMemberDecorate %phys_ptrs_struct 1 Offset 8 +OpDecorate %all_data_var DescriptorSet 0 +OpDecorate %all_data_var Binding 0 + +%uint32 = OpTypeInt 32 0 +%void = OpTypeVoid +%uint8 = OpTypeInt 8 0 +%vec3_uint32 = OpTypeVector %uint32 3 +%void_func = OpTypeFunction %void +%c_uint32_0 = OpConstant %uint32 0 +%c_uint32_1 = OpConstant %uint32 1 +%c_uint32_32 = OpConstant %uint32 32 + +%uint32_input_ptr = OpTypePointer Input %uint32 +%vec3_uint32_input_ptr = OpTypePointer Input %vec3_uint32 +%data_buffer = OpTypeStruct %uint8 +%untyped_phys_ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer +%uint8_phys_ptr = OpTypePointer PhysicalStorageBuffer %uint8 +%data_buffer_phys_ptr = OpTypePointer PhysicalStorageBuffer %data_buffer +%data_buffer_phys_ptr_ptr = OpTypePointer StorageBuffer %data_buffer_phys_ptr +%phys_ptrs_struct = OpTypeStruct %data_buffer_phys_ptr %data_buffer_phys_ptr +%phys_ptrs_struct_ptr = OpTypePointer StorageBuffer %phys_ptrs_struct +%all_data_var = OpVariable %phys_ptrs_struct_ptr StorageBuffer +%id = OpVariable %vec3_uint32_input_ptr Input +%main = OpFunction %void None %void_func +%label_main = OpLabel +%input_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_0 +%input = OpLoad %data_buffer_phys_ptr %input_ptr +%input_loc = OpAccessChain %uint8_phys_ptr %input %c_uint32_0 +%output_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_1 +%output = OpLoad %data_buffer_phys_ptr %output_ptr +%output_loc = OpUntypedAccessChainKHR %untyped_phys_ptr %data_buffer %output %c_uint32_0 +%bitcasted = OpBitcast %untyped_phys_ptr %input_loc +%bitcasted_val = OpLoad %uint8 %bitcasted Aligned 1 +OpStore %output_loc %bitcasted_val Aligned 1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +// https://github.com/KhronosGroup/SPIRV-Tools/issues/6322 +TEST_F(ValidateMemory, PSBStoreAlignedUntypedBitcase) { + const std::string body = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VulkanMemoryModel +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Int16 +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_untyped_pointers" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 Vulkan +OpEntryPoint GLCompute %main "main" %id +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %id BuiltIn GlobalInvocationId +OpDecorate %untyped_phys_ptr ArrayStride 1 +OpDecorate %data_buffer Block +OpMemberDecorate %data_buffer 0 Offset 0 +OpDecorate %phys_ptrs_struct Block +OpMemberDecorate %phys_ptrs_struct 0 Offset 0 +OpMemberDecorate %phys_ptrs_struct 1 Offset 8 +OpDecorate %all_data_var DescriptorSet 0 +OpDecorate %all_data_var Binding 0 + +%uint32 = OpTypeInt 32 0 +%void = OpTypeVoid +%uint16 = OpTypeInt 16 0 +%vec3_uint32 = OpTypeVector %uint32 3 +%void_func = OpTypeFunction %void +%c_uint32_0 = OpConstant %uint32 0 +%c_uint32_1 = OpConstant %uint32 1 +%c_uint32_32 = OpConstant %uint32 32 + +%uint32_input_ptr = OpTypePointer Input %uint32 +%vec3_uint32_input_ptr = OpTypePointer Input %vec3_uint32 +%data_buffer = OpTypeStruct %uint16 +%untyped_phys_ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer +%uint16_phys_ptr = OpTypePointer PhysicalStorageBuffer %uint16 +%data_buffer_phys_ptr = OpTypePointer PhysicalStorageBuffer %data_buffer +%data_buffer_phys_ptr_ptr = OpTypePointer StorageBuffer %data_buffer_phys_ptr +%phys_ptrs_struct = OpTypeStruct %data_buffer_phys_ptr %data_buffer_phys_ptr +%phys_ptrs_struct_ptr = OpTypePointer StorageBuffer %phys_ptrs_struct +%all_data_var = OpVariable %phys_ptrs_struct_ptr StorageBuffer +%id = OpVariable %vec3_uint32_input_ptr Input +%main = OpFunction %void None %void_func +%label_main = OpLabel +%input_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_0 +%input = OpLoad %data_buffer_phys_ptr %input_ptr +%input_loc = OpAccessChain %uint16_phys_ptr %input %c_uint32_0 +%output_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_1 +%output = OpLoad %data_buffer_phys_ptr %output_ptr +%output_loc = OpUntypedAccessChainKHR %untyped_phys_ptr %data_buffer %output %c_uint32_0 +%bitcasted = OpBitcast %untyped_phys_ptr %input_loc +%bitcasted_val = OpLoad %uint16 %bitcasted Aligned 1 +OpStore %output_loc %bitcasted_val Aligned 1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory accesses Aligned operand value 1 is too small, " + "the largest scalar type is 2 bytes")); +} + std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess, const std::string& loadMemoryAccess) { std::string s = R"( @@ -2168,7 +2815,8 @@ %116 = OpLoad %71 %111 %121 = OpLoad %6 %60 %122 = OpAccessChain %82 %120 %79 %121 -OpCooperativeMatrixStoreNV %122 %116 %84 %86 )" + storeMemoryAccess + R"( %81 +OpCooperativeMatrixStoreNV %122 %116 %84 %86 )" + + storeMemoryAccess + R"( %81 OpReturn OpFunctionEnd )"; @@ -2350,19 +2998,21 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -std::string GenCoopMatLoadStoreShaderKHR(const std::string& storeMemoryAccess, - const std::string& loadMemoryAccess, - unsigned layout = 0, - bool useSpecConstantLayout = false, - bool useStoreStride = true, - bool useLoadStride = true) { +std::string GenCoopMatLoadStoreShaderKHR( + const std::string& storeMemoryAccess, const std::string& loadMemoryAccess, + unsigned layout = 0, bool useSpecConstantLayout = false, + bool useStoreStride = true, bool useLoadStride = true, + bool useConstantStride = true, unsigned stride = 4, unsigned rows = 16, + unsigned cols = 16) { std::string s = R"( OpCapability Shader OpCapability GroupNonUniform OpCapability VulkanMemoryModelKHR OpCapability CooperativeMatrixKHR +OpCapability CooperativeMatrixLayoutsARM OpExtension "SPV_KHR_vulkan_memory_model" OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_ARM_cooperative_matrix_layouts" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %4 "main" %11 %21 @@ -2402,6 +3052,7 @@ %7 = OpTypeVector %6 2 %8 = OpTypePointer Function %7 %10 = OpTypePointer Input %6 +%ptr_uint_Function = OpTypePointer Function %6 %11 = OpVariable %10 Input %13 = OpConstant %6 2 %19 = OpTypeVector %6 3 @@ -2411,7 +3062,8 @@ %31 = OpTypePointer Function %6 %33 = OpConstant %6 1024 %34 = OpConstant %6 1 -%38 = OpConstant %6 8 +%cols = OpConstant %6 )" + + std::to_string(cols) + R"( %uint_0 = OpConstant %6 0 )"; if (useSpecConstantLayout) { @@ -2421,9 +3073,10 @@ } s += R"( %68 = OpTypeFloat 32 -%69 = OpConstant %6 16 -%70 = OpConstant %6 3 -%71 = OpTypeCooperativeMatrixKHR %68 %70 %69 %38 %uint_0 +%rows = OpConstant %6 )" + + std::to_string(rows) + R"( +%scope = OpConstant %6 3 +%71 = OpTypeCooperativeMatrixKHR %68 %scope %rows %cols %uint_0 %72 = OpTypePointer Function %71 %74 = OpTypeRuntimeArray %68 %75 = OpTypeStruct %74 @@ -2433,7 +3086,11 @@ %79 = OpConstant %78 0 %81 = OpConstant %6 5 %82 = OpTypePointer StorageBuffer %68 -%stride = OpConstant %6 64 +)"; + if (useConstantStride) { + s += "%stride = OpConstant %6 " + std::to_string(stride); + } + s += R"( %88 = OpTypePointer Private %71 %89 = OpVariable %88 Private %92 = OpTypeRuntimeArray %68 @@ -2464,6 +3121,7 @@ %5 = OpLabel %9 = OpVariable %8 Function %18 = OpVariable %8 Function +%stride_var = OpVariable %ptr_uint_Function Function %32 = OpVariable %31 Function %44 = OpVariable %31 Function %52 = OpVariable %31 Function @@ -2471,6 +3129,11 @@ %73 = OpVariable %72 Function %91 = OpVariable %72 Function %101 = OpVariable %72 Function +)"; + if (!useConstantStride) { + s += "%stride = OpLoad %6 %stride_var"; + } + s += R"( %12 = OpLoad %6 %11 %14 = OpUMod %6 %12 %13 %15 = OpLoad %6 %11 @@ -2491,7 +3154,7 @@ %37 = OpIMul %6 %33 %36 %40 = OpAccessChain %31 %18 %uint_0 %41 = OpLoad %6 %40 -%42 = OpIMul %6 %38 %41 +%42 = OpIMul %6 %cols %41 %43 = OpIAdd %6 %37 %42 OpStore %32 %43 %45 = OpAccessChain %31 %18 %34 @@ -2499,7 +3162,7 @@ %47 = OpIMul %6 %33 %46 %48 = OpAccessChain %31 %18 %uint_0 %49 = OpLoad %6 %48 -%50 = OpIMul %6 %38 %49 +%50 = OpIMul %6 %cols %49 %51 = OpIAdd %6 %47 %50 OpStore %44 %51 %53 = OpAccessChain %31 %18 %34 @@ -2507,7 +3170,7 @@ %55 = OpIMul %6 %33 %54 %56 = OpAccessChain %31 %18 %uint_0 %57 = OpLoad %6 %56 -%58 = OpIMul %6 %38 %57 +%58 = OpIMul %6 %cols %57 %59 = OpIAdd %6 %55 %58 OpStore %52 %59 %61 = OpAccessChain %31 %18 %34 @@ -2515,7 +3178,7 @@ %63 = OpIMul %6 %33 %62 %64 = OpAccessChain %31 %18 %uint_0 %65 = OpLoad %6 %64 -%66 = OpIMul %6 %38 %65 +%66 = OpIMul %6 %cols %65 %67 = OpIAdd %6 %63 %66 OpStore %60 %67 %80 = OpLoad %6 %32 @@ -2594,7 +3257,19 @@ true}, StrideMissingCase{ (unsigned)spv::CooperativeMatrixLayout::ColumnMajorKHR, true, - false})); + false}, + StrideMissingCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, + false, true}, + StrideMissingCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, + true, false}, + StrideMissingCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + false, true}, + StrideMissingCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + true, false})); TEST_P(ValidateCoopMatrixStrideMissing, CoopMatKHRLoadStrideMissingFail) { const StrideMissingCase& param = GetParam(); @@ -2643,14 +3318,198 @@ HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad")); } +TEST_F(ValidateMemory, + CoopMatLoadArmRowBlockedInterleavedLayoutNonConstantStrideFail) { + std::string spirv = GenCoopMatLoadStoreShaderKHR( + "MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerAvailableKHR|NonPrivatePointerKHR", + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, + false, /* useSpecConstantLayout */ + true, /* useStoreStride */ + true, /* useLoadStride */ + false /* useConstantStride */); + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryLayout 4202 requires Stride come from a " + "constant instruction")); +} + +TEST_F(ValidateMemory, + CoopMatLoadArmColumnBlockedInterleavedLayoutNonConstantStrideFail) { + std::string spirv = GenCoopMatLoadStoreShaderKHR( + "MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerAvailableKHR|NonPrivatePointerKHR", + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + false, /* useSpecConstantLayout */ + true, /* useStoreStride */ + true, /* useLoadStride */ + false /* useConstantStride */); + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryLayout 4203 requires Stride come from a " + "constant instruction")); +} + +struct StrideValueCase { + unsigned layout; + unsigned stride; + bool valid; +}; + +using ValidateCoopMatrixStrideValue = spvtest::ValidateBase<StrideValueCase>; + +INSTANTIATE_TEST_SUITE_P( + CoopMatrixStrideValue, ValidateCoopMatrixStrideValue, + Values( + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 1, + true}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 2, + true}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4, + true}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 8, + false}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 1, true}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 2, true}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 4, true}, + StrideValueCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 8, false})); + +TEST_P(ValidateCoopMatrixStrideValue, CoopMatLoadStoreStrideValue) { + const StrideValueCase& param = GetParam(); + std::string spirv = GenCoopMatLoadStoreShaderKHR( + "MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR", param.layout, + false /*useSpecConstantLayout*/, true /*useStoreStride*/, + true /*useLoadStride*/, true /*useConstantStride*/, param.stride); + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + if (param.valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryLayout " + std::to_string(param.layout) + + " requires Stride be 1, 2, or 4")); + } +} + +struct MatrixSizeCase { + unsigned layout; + unsigned rows; + unsigned cols; + unsigned stride; + bool valid_rows; + bool valid_cols; +}; + +using ValidateCoopMatrixSize = spvtest::ValidateBase<MatrixSizeCase>; + +INSTANTIATE_TEST_SUITE_P( + CoopMatrixMatrixSize, ValidateCoopMatrixSize, + Values( + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4, + 4, 1, true, true}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4, + 8, 2, true, true}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 2, + 4, 1, false, true}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 2, + 4, 2, false, false}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4, + 2, 1, true, false}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4, + 4, 2, true, false}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 4, 4, 1, true, true}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 8, 4, 2, true, true}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 4, 2, 1, true, false}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 4, 2, 2, false, false}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 2, 4, 1, false, true}, + MatrixSizeCase{ + (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM, + 4, 4, 2, false, true})); + +TEST_P(ValidateCoopMatrixSize, CoopMatLoadStoreMatrixSize) { + const MatrixSizeCase& param = GetParam(); + std::string spirv = GenCoopMatLoadStoreShaderKHR( + "MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR", param.layout, + false /*useSpecConstantLayout*/, true /*useStoreStride*/, + true /*useLoadStride*/, true /*useConstantStride*/, param.stride, + param.rows, param.cols); + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + + uint32_t rows_required_multiple = 4; + uint32_t cols_required_multiple = + 16 / 4; // The shader always uses FP32 matrix components + + if (param.layout == + (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) { + cols_required_multiple *= param.stride; + } + if (param.layout == + (uint64_t)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM) { + rows_required_multiple *= param.stride; + } + + if (param.valid_rows && param.valid_cols) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + } else if (param.valid_rows) { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryLayout " + std::to_string(param.layout) + + " with a Stride of " + std::to_string(param.stride) + + " requires that the number of columns be a multiple of " + + std::to_string(cols_required_multiple))); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryLayout " + std::to_string(param.layout) + + " with a Stride of " + std::to_string(param.stride) + + " requires that the number of rows be a multiple of " + + std::to_string(rows_required_multiple))); + } +} + TEST_F(ValidateMemory, CoopMatKHRInvalidStorageClassFail) { const std::string body = R"( OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -2673,8 +3532,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2688,9 +3547,10 @@ OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -2712,8 +3572,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("The Result Type of OpCooperativeMatrixLengthKHR <id> " @@ -2726,9 +3586,10 @@ OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -2750,8 +3611,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("The type in OpCooperativeMatrixLengthKHR <id> '5[%uint]' " @@ -2764,9 +3625,10 @@ OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" %void = OpTypeVoid %func = OpTypeFunction %void @@ -2788,8 +3650,8 @@ OpReturn OpFunctionEnd)"; - CompileSuccessfully(body.c_str()); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, VulkanRTAOutsideOfStructBad) { @@ -2820,7 +3682,8 @@ "OpVariable, <id> '5[%5]', is attempting to create memory for an " "illegal type, OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray " "can only appear as the final member of an OpTypeStruct, thus cannot " - "be instantiated via OpVariable\n %5 = OpVariable " + "be instantiated via OpVariable, unless the RuntimeDescriptorArray " + "Capability is declared\n %5 = OpVariable " "%_ptr_UniformConstant__runtimearr_2 UniformConstant\n")); } @@ -2834,6 +3697,10 @@ OpExecutionMode %func OriginUpperLeft OpDecorate %struct Block OpMemberDecorate %struct 0 Offset 0 +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 1 %sampler_t = OpTypeSampler %uint = OpTypeInt 32 0 %array_t = OpTypeRuntimeArray %sampler_t @@ -2883,7 +3750,7 @@ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable " + HasSubstr("For Vulkan with RuntimeDescriptorArray, a variable " "containing OpTypeRuntimeArray must have storage class of " "StorageBuffer, Uniform, or UniformConstant.\n %5 = " "OpVariable %_ptr_Workgroup__runtimearr_uint Workgroup\n")); @@ -2898,6 +3765,8 @@ OpDecorate %array_t ArrayStride 4 OpMemberDecorate %struct_t 0 Offset 0 OpDecorate %struct_t Block +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 %uint_t = OpTypeInt 32 0 %array_t = OpTypeRuntimeArray %uint_t %struct_t = OpTypeStruct %array_t @@ -2989,6 +3858,8 @@ OpDecorate %array_t ArrayStride 4 OpMemberDecorate %struct_t 0 Offset 0 OpDecorate %struct_t BufferBlock +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 %uint_t = OpTypeInt 32 0 %array_t = OpTypeRuntimeArray %uint_t %struct_t = OpTypeStruct %array_t @@ -3006,13 +3877,17 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } -TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) { +TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlock) { std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 16 OpDecorate %struct_t Block +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %2 Binding 0 +OpDecorate %2 DescriptorSet 0 %uint_t = OpTypeInt 32 0 %array_t = OpTypeRuntimeArray %uint_t %struct_t = OpTypeStruct %array_t @@ -3027,14 +3902,41 @@ )"; CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanArrayLengthUniformRuntimeArray) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 16 +OpDecorate %struct_t Block +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %2 Binding 0 +OpDecorate %2 DescriptorSet 0 +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer Uniform %struct_t +%2 = OpVariable %struct_ptr Uniform +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +%length = OpArrayLength %uint_t %2 0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); + AnyVUID("VUID-StandaloneSpirv-OpArrayLength-11805")); EXPECT_THAT(getDiagnosticString(), - HasSubstr("For Vulkan, an OpTypeStruct variable containing an " - "OpTypeRuntimeArray must be decorated with BufferBlock " - "if it has storage class Uniform.\n %6 = OpVariable " - "%_ptr_Uniform__struct_2 Uniform\n")); + HasSubstr("OpArrayLength must not be used on the " + "OpTypeRuntimeArray inside a Uniform block")); } TEST_F(ValidateMemory, VulkanRTAInsideRTABad) { @@ -3112,9 +4014,10 @@ OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t ArrayStride 4 OpMemberDecorate %struct_t 0 Offset 0 OpDecorate %struct_t Block +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 %uint_t = OpTypeInt 32 0 %struct_t = OpTypeStruct %uint_t %array_t = OpTypeRuntimeArray %struct_t @@ -3358,9 +4261,10 @@ OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft OpDecorate %inner_array_t ArrayStride 4 -OpDecorate %array_t ArrayStride 4 OpMemberDecorate %struct_t 0 Offset 0 OpDecorate %struct_t Block +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 %uint_t = OpTypeInt 32 0 %inner_array_t = OpTypeRuntimeArray %uint_t %struct_t = OpTypeStruct %inner_array_t @@ -3388,9 +4292,10 @@ OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft OpDecorate %inner_array_t ArrayStride 4 -OpDecorate %array_t ArrayStride 4 OpMemberDecorate %struct_t 0 Offset 0 OpDecorate %struct_t Block +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 %uint_t = OpTypeInt 32 0 %inner_array_t = OpTypeRuntimeArray %uint_t %struct_t = OpTypeStruct %inner_array_t @@ -3761,7 +4666,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Instruction cannot for logical addressing model be " + HasSubstr("Instruction on logical pointers cannot be " "used without a variable pointers capability")); } } @@ -3884,8 +4789,11 @@ %void = OpTypeVoid %bool = OpTypeBool %int = OpTypeInt 32 0 +%float = OpTypeFloat 32 %ptr_int = OpTypePointer Private %int %var = OpVariable %ptr_int Private +%ptr_float = OpTypePointer Private %float +%var2 = OpVariable %ptr_float Private %func_ty = OpTypeFunction %void %func = OpFunction %void None %func_ty %1 = OpLabel @@ -3898,7 +4806,7 @@ spirv += " %bool "; } - spirv += R"(%var %ld + spirv += R"(%var %var2 OpReturn OpFunctionEnd )"; @@ -3909,6 +4817,223 @@ HasSubstr("The types of Operand 1 and Operand 2 must match")); } +TEST_P(ValidatePointerComparisons, GoodUntypedPointerSameType) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_P(ValidatePointerComparisons, GoodUntypedPointerSameStorageClass) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr1 = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr1 StorageBuffer +%ptr2 = OpTypeUntypedPointerKHR StorageBuffer +%var2 = OpUntypedVariableKHR %ptr2 StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + if (operation == "OpPtrDiff") { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The types of Operand 1 and Operand 2 must match")); + } else { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + } +} + +TEST_P(ValidatePointerComparisons, BadUntypedPointerDiffStorageClass) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr1 = OpTypeUntypedPointerKHR StorageBuffer +%var1 = OpUntypedVariableKHR %ptr1 StorageBuffer +%ptr2 = OpTypeUntypedPointerKHR Workgroup +%var2 = OpUntypedVariableKHR %ptr2 Workgroup %int +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var1 %var2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + if (operation == "OpPtrDiff") { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The types of Operand 1 and Operand 2 must match")); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer storage classes must match")); + } +} + +TEST_P(ValidatePointerComparisons, GoodMixedPointerSameStorageClass) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr1 = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr1 StorageBuffer +%ptr2 = OpTypePointer StorageBuffer %int +%var2 = OpVariable %ptr2 StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + if (operation == "OpPtrDiff") { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The types of Operand 1 and Operand 2 must match")); + } else { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + } +} + +TEST_P(ValidatePointerComparisons, BadMixedPointerDiffStorageClass) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr1 = OpTypeUntypedPointerKHR StorageBuffer +%var1 = OpUntypedVariableKHR %ptr1 StorageBuffer +%ptr2 = OpTypePointer Workgroup %int +%var2 = OpVariable %ptr2 Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var1 %var2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + if (operation == "OpPtrDiff") { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The types of Operand 1 and Operand 2 must match")); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer storage classes must match")); + } +} + INSTANTIATE_TEST_SUITE_P(PointerComparisons, ValidatePointerComparisons, Values("OpPtrEqual", "OpPtrNotEqual", "OpPtrDiff")); @@ -5169,8 +6294,8 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); EXPECT_THAT(getDiagnosticString(), AnyVUID(" VUID-StandaloneSpirv-OpVariable-04734")); EXPECT_THAT(getDiagnosticString(), @@ -5197,8 +6322,8 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } TEST_F(ValidateMemory, LoadRuntimeArray) { @@ -5431,7 +6556,45 @@ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Vulkan requires that data type be specified")); + AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11167")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Storage class is StorageBuffer, but Vulkan requires that Data Type " + "be specified when not using UniformConstant storage class")); +} + +TEST_F(ValidateMemory, UntypedVariableNoDataTypeNonHeapVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpCapability DescriptorHeapEXT +OpCapability UntypedPointersKHR +OpExtension "SPV_EXT_descriptor_heap" +OpExtension "SPV_KHR_untyped_pointers" +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%ptr = OpTypeUntypedPointerKHR UniformConstant +%var = OpUntypedVariableKHR %ptr UniformConstant +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11347")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Storage class is UniformConstant, but Vulkan requires " + "that Data Type be specified if the variable is not " + "decorated with SamplerHeapEXT or ResourceHeapEXT")); } TEST_F(ValidateMemory, PtrAccessChainArrayStrideBad) { @@ -5674,8 +6837,8 @@ CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds")); - EXPECT_THAT(getDiagnosticString(), HasSubstr("cannot find index -224")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index at word 4 may not have a negative value")); } TEST_F(ValidateMemory, AccessChainNegativeStructIndex64) { @@ -5702,8 +6865,8 @@ CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds")); - EXPECT_THAT(getDiagnosticString(), HasSubstr("cannot find index -224")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index at word 4 may not have a negative value")); } TEST_F(ValidateMemory, UntypedVariableFunctionOutsideFunction) { @@ -5863,6 +7026,7 @@ const bool ptr = opcode == "OpUntypedPtrAccessChainKHR" || opcode == "OpUntypedInBoundsPtrAccessChainKHR"; const std::string extra_param = ptr ? "%int_0" : ""; + const std::string deco = ptr ? "OpDecorate %ptr_ssbo ArrayStride 4" : ""; const std::string spirv = R"( OpCapability Shader @@ -5873,6 +7037,7 @@ OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" +)" + deco + R"( %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 @@ -5889,6 +7054,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -5898,6 +7065,7 @@ const bool ptr = opcode == "OpUntypedPtrAccessChainKHR" || opcode == "OpUntypedInBoundsPtrAccessChainKHR"; const std::string extra_param = ptr ? "%int_0" : ""; + const std::string deco = ptr ? "OpDecorate %ptr ArrayStride 4" : ""; const std::string spirv = R"( OpCapability Shader @@ -5908,6 +7076,7 @@ OpExtension "SPV_KHR_untyped_pointers" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" +)" + deco + R"( %void = OpTypeVoid %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 @@ -5924,6 +7093,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -5961,6 +7132,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6000,6 +7173,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6038,6 +7213,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6076,6 +7253,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6114,6 +7293,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6153,6 +7334,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( @@ -6194,6 +7377,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6233,6 +7418,8 @@ OpFunctionEnd )"; + getValidatorOptions()->relax_logical_pointer = + opcode == "OpUntypedInBoundsPtrAccessChainKHR"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -6568,6 +7755,41 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr("Size must be a multiple of 2")); } +TEST_F(ValidateMemory, CopyMemorySizedVulkanConstant) { + const std::string spirv = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %v1 %v2 + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %struct Block + OpDecorate %v1 DescriptorSet 0 + OpDecorate %v1 Binding 0 + OpDecorate %v2 DescriptorSet 0 + OpDecorate %v2 Binding 0 + OpMemberDecorate %struct 0 Offset 0 + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 + %struct = OpTypeStruct %int + %ptr = OpTypeUntypedPointerKHR StorageBuffer + %v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct + %v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpCopyMemorySized %v2 %v1 %int_2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Size must be a multiple of 4")); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-Size-11165")); +} + TEST_F(ValidateMemory, PtrEqualUntypedPointersGood) { const std::string spirv = R"( OpCapability Shader @@ -6851,8 +8073,9 @@ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("must be OpTypeInt with width 32 and signedness 0")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be OpTypeInt with width 32 or 64 and signedness 0")); } TEST_F(ValidateMemory, UntypedArrayLengthBadPointer) { @@ -6886,7 +8109,46 @@ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Pointer must be an untyped pointer")); + HasSubstr("Pointer must be an untyped pointer object")); +} + +TEST_F(ValidateMemory, UntypedArrayLengthBadPointer2) { + const std::string spirv = R"( + OpCapability Shader + OpCapability UntypedPointersKHR + OpCapability Int64 + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %b + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_runtimearr_float ArrayStride 4 + OpDecorate %B Block + OpMemberDecorate %B 0 NonWritable + OpMemberDecorate %B 0 Offset 0 + OpDecorate %b NonWritable + OpDecorate %b Binding 0 + OpDecorate %b DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float + %B = OpTypeStruct %_runtimearr_float + %ptr = OpTypeUntypedPointerKHR StorageBuffer + %b = OpUntypedVariableKHR %ptr StorageBuffer %B + %ulong = OpTypeInt 64 0 + %long = OpTypeInt 64 1 + %main = OpFunction %void None %4 + %6 = OpLabel + %13 = OpUntypedArrayLengthKHR %ulong %b %ptr 0 + %15 = OpBitcast %long %13 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer must be an untyped pointer object")); } TEST_F(ValidateMemory, UntypedArrayLengtBadStruct) { @@ -7171,6 +8433,639 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } +TEST_F(ValidateMemory, PtrAccessChainElementNotInteger) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %ptr_int ArrayStride 4 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%array = OpTypeArray %int %int_4 +%ptr_array = OpTypePointer Workgroup %array +%ptr_int = OpTypePointer Workgroup %int +%var = OpVariable %ptr_array Workgroup +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ptr_gep = OpPtrAccessChain %ptr_int %gep %float_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Element must be an integer")); +} + +TEST_F(ValidateMemory, PtrAccessChainElementNotIntegerUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpCapability WorkgroupMemoryExplicitLayoutKHR +OpExtension "SPV_KHR_untyped_pointers" +OpExtension "SPV_KHR_workgroup_memory_explicit_layout" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %ptr_int ArrayStride 4 +OpDecorate %array ArrayStride 4 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%array = OpTypeArray %int %int_4 +%block = OpTypeStruct %array +%ptr_block = OpTypeUntypedPointerKHR Workgroup +%ptr_int = OpTypeUntypedPointerKHR Workgroup +%var = OpUntypedVariableKHR %ptr_block Workgroup %block +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpUntypedAccessChainKHR %ptr_int %block %var %int_0 %int_0 +%ptr_gep = OpUntypedPtrAccessChainKHR %ptr_int %int %gep %float_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Element must be an integer")); +} + +TEST_F(ValidateMemory, PtrAccessChainElementBlockArrayNonZeroConstant) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +%block = OpTypeStruct %int +%array = OpTypeArray %block %int_4 +%ptr_array = OpTypePointer StorageBuffer %array +%ptr_block = OpTypePointer StorageBuffer %block +%var = OpVariable %ptr_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_block %var %int_0 +%ptr_gep = OpPtrAccessChain %ptr_block %gep %int_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Element must be 0 if the interpretation type is a " + "Block- or BufferBlock-decorated structure")); +} + +TEST_F(ValidateMemory, PtrAccessChainElementBlockArrayNonZeroConstantUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +%block = OpTypeStruct %int +%array = OpTypeArray %block %int_4 +%ptr_array = OpTypeUntypedPointerKHR StorageBuffer +%ptr_block = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr_array StorageBuffer %array +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpUntypedAccessChainKHR %ptr_block %array %var %int_0 +%ptr_gep = OpUntypedPtrAccessChainKHR %ptr_block %block %gep %int_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Element must be 0 if the interpretation type is a " + "Block- or BufferBlock-decorated structure")); +} + +TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VariablePointers +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%block = OpTypeStruct %int +%array1 = OpTypeArray %block %int_4 +%array2 = OpTypeArray %block %int_4 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %array1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpUntypedAccessChainKHR %ptr %array2 %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If Base or Base Type is a Block or BufferBlock array, " + "the other must also be the same array")); +} + +TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VariablePointers +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%block = OpTypeStruct %int +%array1 = OpTypeArray %block %int_4 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%var = OpUntypedVariableKHR %ptr StorageBuffer %array1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpUntypedAccessChainKHR %ptr %block %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Both Base Type and Base must be Block or BufferBlock " + "arrays or neither can be")); +} + +TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VariablePointers +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%block = OpTypeStruct %int +%array1 = OpTypeArray %block %int_4 +%array2 = OpTypeArray %block %int_4 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%ptr_block_array = OpTypePointer StorageBuffer %array1 +%var = OpVariable %ptr_block_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%copy1 = OpCopyObject %ptr_block_array %var +%copy2 = OpCopyObject %ptr_block_array %copy1 +%gep = OpUntypedAccessChainKHR %ptr %array2 %copy2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If Base or Base Type is a Block or BufferBlock array, " + "the other must also be the same array")); +} + +TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch4) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VariablePointers +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%block = OpTypeStruct %int +%array1 = OpTypeArray %block %int_4 +%ptr = OpTypeUntypedPointerKHR StorageBuffer +%ptr_block_array = OpTypePointer StorageBuffer %array1 +%var = OpVariable %ptr_block_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%copy = OpCopyObject %ptr_block_array %var +%gep = OpUntypedAccessChainKHR %ptr %block %copy +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Both Base Type and Base must be Block or BufferBlock " + "arrays or neither can be")); +} + +TEST_F(ValidateMemory, VariableFunctionPointer) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Function %int +%ptr_ptr = OpTypePointer Function %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_ptr Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, variables can only allocate a pointer " + "to the StorageBuffer or Workgroup storage classes")); +} + +TEST_F(ValidateMemory, VariableFunctionPointerUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypeUntypedPointerKHR Function +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpUntypedVariableKHR %ptr Function %ptr +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, variables can only allocate a pointer " + "to the StorageBuffer or Workgroup storage classes")); +} + +TEST_F(ValidateMemory, VariableStorageBufferPointer) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer StorageBuffer %int +%ptr_ptr = OpTypePointer Function %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_ptr Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing, variables can only allocate a storage " + "buffer pointer if the VariablePointersStorageBuffer capability " + "is declared")); +} + +TEST_F(ValidateMemory, VariableStorageBufferPointerUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr_s = OpTypeUntypedPointerKHR StorageBuffer +%ptr_f = OpTypeUntypedPointerKHR Function +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpUntypedVariableKHR %ptr_f Function %ptr_s +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing, variables can only allocate a storage " + "buffer pointer if the VariablePointersStorageBuffer capability " + "is declared")); +} + +TEST_F(ValidateMemory, VariableWorkgroupPointer) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +%ptr_ptr = OpTypePointer Function %ptr +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_ptr Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing, variables can only allocate a workgroup " + "pointer if the VariablePointers capability is declared")); +} + +TEST_F(ValidateMemory, VariableWorkgroupPointerUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr_w = OpTypeUntypedPointerKHR Workgroup +%ptr_f = OpTypeUntypedPointerKHR Function +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpUntypedVariableKHR %ptr_f Function %ptr_w +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing, variables can only allocate a workgroup " + "pointer if the VariablePointers capability is declared")); +} + +TEST_F(ValidateMemory, VariablePointerBadStorageClass) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr = OpTypePointer Workgroup %int +%ptr_ptr = OpTypePointer Workgroup %ptr +%var = OpVariable %ptr_ptr Workgroup +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing with variable pointers, variables that " + "allocate pointers must be in Function or Private storage classes")); +} + +TEST_F(ValidateMemory, VariablePointerInStruct) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr_s = OpTypePointer Workgroup %int +%struct_t = OpTypeStruct %ptr_s +%ptr_struct = OpTypePointer Function %struct_t +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_struct Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VariablePointerInArray) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%ptr_s = OpTypePointer Workgroup %int +%array_t = OpTypeArray %ptr_s %int_4 +%ptr_array = OpTypePointer Function %array_t +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_array Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, NoVariablePointerInStruct) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr_f = OpTypePointer Function %int +%struct_t = OpTypeStruct %ptr_f +%ptr_struct = OpTypePointer Function %struct_t +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_struct Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Logical addressing, variables can only allocate a pointer " + "to the StorageBuffer or Workgroup storage classes")); +} + +TEST_F(ValidateMemory, VariablePointerBadStorageClassUntyped) { + const std::string spirv = R"( +OpCapability Shader +OpCapability UntypedPointersKHR +OpCapability VariablePointers +OpExtension "SPV_KHR_untyped_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%ptr_w = OpTypeUntypedPointerKHR Workgroup +%var = OpUntypedVariableKHR %ptr_w Workgroup %ptr_w +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing with variable pointers, variables that " + "allocate pointers must be in Function or Private storage classes")); +} + +TEST_F(ValidateMemory, AccessChainNegativeSignedIndex) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_n1 = OpConstant %int -1 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%ptr_array = OpTypePointer Workgroup %array +%ptr_int = OpTypePointer Workgroup %int +%var = OpVariable %ptr_array Workgroup +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_n1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index at word 4 may not have a negative value")); +} + std::string GenCoopMat2Shader(const std::string& extra_types, const std::string& main_body, const std::string& after_main = "", @@ -7273,8 +9168,8 @@ R"( )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorLayoutInvalidDimFail) { @@ -7287,8 +9182,8 @@ R"( )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("must be between 1 and 5")); } @@ -7302,8 +9197,8 @@ R"( )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a valid TensorClampMode")); } @@ -7320,8 +9215,8 @@ R"( )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("must be between 1 and 5")); } @@ -7337,8 +9232,8 @@ R"( )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Permutation values don't form a valid permutation")); } @@ -7355,8 +9250,8 @@ R"( )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Incorrect number of permutation values.")); } @@ -7374,8 +9269,8 @@ %tl2 = OpTensorLayoutSetBlockSizeNV %layout %tl %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorLayoutBlockSizeFail) { @@ -7391,8 +9286,8 @@ %tl2 = OpTensorLayoutSetBlockSizeNV %layout %tl %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("unexpected number of operands")); } @@ -7410,8 +9305,8 @@ %tl2 = OpTensorLayoutSetDimensionNV %layout %tl %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorLayoutDimensionFail) { @@ -7427,8 +9322,8 @@ %tl2 = OpTensorLayoutSetDimensionNV %layout %tl %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("unexpected number of operands")); } @@ -7446,8 +9341,8 @@ %tl2 = OpTensorLayoutSetStrideNV %layout %tl %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorLayoutStrideFail) { @@ -7463,8 +9358,8 @@ %tl2 = OpTensorLayoutSetStrideNV %layout %tl %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("unexpected number of operands")); } @@ -7482,8 +9377,8 @@ %tl2 = OpTensorLayoutSliceNV %layout %tl %b %b %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorLayoutSliceFail) { @@ -7499,8 +9394,8 @@ %tl2 = OpTensorLayoutSliceNV %layout %tl %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("unexpected number of operands")); } @@ -7518,8 +9413,8 @@ %tl2 = OpTensorLayoutSetClampValueNV %layout %tl %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorViewDimensionPass) { @@ -7538,8 +9433,8 @@ %tv2 = OpTensorViewSetDimensionNV %view %tv %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorViewDimensionFail) { @@ -7558,8 +9453,8 @@ %tv2 = OpTensorViewSetDimensionNV %view %tv %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("unexpected number of operands")); } @@ -7580,8 +9475,8 @@ %tv2 = OpTensorViewSetStrideNV %view %tv %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2TensorViewStrideFail) { @@ -7600,8 +9495,8 @@ %tv2 = OpTensorViewSetStrideNV %view %tv %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("unexpected number of operands")); } @@ -7622,8 +9517,8 @@ %tv2 = OpTensorViewSetClipNV %view %tv %b %b %b %b )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2LoadStoreTensorPass) { @@ -7653,8 +9548,8 @@ OpCooperativeMatrixStoreTensorNV %array_ptr %mat %tl Aligned 4 TensorView %tv )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateMemory, CoopMat2LoadTensorWrongLayoutTypeFail) { @@ -7675,8 +9570,8 @@ %mat2 = OpCooperativeMatrixLoadTensorNV %f16mat %array_ptr %mat %tv None None )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("does not have a tensor layout type")); } @@ -7699,8 +9594,8 @@ %mat2 = OpCooperativeMatrixLoadTensorNV %f16mat %array_ptr %mat %tl None None )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("type does not match Result Type")); } @@ -7723,8 +9618,8 @@ %mat2 = OpCooperativeMatrixLoadTensorNV %f32mat %array_ptr %mat %tl None DecodeFunc %decodefunc )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("return type must match matrix component type")); } @@ -7762,8 +9657,8 @@ OpDecorate %psb2 Restrict )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("dimension equal to the tensor dimension")); } @@ -7797,13 +9692,777 @@ OpFunctionEnd )"); - CompileSuccessfully(spirv.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("first parameter must be pointer to PhysicalStorageBuffer")); } +TEST_F(ValidateMemory, PtrAccessChainNodePayloadArray) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ShaderEnqueueAMDX +OpExtension "SPV_AMDX_shader_enqueue" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%node0 = OpConstantStringAMDX "node0" +%node1 = OpConstantStringAMDX "node1" +%node2 = OpConstantStringAMDX "node2" +%S = OpTypeStruct %uint +%_payloadarr_S = OpTypeNodePayloadArrayAMDX %S +%_ptr_NodePayloadAMDX__payloadarr_S = OpTypePointer NodePayloadAMDX %_payloadarr_S +%_ptr_NodePayloadAMDX_uint = OpTypePointer NodePayloadAMDX %uint +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%input = OpVariable %_ptr_NodePayloadAMDX__payloadarr_S NodePayloadAMDX +%main = OpFunction %void None %void_fn +%entry = OpLabel +%x = OpAccessChain %_ptr_NodePayloadAMDX_uint %input %uint_0 %uint_0 +OpReturn +OpFunctionEnd +)"; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +std::string GenCoopVecLoadStoreShader(const std::string& storeMemoryAccess, + const std::string& loadMemoryAccess) { + std::string s = R"( +OpCapability Shader +OpCapability Float16 +OpCapability StorageBuffer16BitAccess +OpCapability VulkanMemoryModel +OpCapability CooperativeVectorNV +OpCapability ReplicatedCompositesEXT +OpExtension "SPV_EXT_replicated_composites" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_vector" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint GLCompute %4 "main" %48 %73 +OpExecutionMode %4 LocalSize 1 1 1 + +OpDecorate %45 ArrayStride 2 +OpDecorate %46 Block +OpMemberDecorate %46 0 Offset 0 +OpDecorate %48 Binding 0 +OpDecorate %48 DescriptorSet 0 + +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%49 = OpTypeInt 32 1 +%41 = OpTypeFloat 16 + +%14 = OpConstant %6 1 +%50 = OpConstant %49 0 +%82 = OpConstant %6 5 + +%42 = OpTypeCooperativeVectorNV %41 %14 +%43 = OpTypePointer Function %42 + +%45 = OpTypeRuntimeArray %41 +%46 = OpTypeStruct %45 +%47 = OpTypePointer StorageBuffer %46 +%48 = OpVariable %47 StorageBuffer +%51 = OpTypePointer StorageBuffer %45 + +%57 = OpTypePointer Private %42 +%73 = OpVariable %57 Private + +%4 = OpFunction %2 None %3 +%5 = OpLabel +%52 = OpAccessChain %51 %48 %50 +%56 = OpCooperativeVectorLoadNV %42 %52 %50 )" + + loadMemoryAccess + R"( %82 +%77 = OpLoad %42 %73 +OpCooperativeVectorStoreNV %52 %50 %77 )" + + storeMemoryAccess + R"( %82 +OpReturn +OpFunctionEnd +)"; + + return s; +} + +TEST_F(ValidateMemory, CoopVecLoadStoreSuccess) { + std::string spirv = + GenCoopVecLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); +} + +TEST_F(ValidateMemory, CoopVecStoreMemoryAccessFail) { + std::string spirv = + GenCoopVecLoadStoreShader("MakePointerVisibleKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MakePointerVisibleKHR cannot be used with OpStore")); +} + +TEST_F(ValidateMemory, CoopVecLoadMemoryAccessFail) { + std::string spirv = + GenCoopVecLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerAvailableKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad")); +} + +TEST_F(ValidateMemory, CoopVecInvalidStorageClassFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeVectorNV +OpCapability ReplicatedCompositesEXT +OpExtension "SPV_NV_cooperative_vector" +OpExtension "SPV_EXT_replicated_composites" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 + +%u32_8 = OpConstant %u32 8 +%use_A = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8 + +%str = OpTypeStruct %f16vec +%str_ptr = OpTypePointer Workgroup %str +%sh = OpVariable %str_ptr Workgroup + +%main = OpFunction %void None %func +%main_entry = OpLabel + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Cooperative vector types (or types containing them) can only be " + "allocated in Function or Private storage classes or as function " + "parameters")); +} + +std::string GenCoopVecShader(const std::string& extra_types, + const std::string& main_body, + const std::string& execution_modes = "") { + const std::string prefix = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int64 +OpCapability Int16 +OpCapability StorageBuffer16BitAccess +OpCapability VulkanMemoryModel +OpCapability CooperativeVectorNV +OpCapability CooperativeVectorTrainingNV +OpCapability ReplicatedCompositesEXT +OpCapability Shader64BitIndexingEXT +OpExtension "SPV_EXT_replicated_composites" +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_vector" +OpExtension "SPV_EXT_shader_64bit_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint GLCompute %main "main" %48 %73 +OpExecutionMode %main LocalSize 1 1 1 + +)" + execution_modes + + R"( + +OpDecorate %f16_arr ArrayStride 2 +OpDecorate %46 Block +OpMemberDecorate %46 0 Offset 0 +OpDecorate %48 Binding 0 +OpDecorate %48 DescriptorSet 0 + +%void = OpTypeVoid +%func = OpTypeFunction %void +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f16 = OpTypeFloat 16 +%bool = OpTypeBool + +%false = OpConstantFalse %bool +%u32_4 = OpConstant %u32 4 +%u32_8 = OpConstant %u32 8 +%s32_0 = OpConstant %s32 0 +%f16_0 = OpConstant %f16 0 + +%f16vec4 = OpTypeCooperativeVectorNV %f16 %u32_4 +%f16vec8 = OpTypeCooperativeVectorNV %f16 %u32_8 + +%f16_arr = OpTypeRuntimeArray %f16 +%46 = OpTypeStruct %f16_arr +%47 = OpTypePointer StorageBuffer %46 +%48 = OpVariable %47 StorageBuffer +%51 = OpTypePointer StorageBuffer %f16_arr + +%57 = OpTypePointer Private %f16vec4 +%73 = OpVariable %57 Private +%u32ptr = OpTypePointer Function %u32 + +%input4 = OpConstantCompositeReplicateEXT %f16vec4 %f16_0 +%input8 = OpConstantCompositeReplicateEXT %f16vec8 %f16_0 +%interp = OpConstant %u32 0 +%offset = OpConstant %u32 0 + +)"; + + const std::string func_begin = + R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +%u32var = OpVariable %u32ptr Function +%array_ptr = OpAccessChain %51 %48 %s32_0 +)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + extra_types + func_begin + main_body + suffix; +} + +TEST_F(ValidateMemory, CoopVecMatMulSuccess) { + std::string spirv = GenCoopVecShader("", + R"( +%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false +%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input8 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_8 %s32_0 %false +%result2 = OpCooperativeVectorMatrixMulAddNV %f16vec8 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_8 %u32_4 %s32_0 %false +%result3 = OpCooperativeVectorMatrixMulNV %f16vec4 %input4 %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false +%result4 = OpCooperativeVectorMatrixMulNV %f16vec4 %input8 %interp %array_ptr %offset %interp %u32_4 %u32_8 %s32_0 %false +%result5 = OpCooperativeVectorMatrixMulNV %f16vec8 %input4 %interp %array_ptr %offset %interp %u32_8 %u32_4 %s32_0 %false + +OpCooperativeVectorReduceSumAccumulateNV %array_ptr %offset %input4 +OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %input4 %input8 %interp %interp + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); +} + +TEST_F(ValidateMemory, CoopVecMatMulKMismatchFail) { + std::string spirv = GenCoopVecShader(R"()", + R"( +%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input8 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV input number of " + "components 8 does not match K 4")); +} + +TEST_F(ValidateMemory, CoopVecMatMulPackedKMismatchPass) { + std::string spirv = GenCoopVecShader( + R"( +%packed = OpConstant %u32 1000491001 + )", + R"( +%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input8 %packed %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); +} + +TEST_F(ValidateMemory, CoopVecMatMulMMismatchFail) { + std::string spirv = GenCoopVecShader(R"()", + R"( +%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec8 %input8 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_8 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV result type number " + "of components 8 does not match M 4")); +} + +TEST_F(ValidateMemory, CoopVecMatMulTransposeTypeFail) { + std::string spirv = GenCoopVecShader(R"()", + R"( +%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %s32_0 + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV Transpose <id> " + "'16[%int_0]' is not a scalar boolean")); +} + +TEST_F(ValidateMemory, CoopVecMatMulInputInterpretationNotConstantFail) { + std::string spirv = GenCoopVecShader( + R"( + )", + R"( +%u32val = OpLoad %u32 %u32var +%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %u32val %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV InputInterpretation " + "<id> '31[%31]' is not a constant instruction")); +} + +TEST_F(ValidateMemory, CoopVecMatMulMatrixInterpretationNotConstantFail) { + std::string spirv = GenCoopVecShader( + R"( + )", + R"( +%u32val = OpLoad %u32 %u32var +%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %u32val %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV MatrixInterpretation <id> " + "'31[%31]' is not a constant instruction")); +} + +TEST_F(ValidateMemory, CoopVecMatMulBiasInterpretationNotConstantFail) { + std::string spirv = GenCoopVecShader( + R"( + )", + R"( +%u32val = OpLoad %u32 %u32var +%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %u32val %u32_4 %u32_4 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV BiasInterpretation " + "<id> '31[%31]' is not a constant instruction")); +} + +TEST_F(ValidateMemory, CoopVecMatMulInputInterpretationNotInt32Fail) { + std::string spirv = GenCoopVecShader( + R"( + )", + R"( +%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %false %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorMatrixMulAddNV InputInterpretation " + "type <id> '12[%bool]' is not a 32 bit integer")); +} + +TEST_F(ValidateMemory, CoopVecOuterProductABMismatchFail) { + std::string spirv = GenCoopVecShader( + R"( +%f32 = OpTypeFloat 32 +%f32vec8 = OpTypeCooperativeVectorNV %f32 %u32_8 +%f32_0 = OpConstant %f32 0 +%input8f32 = OpConstantCompositeReplicateEXT %f32vec8 %f32_0 + )", + R"( +OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %input4 %input8f32 %interp %interp + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpCooperativeVectorOuterProductAccumulateNV A and B component " + "types '11[%half]' and '28[%float]' do not match")); +} + +TEST_F(ValidateMemory, CoopVecOuterProductIntOffsetFail) { + std::string spirv = GenCoopVecShader( + R"( +%u16 = OpTypeInt 16 0 +%u16_0 = OpConstant %u16 0 + )", + R"( +OpCooperativeVectorOuterProductAccumulateNV %array_ptr %u16_0 %input4 %input8 %interp %interp + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpCooperativeVectorOuterProductAccumulateNV Offset " + "type <id> '28[%ushort]' is not a 32 or 64 bit integer")); +} + +TEST_F(ValidateMemory, CoopVecOuterProductInt32MatrixStrideFail) { + std::string spirv = GenCoopVecShader( + R"( +%u64 = OpTypeInt 64 0 +%u64_0 = OpConstant %u64 0 + )", + R"( +OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %input4 %input8 %interp %interp %u64_0 + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpCooperativeVectorOuterProductAccumulateNV MatrixStride type " + "<id> '28[%ulong]' is not a 32 bit integer")); +} + +TEST_F(ValidateMemory, CoopVecOuterProductVectorTypeFail) { + std::string spirv = GenCoopVecShader( + R"( +%f16v4 = OpTypeVector %f16 4 +%f16c = OpConstantCompositeReplicateEXT %f16v4 %f16_0 + )", + R"( +OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %f16c %input8 %interp %interp + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorOuterProductAccumulateNV A type " + "<id> '28[%v4half]' is not a cooperative vector type")); +} + +TEST_F(ValidateMemory, CoopVecReduceSumIntOffsetFail) { + std::string spirv = GenCoopVecShader( + R"( +%u16 = OpTypeInt 16 0 +%u16_0 = OpConstant %u16 0 + )", + R"( +OpCooperativeVectorReduceSumAccumulateNV %array_ptr %u16_0 %input4 + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorReduceSumAccumulateNV Offset type " + "<id> '28[%ushort]' is not a 32 or 64 bit integer")); +} + +TEST_F(ValidateMemory, CoopVecOuterProductInt64OffsetPass) { + std::string spirv = GenCoopVecShader( + R"( +%u64 = OpTypeInt 64 0 +%u64_0 = OpConstant %u64 0 + )", + R"( +OpCooperativeVectorOuterProductAccumulateNV %array_ptr %u64_0 %input4 %input8 %interp %interp + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); +} + +TEST_F(ValidateMemory, CoopVecReduceSumVectorTypeFail) { + std::string spirv = GenCoopVecShader( + R"( +%f16v4 = OpTypeVector %f16 4 +%f16c = OpConstantCompositeReplicateEXT %f16v4 %f16_0 + )", + R"( +OpCooperativeVectorReduceSumAccumulateNV %array_ptr %offset %f16c + )"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpCooperativeVectorReduceSumAccumulateNV V type <id> " + "'28[%v4half]' is not a cooperative vector type.")); +} + +TEST_F(ValidateMemory, CoopMatMatrixBFloatFAdd) { + const std::string body = + R"( + OpCapability Shader + OpCapability Float16 + OpCapability BFloat16TypeKHR + OpCapability BFloat16CooperativeMatrixKHR + OpCapability VulkanMemoryModel + OpCapability CooperativeMatrixKHR + OpExtension "SPV_KHR_bfloat16" + OpExtension "SPV_KHR_vulkan_memory_model" + OpExtension "SPV_KHR_cooperative_matrix" + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %main "main" %_ %__0 %__1 + OpExecutionMode %main LocalSize 32 1 1 + OpDecorate %_arr_bfloat16_uint_64 ArrayStride 2 + OpDecorate %A Block + OpMemberDecorate %A 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + OpDecorate %_arr_bfloat16_uint_64_0 ArrayStride 2 + OpDecorate %B Block + OpMemberDecorate %B 0 Offset 0 + OpDecorate %__0 Binding 1 + OpDecorate %__0 DescriptorSet 0 + OpDecorate %_arr_bfloat16_uint_64_1 ArrayStride 2 + OpDecorate %R Block + OpMemberDecorate %R 0 Offset 0 + OpDecorate %__1 Binding 2 + OpDecorate %__1 DescriptorSet 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bfloat16 = OpTypeFloat 16 BFloat16KHR + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_8 = OpConstant %uint 8 + %uint_0 = OpConstant %uint 0 + %12 = OpTypeCooperativeMatrixKHR %bfloat16 %uint_3 %uint_8 %uint_8 %uint_0 +%_ptr_Function_12 = OpTypePointer Function %12 + %uint_64 = OpConstant %uint 64 +%_arr_bfloat16_uint_64 = OpTypeArray %bfloat16 %uint_64 + %A = OpTypeStruct %_arr_bfloat16_uint_64 +%_ptr_StorageBuffer_A = OpTypePointer StorageBuffer %A + %_ = OpVariable %_ptr_StorageBuffer_A StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_bfloat16 = OpTypePointer StorageBuffer %bfloat16 +%_arr_bfloat16_uint_64_0 = OpTypeArray %bfloat16 %uint_64 + %B = OpTypeStruct %_arr_bfloat16_uint_64_0 +%_ptr_StorageBuffer_B = OpTypePointer StorageBuffer %B + %__0 = OpVariable %_ptr_StorageBuffer_B StorageBuffer + %v3uint = OpTypeVector %uint 3 + %uint_32 = OpConstant %uint 32 + %uint_1 = OpConstant %uint 1 + %35 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%_arr_bfloat16_uint_64_1 = OpTypeArray %bfloat16 %uint_64 + %R = OpTypeStruct %_arr_bfloat16_uint_64_1 +%_ptr_StorageBuffer_R = OpTypePointer StorageBuffer %R + %__1 = OpVariable %_ptr_StorageBuffer_R StorageBuffer + %main = OpFunction %void None %4 + %6 = OpLabel + %matX = OpVariable %_ptr_Function_12 Function + %matY = OpVariable %_ptr_Function_12 Function + %23 = OpAccessChain %_ptr_StorageBuffer_bfloat16 %_ %int_0 %uint_0 + %24 = OpCooperativeMatrixLoadKHR %12 %23 %int_0 %uint_8 None + OpStore %matX %24 + %30 = OpAccessChain %_ptr_StorageBuffer_bfloat16 %__0 %int_0 %uint_0 + %31 = OpCooperativeMatrixLoadKHR %12 %30 %int_0 %uint_8 None + OpStore %matY %31 + %32 = OpLoad %12 %matX + %33 = OpLoad %12 %matY + %34 = OpFAdd %12 %32 %33 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FAdd doesn't support BFloat16 type")); +} + +TEST_F(ValidateMemory, CoopMatMatrixFloat8FAdd) { + const std::string body = + R"( + OpCapability Shader + OpCapability Float8EXT + OpCapability Float8CooperativeMatrixEXT + OpCapability VulkanMemoryModel + OpCapability CooperativeMatrixKHR + OpExtension "SPV_EXT_float8" + OpExtension "SPV_KHR_cooperative_matrix" + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 32 1 1 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %4 = OpTypeFunction %void + %fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_16 = OpConstant %uint 16 + %uint_0 = OpConstant %uint 0 + %12 = OpTypeCooperativeMatrixKHR %fp8e4m3 %uint_3 %uint_16 %uint_16 %uint_0 +%_ptr_Function_12 = OpTypePointer Function %12 + %v3uint = OpTypeVector %uint 3 + %uint_32 = OpConstant %uint 32 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %4 + %6 = OpLabel + %matR = OpVariable %_ptr_Function_12 Function + %matX = OpVariable %_ptr_Function_12 Function + %matY = OpVariable %_ptr_Function_12 Function + %16 = OpLoad %12 %matX + %18 = OpLoad %12 %matY + %19 = OpFAdd %12 %16 %18 + OpStore %matR %19 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FAdd doesn't support FP8 E4M3/E5M2 types")); +} + +TEST_F(ValidateMemory, PhysicalStorageBufferArray) { + const std::string body = + R"( + OpCapability Shader + OpCapability Int64 + OpCapability PhysicalStorageBufferAddresses + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_physical_storage_buffer" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %18 "main" + OpExecutionMode %18 LocalSize 1 1 1 + OpSource OpenCL_C 120 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 256 + OpDecorate %array ArrayStride 16 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %ulong = OpTypeInt 64 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %17 = OpTypeFunction %void + %ulong_0 = OpConstant %ulong 0 + %uint_3 = OpConstant %uint 3 + %array = OpTypeArray %v4float %uint_3 + %S = OpTypeStruct %array %uint + %ptr_S = OpTypePointer PhysicalStorageBuffer %S + %float_0 = OpConstant %float 0 + %v4float_0 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %23 = OpConstantComposite %array %v4float_0 %v4float_0 %v4float_0 + %24 = OpConstantComposite %S %23 %uint_3 + %18 = OpFunction %void None %17 + %19 = OpLabel + %58 = OpConvertUToPtr %ptr_S %ulong_0 + OpStore %58 %24 Aligned 4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateMemory, LongVectorPrivateStorageClassGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability LongVectorEXT +OpExtension "SPV_EXT_long_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "func" +%float = OpTypeFloat 32 +%vec5 = OpTypeVector %float 5 +%vec5ptr = OpTypePointer Private %vec5 +%1 = OpVariable %vec5ptr Private +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, LongVectorInputStorageClassBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability LongVectorEXT +OpExtension "SPV_EXT_long_vector" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "func" +%float = OpTypeFloat 32 +%vec5 = OpTypeVector %float 5 +%vec5ptr = OpTypePointer Input %vec5 +%1 = OpVariable %vec5ptr Input +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Long vector types with more than 4 components (or types " + "containing them) not supported in storage class Input")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Type-12297")); +} + +TEST_F(ValidateMemory, LongVectorMissingCapabilityBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "func" +%float = OpTypeFloat 32 +%vec5 = OpTypeVector %float 5 +%vec5ptr = OpTypePointer Private %vec5 +%1 = OpVariable %vec5ptr Private +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Illegal number of components (5) for TypeVector")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-12295")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp index a7b96a4..95eb5e8 100644 --- a/test/val/val_mesh_shading_test.cpp +++ b/test/val/val_mesh_shading_test.cpp
@@ -128,6 +128,104 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); } +// https://godbolt.org/z/Kvb1rsceP +TEST_F(ValidateMeshShading, BasicMeshBuiltinSuccess) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshVerticesEXT %vertexOutput %gl_MeshPrimitivesEXT %gl_LocalInvocationIndex %gl_PrimitiveTriangleIndicesEXT + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %gl_MeshPerVertexEXT Block + OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position + OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize + OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance + OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance + OpDecorate %VertexOutput Block + OpDecorate %vertexOutput Location 0 + OpDecorate %gl_MeshPerPrimitiveEXT Block + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveId + OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 BuiltIn Layer + OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 BuiltIn ViewportIndex + OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 PerPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 BuiltIn CullPrimitiveEXT + OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 PerPrimitiveEXT + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3 +%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3 +%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 +%VertexOutput = OpTypeStruct %v4float +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Output__arr_VertexOutput_uint_3 = OpTypePointer Output %_arr_VertexOutput_uint_3 +%vertexOutput = OpVariable %_ptr_Output__arr_VertexOutput_uint_3 Output + %bool = OpTypeBool +%gl_MeshPerPrimitiveEXT = OpTypeStruct %int %int %int %bool +%_arr_gl_MeshPerPrimitiveEXT_uint_1 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_1 +%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_1 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_1 +%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_1 Output +%_ptr_Input_uint = OpTypePointer Input %uint +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input + %int_3 = OpConstant %int 3 + %false = OpConstantFalse %bool +%_ptr_Output_bool = OpTypePointer Output %bool + %v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1 +%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1 +%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output + %uint_0 = OpConstant %uint 0 + %52 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %main = OpFunction %void None %4 + %6 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %22 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0 + OpStore %22 %20 + %24 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_1 %int_0 + OpStore %24 %20 + %26 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_2 %int_0 + OpStore %26 %20 + %31 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_0 %int_0 + OpStore %31 %20 + %32 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_1 %int_0 + OpStore %32 %20 + %33 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_2 %int_0 + OpStore %33 %20 + %41 = OpLoad %uint %gl_LocalInvocationIndex + %45 = OpAccessChain %_ptr_Output_bool %gl_MeshPrimitivesEXT %41 %int_3 + OpStore %45 %false + %50 = OpLoad %uint %gl_LocalInvocationIndex + %54 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %50 + OpStore %54 %52 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + TEST_F(ValidateMeshShading, VulkanBasicMeshAndTaskSuccess) { const std::string body = R"( OpCapability MeshShadingEXT @@ -136,6 +234,8 @@ OpMemoryModel Logical GLSL450 OpEntryPoint MeshEXT %mainMesh "mainMesh" OpEntryPoint TaskEXT %mainTask "mainTask" + OpExecutionMode %mainMesh LocalSize 1 1 1 + OpExecutionMode %mainTask LocalSize 1 1 1 OpExecutionMode %mainMesh OutputVertices 1 OpExecutionMode %mainMesh OutputPrimitivesEXT 1 OpExecutionMode %mainMesh OutputTrianglesEXT @@ -467,6 +567,80 @@ "TaskEXT and MeshKHR execution model")); } +TEST_F(ValidateMeshShading, BadMultipleTaskPayloadWorkgroupEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %payload %payload1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %payload1 = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("There can be at most one " + "OpVariable with storage " + "class TaskPayloadWorkgroupEXT associated with " + "an OpEntryPoint")); +} + +TEST_F(ValidateMeshShading, TaskPayloadWorkgroupTaskExtExecutionModel) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %payload + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, TaskPayloadWorkgroupMeshExtExecutionModel) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %payload + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + TEST_F(ValidateMeshShading, OpSetMeshOutputsBadVertexCount) { const std::string body = R"( OpCapability MeshShadingEXT @@ -598,6 +772,770 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); } +TEST_F(ValidateMeshShading, MeshOutputScalar) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %x + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %x Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%o_ptr = OpTypePointer Output %uint + %x = OpVariable %o_ptr Output + %main = OpFunction %void None %4 + %6 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In the MeshEXT Execution Mode, all Output Variables " + "must contain an Array.")); +} + +TEST_F(ValidateMeshShading, MeshOutputScalarStruct) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %vertexOutput + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %VertexOutput Block + OpMemberDecorate %VertexOutput 0 Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%VertexOutput = OpTypeStruct %v4float +%_ptr_Output_uint_3 = OpTypePointer Output %VertexOutput +%vertexOutput = OpVariable %_ptr_Output_uint_3 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %4 + %6 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + %21 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_0 + OpStore %21 %19 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In the MeshEXT Execution Mode, all Output Variables " + "must contain an Array.")); +} + +TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInMeshEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %iid "iid" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %myblock "myblock" + OpMemberName %myblock 0 "f" + OpName %blk "blk" + OpName %triangleNormal "triangleNormal" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpMemberDecorate %myblock 0 PerPrimitiveEXT + OpDecorate %myblock Block + OpDecorate %blk Location 0 + OpDecorate %triangleNormal PerPrimitiveEXT + OpDecorate %triangleNormal Location 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 + %myblock = OpTypeStruct %float + %uint_32 = OpConstant %uint 32 +%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32 +%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32 + %blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_11 = OpConstant %float 11 +%_ptr_Output_float = OpTypePointer Output %float + %v3float = OpTypeVector %float 3 +%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32 +%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32 +%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Input + %33 = OpConstantComposite %v3float %float_11 %float_11 %float_11 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %iid = OpVariable %_ptr_Function_uint Function + %14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 + %15 = OpLoad %uint %14 + OpStore %iid %15 + %22 = OpLoad %uint %iid + %27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0 + OpStore %27 %float_11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PerPrimitiveEXT decoration must be applied only to " + "variables in the Output Storage Class in the Storage " + "Class in the MeshEXT Execution Model.")); +} + +TEST_F(ValidateMeshShading, VulkanPerPrimitiveEXTStorageClassInMeshEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %iid "iid" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %myblock "myblock" + OpMemberName %myblock 0 "f" + OpName %blk "blk" + OpName %triangleNormal "triangleNormal" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpMemberDecorate %myblock 0 PerPrimitiveEXT + OpDecorate %myblock Block + OpDecorate %blk Location 0 + OpDecorate %triangleNormal PerPrimitiveEXT + OpDecorate %triangleNormal Location 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint +%float = OpTypeFloat 32 +%myblock = OpTypeStruct %float +%uint_32 = OpConstant %uint 32 +%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32 +%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32 +%blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float_11 = OpConstant %float 11 +%_ptr_Output_float = OpTypePointer Output %float +%v3float = OpTypeVector %float 3 +%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32 +%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32 +%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Input +%33 = OpConstantComposite %v3float %float_11 %float_11 %float_11 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 + %5 = OpLabel +%iid = OpVariable %_ptr_Function_uint Function +%14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 +%15 = OpLoad %uint %14 + OpStore %iid %15 +%22 = OpLoad %uint %iid +%27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0 + OpStore %27 %float_11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveId-PrimitiveId-04336")); +} + +TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInFrag) { + const std::string body = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %triangleNormal + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %triangleNormal "triangleNormal" + OpDecorate %triangleNormal PerPrimitiveNV + OpDecorate %triangleNormal Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3 +%triangleNormal = OpVariable %_ptr_Input__arr_v3float_uint_3 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Input_v3float %triangleNormal %int_0 + %19 = OpLoad %v3float %18 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PerPrimitiveEXT decoration must be applied only to " + "variables in the Input Storage Class in the Fragment " + "Execution Model.")); +} + +TEST_F(ValidateMeshShading, PerPrimitiveEXTStorageClassInFrag) { + const std::string body = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %res3 %triangleNormal + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %res3 "res3" + OpName %triangleNormal "triangleNormal" + OpDecorate %res3 Location 0 + OpDecorate %triangleNormal PerPrimitiveNV + OpDecorate %triangleNormal Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %_ptr_Output_v3float = OpTypePointer Output %v3float + %res3 = OpVariable %_ptr_Output_v3float Output + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 + %_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 + %triangleNormal = OpVariable %_ptr_Input__arr_v3float_uint_3 Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %_ptr_Input_v3float = OpTypePointer Input %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Input_v3float %triangleNormal %int_0 + %19 = OpLoad %v3float %18 + OpStore %res3 %19 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, PerPrimitiveEXTStorageClassInMeshEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %iid "iid" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %myblock "myblock" + OpMemberName %myblock 0 "f" + OpName %blk "blk" + OpName %triangleNormal "triangleNormal" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpMemberDecorate %myblock 0 PerPrimitiveNV + OpDecorate %myblock Block + OpDecorate %blk Location 0 + OpDecorate %triangleNormal PerPrimitiveNV + OpDecorate %triangleNormal Location 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 + %myblock = OpTypeStruct %float + %uint_32 = OpConstant %uint 32 +%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32 +%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32 + %blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_11 = OpConstant %float 11 +%_ptr_Output_float = OpTypePointer Output %float + %v3float = OpTypeVector %float 3 +%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32 +%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Output %_arr_v3float_uint_32 +%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Output + %33 = OpConstantComposite %v3float %float_11 %float_11 %float_11 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %iid = OpVariable %_ptr_Function_uint Function + %14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 + %15 = OpLoad %uint %14 + OpStore %iid %15 + %22 = OpLoad %uint %iid + %27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0 + OpStore %27 %float_11 + %32 = OpLoad %uint %iid + %35 = OpAccessChain %_ptr_Output_v3float %triangleNormal %32 + OpStore %35 %33 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, MeshWriteOutput) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %x %y + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %x Location 1 + OpDecorate %y Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3 + %x = OpVariable %_ptr_Output__arr_uint_uint_3 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 + %y = OpVariable %_ptr_Output__arr_v4float_uint_3 Output + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %4 + %6 = OpLabel + %16 = OpAccessChain %_ptr_Output_uint %x %int_0 + OpStore %16 %uint_1 + %25 = OpAccessChain %_ptr_Output_v4float %y %int_0 + OpStore %25 %23 + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateMeshShading, MeshReadOutputInt) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %x + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %x Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_3 = OpConstant %uint 3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3 + %x = OpVariable %_ptr_Output__arr_uint_uint_3 Output + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Output_uint = OpTypePointer Output %uint + %main = OpFunction %void None %4 + %6 = OpLabel + %y = OpVariable %_ptr_Function_uint Function + %18 = OpAccessChain %_ptr_Output_uint %x %int_1 + %19 = OpLoad %uint %18 + OpStore %y %19 + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-MeshEXT-07107")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Output Storage Class in a Mesh Execution Model " + "must not be read from")); +} + +TEST_F(ValidateMeshShading, MeshReadOutputVec) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %x + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %x Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 + %x = OpVariable %_ptr_Output__arr_v4float_uint_3 Output + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %4 + %6 = OpLabel + %y = OpVariable %_ptr_Function_v4float Function + %20 = OpAccessChain %_ptr_Output_v4float %x %int_1 + %21 = OpLoad %v4float %20 + OpStore %y %21 + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-MeshEXT-07107")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Output Storage Class in a Mesh Execution Model " + "must not be read from")); +} + +TEST_F(ValidateMeshShading, MeshReadOutputStruct) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %x_0 + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %x_0 Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Function_uint = OpTypePointer Function %uint + %Bar = OpTypeStruct %uint + %Foo = OpTypeStruct %Bar + %uint_3 = OpConstant %uint 3 +%_arr_Foo_uint_3 = OpTypeArray %Foo %uint_3 +%_ptr_Output__arr_Foo_uint_3 = OpTypePointer Output %_arr_Foo_uint_3 + %x_0 = OpVariable %_ptr_Output__arr_Foo_uint_3 Output + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %int_0 = OpConstant %int 0 +%_ptr_Output_uint = OpTypePointer Output %uint + %main = OpFunction %void None %4 + %6 = OpLabel + %x = OpVariable %_ptr_Function_uint Function + %21 = OpAccessChain %_ptr_Output_uint %x_0 %int_2 %int_0 %int_0 + %22 = OpLoad %uint %21 + OpStore %x %22 + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-MeshEXT-07107")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Output Storage Class in a Mesh Execution Model " + "must not be read from")); +} + +TEST_F(ValidateMeshShading, MeshReadOutputIntUntyped) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpCapability UntypedPointersKHR + OpExtension "SPV_EXT_mesh_shader" + OpExtension "SPV_KHR_untyped_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %x + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %x Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_3 = OpConstant %uint 3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3 + %x = OpVariable %_ptr_Output__arr_uint_uint_3 Output + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Output_uint = OpTypeUntypedPointerKHR Output + %main = OpFunction %void None %4 + %6 = OpLabel + %y = OpVariable %_ptr_Function_uint Function + %18 = OpUntypedAccessChainKHR %_ptr_Output_uint %_arr_uint_uint_3 %x %int_1 + %19 = OpLoad %uint %18 + OpStore %y %19 + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Vulkan, untyped pointers can only be used in an " + "explicitly laid out storage class")); +} + +// https://godbolt.org/z/8s5W19xoc +TEST_F(ValidateMeshShading, SetMeshOutputsConstantInCondition) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %_ %gl_MeshVerticesEXT %gl_PrimitiveTriangleIndicesEXT + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %SSBO Block + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpMemberDecorate %SSBO 2 Offset 8 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + OpDecorate %gl_MeshPerVertexEXT Block + OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position + OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize + OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance + OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance + OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %SSBO = OpTypeStruct %uint %uint %uint +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %bool = OpTypeBool + %uint_500 = OpConstant %uint 500 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 + %uint_3 = OpConstant %uint 3 +%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3 +%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3 +%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output + %float_0 = OpConstant %float 0 + %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1 +%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1 +%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %47 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %main = OpFunction %void None %4 + %6 = OpLabel + %15 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %16 = OpLoad %uint %15 + %18 = OpIEqual %bool %16 %uint_1 + OpSelectionMerge %20 None + OpBranchConditional %18 %19 %22 + %19 = OpLabel + OpSetMeshOutputsEXT %uint_500 %uint_500 + OpBranch %20 + %22 = OpLabel + %24 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %25 = OpLoad %uint %24 + %27 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_2 + %28 = OpLoad %uint %27 + OpSetMeshOutputsEXT %25 %28 + OpBranch %20 + %20 = OpLabel + %40 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0 + OpStore %40 %38 + %49 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0 + OpStore %49 %47 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpSetMeshOutputsEXT Vertex Count (500) is larger than the " + "OutputVertices in OpExecutionMode (3).\nOpSetMeshOutputsEXT " + "Primitive Count (500) is larger than the OutputPrimitivesEXT " + "in OpExecutionMode (1)")); +} + +// https://godbolt.org/z/TzaKxYGrY (allowed until spec constants are frozen) +TEST_F(ValidateMeshShading, SetMeshOutputsSpecConstants) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_MeshVerticesEXT %gl_PrimitiveTriangleIndicesEXT + OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1 + OpExecutionMode %main OutputVertices 3 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + OpDecorate %V SpecId 0 + OpDecorate %P SpecId 1 + OpDecorate %gl_MeshPerVertexEXT Block + OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position + OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize + OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance + OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance + OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %V = OpSpecConstant %uint 500 + %P = OpSpecConstant %uint 500 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 + %uint_3 = OpConstant %uint 3 +%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3 +%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3 +%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %22 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %v3uint = OpTypeVector %uint 3 +%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1 +%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1 +%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %31 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint + %main = OpFunction %void None %4 + %6 = OpLabel + OpSetMeshOutputsEXT %V %P + %24 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0 + OpStore %24 %22 + %33 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0 + OpStore %33 %31 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp index 2188e03..f54b20c 100644 --- a/test/val/val_misc_test.cpp +++ b/test/val/val_misc_test.cpp
@@ -378,9 +378,10 @@ OpCapability Float16 OpCapability Int16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" OpExecutionModeId %main LocalSizeId %u32_16 %u32_16 %u32_16 %void = OpTypeVoid @@ -411,9 +412,10 @@ OpCapability Float16 OpCapability Int16 OpCapability CooperativeMatrixKHR +OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_cooperative_matrix" OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical GLSL450 +OpMemoryModel Logical VulkanKHR OpEntryPoint GLCompute %main "main" OpExecutionModeId %main LocalSizeId %u32_16 %u32_8 %u32_16 %void = OpTypeVoid @@ -442,6 +444,61 @@ "before LocalSizeId constant value")); } +TEST_F(ValidateMisc, CoopMatDeviceScope) { + const std::string body = R"( + OpCapability Shader + OpCapability Float16 + OpCapability VulkanMemoryModel + OpCapability CooperativeMatrixKHR + OpExtension "SPV_KHR_cooperative_matrix" + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 32 1 1 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpDecorate %InputA Block + OpMemberDecorate %InputA 0 Offset 0 + OpDecorate %_ Binding 0 + OpDecorate %_ DescriptorSet 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %4 = OpTypeFunction %void + %half = OpTypeFloat 16 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_16 = OpConstant %uint 16 + %uint_0 = OpConstant %uint 0 + ;; Using Device Scope + %12 = OpTypeCooperativeMatrixKHR %half %uint_1 %uint_16 %uint_16 %uint_0 +%_ptr_Function_12 = OpTypePointer Function %12 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %InputA = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer_InputA = OpTypePointer StorageBuffer %InputA + %_ = OpVariable %_ptr_StorageBuffer_InputA StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_5 = OpConstant %uint 5 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %v3uint = OpTypeVector %uint 3 + %uint_32 = OpConstant %uint 32 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %4 + %6 = OpLabel + %A = OpVariable %_ptr_Function_12 Function + %23 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 %uint_0 + %24 = OpCooperativeMatrixLoadKHR %12 %23 %int_0 %uint_16 MakePointerVisible|NonPrivatePointer %uint_5 + OpStore %A %24 + OpReturn + OpFunctionEnd)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Scope-12243")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeCooperativeMatrixKHR Scope is limited to " + "Workgroup and Subgroup")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 83a0503..8e9fca8 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -62,7 +64,7 @@ CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), - AnyVUID("VUID-StandaloneSpirv-LocalSize-06426")); + AnyVUID("VUID-StandaloneSpirv-None-10685")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -71,6 +73,53 @@ "or an object decorated with WorkgroupSize must be specified.")); } +TEST_F(ValidateMode, MeshNoModeVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main OutputVertices 81 +OpExecutionMode %main OutputPrimitivesEXT 16 +OpExecutionMode %main OutputPoints +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10685")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In the Vulkan environment, MeshEXT execution model entry " + "points require either the LocalSize or LocalSizeId execution mode " + "or an object decorated with WorkgroupSize must be specified.")); +} + +TEST_F(ValidateMode, TaskNoModeVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint TaskEXT %main "main" +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10685")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In the Vulkan environment, TaskEXT 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) { const std::string spirv = R"( OpCapability Shader @@ -88,6 +137,176 @@ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } +TEST_F(ValidateMode, GLComputeZeroWorkgroupSize) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "WorkgroupSize decorations must not have a static product of zero")); +} + +TEST_F(ValidateMode, GLComputeZeroSpecWorkgroupSize) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_0 = OpSpecConstant %int 0 +%int_1 = OpConstant %int 1 +%int3_1 = OpSpecConstantComposite %int3 %int_1 %int_0 %int_0 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, GLComputeZeroSpecCompositeWorkgroupSize) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_0 = OpSpecConstant %int 0 +%int_1 = OpSpecConstant %int 1 +%int3_1 = OpSpecConstantComposite %int3 %int_1 %int_0 %int_0 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, KernelZeroWorkgroupSizeConstant) { + const std::string spirv = R"( +OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable")); +} + +TEST_F(ValidateMode, KernelZeroWorkgroupSizeVariable) { + const std::string spirv = R"( +OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +OpDecorate %var BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%ptr = OpTypePointer Input %int3 +%var = OpVariable %ptr Input +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, GLComputeWorkgroupSizeDerivativeGroupQuads) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupQuadsKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main DerivativeGroupQuadsKHR +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int3_1 = OpConstantComposite %int3 %int_1 %int_2 %int_1 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("WorkgroupSize decorations has a static dimensions of (X = 1, " + "Y = 2) but Entry Point id 1 has an DerivativeGroupQuadsKHR " + "execution mode, so both dimensions must be a multiple of 2")); +} + +TEST_F(ValidateMode, GLComputeWorkgroupSizeDerivativeGroupLinear) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupLinearKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main DerivativeGroupLinearKHR +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%uvec3 = OpTypeVector %int 3 +%int_3 = OpConstant %int 3 +%int_2 = OpConstant %int 2 +%int3_1 = OpConstantComposite %uvec3 %int_3 %int_3 %int_2 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "WorkgroupSize decorations has a static dimensions of (X = 3, Y = 3, " + "Z = 2) but Entry Point id 1 has an DerivativeGroupLinearKHR " + "execution mode, so the product (18) must be a multiple of 4")); +} + +TEST_F(ValidateMode, GLComputeWorkgroupSizeDerivativeGroupQuadsOverride) { + // "If an object is decorated with the WorkgroupSize decoration, this takes + // precedence over any LocalSize or LocalSizeId execution mode." + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupQuadsKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main DerivativeGroupQuadsKHR +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_2 = OpConstant %int 2 +%int3_1 = OpConstantComposite %int3 %int_2 %int_2 %int_2 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + TEST_F(ValidateMode, GLComputeVulkanLocalSize) { const std::string spirv = R"( OpCapability Shader @@ -101,6 +320,97 @@ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } +TEST_F(ValidateMode, GLComputeZeroLocalSize) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 0 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Local Size execution mode must not have a product of zero")); +} + +TEST_F(ValidateMode, KernelZeroLocalSize) { + const std::string spirv = R"( +OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +OpExecutionMode %main LocalSize 1 1 0 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Local Size execution mode must not have a product of zero")); +} + +TEST_F(ValidateMode, GLComputeLocalSizeDerivativeGroupQuads) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupQuadsKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main DerivativeGroupQuadsKHR +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Local Size execution mode dimensions is (X = 1, Y = 1) but " + "Entry Point id 1 also has an DerivativeGroupQuadsKHR " + "execution mode, so both dimensions must be a multiple of 2")); +} + +TEST_F(ValidateMode, GLComputeLocalSizeDerivativeGroupLinear) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupLinearKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main DerivativeGroupLinearKHR +OpExecutionMode %main LocalSize 3 3 3 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Local Size execution mode dimensions is (X = 3, Y = 3, Z = 3) " + "but Entry Point id 1 also has an DerivativeGroupLinearKHR " + "execution mode, so the product (27) must be a multiple of 4")); +} + +TEST_F(ValidateMode, GLComputeLocalSizeDerivativeGroupQuadsCapabiltyOnly) { + // Declares capability but doesn't have OpExecutionMode + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupQuadsKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) { const std::string spirv = R"( OpCapability Shader @@ -135,6 +445,142 @@ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); } +TEST_F(ValidateMode, GLComputeZeroLocalSizeId) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId execution mode must not have a product of zero")); +} + +TEST_F(ValidateMode, GLComputeZeroSpecLocalSizeId) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_0 = OpSpecConstant %int 0 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateMode, KernelZeroLocalSizeId) { + const std::string spirv = R"( +OpCapability Addresses +OpCapability Linkage +OpCapability Kernel +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId execution mode must not have a product of zero")); +} + +// https://github.com/KhronosGroup/SPIRV-Tools/issues/5939 +TEST_F(ValidateMode, KernelZeroLocalSize64) { + const std::string spirv = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Int64 + OpCapability Linkage + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %test "test" %__spirv_BuiltInWorkgroupSize + OpExecutionMode %test ContractionOff + OpDecorate %__spirv_BuiltInWorkgroupSize Constant + OpDecorate %__spirv_BuiltInWorkgroupSize LinkageAttributes "__spirv_BuiltInWorkgroupSize" Import + OpDecorate %__spirv_BuiltInWorkgroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %ulong = OpTypeInt 64 0 + %v3ulong = OpTypeVector %ulong 3 +%_ptr_Input_v3ulong = OpTypePointer Input %v3ulong + %8 = OpTypeFunction %void +%__spirv_BuiltInWorkgroupSize = OpVariable %_ptr_Input_v3ulong Input + %test = OpFunction %void None %8 + %entry = OpLabel + %11 = OpLoad %v3ulong %__spirv_BuiltInWorkgroupSize Aligned 1 + %12 = OpCompositeExtract %ulong %11 0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, GLComputeLocalSizeIdDerivativeGroupQuads) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupQuadsKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_2 %int_1 %int_2 +OpExecutionMode %main DerivativeGroupQuadsKHR +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId execution mode dimensions is (X = 2, Y = 1) but " + "Entry Point id 1 also has an DerivativeGroupQuadsKHR " + "execution mode, so both dimensions must be a multiple of 2")); +} + +TEST_F(ValidateMode, GLComputeLocalSizeIdDerivativeGroupLinear) { + const std::string spirv = R"( +OpCapability Shader +OpCapability ComputeDerivativeGroupLinearKHR +OpExtension "SPV_KHR_compute_shader_derivatives" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +OpExecutionMode %main DerivativeGroupLinearKHR +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("LocalSizeId execution mode dimensions is (X = 1, Y = 1, Z = " + "1) but Entry Point id 1 also has an DerivativeGroupLinearKHR " + "execution mode, so the product (1) must be a multiple of 4")); +} + TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { const std::string spirv = R"( OpCapability Shader @@ -814,6 +1260,109 @@ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateModeExecution, MeshEXTOutputVertices) { + const std::string spirv = R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 3 +OpExecutionMode %main OutputPrimitivesNV 1 +OpExecutionMode %main OutputTrianglesNV +OpSource GLSL 460 +OpSourceExtension "GL_EXT_mesh_shader" +OpName %main "main" +OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpSetMeshOutputsEXT %uint_3 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateModeExecution, VulkanBadMeshEXTOutputVertices) { + const std::string spirv = R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 0 +OpExecutionMode %main OutputPrimitivesNV 1 +OpExecutionMode %main OutputTrianglesNV +OpSource GLSL 460 +OpSourceExtension "GL_EXT_mesh_shader" +OpName %main "main" +OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpSetMeshOutputsEXT %uint_3 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-MeshEXT-07330")); +} + +TEST_F(ValidateModeExecution, VulkanBadMeshEXTOutputOutputPrimitivesEXT) { + const std::string spirv = R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 1 +OpExecutionMode %main OutputPrimitivesNV 0 +OpExecutionMode %main OutputTrianglesNV +OpSource GLSL 460 +OpSourceExtension "GL_EXT_mesh_shader" +OpName %main "main" +OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpSetMeshOutputsEXT %uint_3 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-MeshEXT-07331")); +} + TEST_F(ValidateModeExecution, MeshNVOutputVertices) { const std::string spirv = R"( OpCapability Shader @@ -1282,6 +1831,23 @@ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateMode, FragmentShaderPostDepthCoverageVertexBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability SampleMaskPostDepthCoverage +OpExtension "SPV_KHR_post_depth_coverage" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionMode %main PostDepthCoverage +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Execution mode can only be used with the Fragment execution model")); +} TEST_F(ValidateMode, FragmentShaderStencilRefFrontTooManyModesBad) { const std::string spirv = R"( @@ -1331,6 +1897,24 @@ "execution modes.")); } +TEST_F(ValidateMode, FragmentShaderStencilRefReplacingVertexBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability StencilExportEXT +OpExtension "SPV_EXT_shader_stencil_export" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionMode %main StencilRefReplacingEXT +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Execution mode can only be used with the Fragment execution model")); +} + TEST_F(ValidateMode, FragmentShaderStencilRefFrontGood) { const std::string spirv = R"( OpCapability Shader @@ -2208,6 +2792,396 @@ "Execution mode can only be used with the Fragment execution model")); } +const std::string kNodeShaderPrelude = R"( +OpCapability Shader +OpCapability ShaderEnqueueAMDX +OpExtension "SPV_AMDX_shader_enqueue" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpEntryPoint GLCompute %other "other" +)"; + +const std::string kNodeShaderPostlude = R"( +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%node0 = OpConstantStringAMDX "node0" +%node1 = OpConstantStringAMDX "node1" +%node2 = OpConstantStringAMDX "node2" +%S = OpTypeStruct +%_payloadarr_S_0 = OpTypeNodePayloadArrayAMDX %S +%_payloadarr_S = OpTypeNodePayloadArrayAMDX %S +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%other = OpFunction %void None %void_fn +%entry0 = OpLabel +OpReturn +OpFunctionEnd +)"; + +TEST_F(ValidateMode, NodeShader) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateMode, NodeShaderModeShaderIndex) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionMode %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionMode %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands")); +} + +TEST_F(ValidateMode, NodeShaderModeIsApiEntry) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionMode %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands")); +} + +TEST_F(ValidateMode, NodeShaderModeMaxNodeRecursion) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionMode %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands")); +} + +TEST_F(ValidateMode, NodeShaderModeMaxNumWorkgroups) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionMode %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands")); +} + +TEST_F(ValidateMode, NodeShaderModeStaticNumWorkgroups) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionMode %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands")); +} + +TEST_F(ValidateMode, NodeShaderModeSharesInputWith) { + const std::string spirv = kNodeShaderPrelude + R"( +OpExecutionModeId %main ShaderIndexAMDX %uint_0 +OpExecutionModeId %main IsApiEntryAMDX %true +OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1 +OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpExecutionMode %main SharesInputWithAMDX %node0 %uint_0 +OpExecutionModeId %other ShaderIndexAMDX %uint_0 +OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1 +OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1 +OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2 +OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0 +OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1 +OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0 +)" + kNodeShaderPostlude; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands")); +} + +TEST_F(ValidateMode, GLComputeNoModeVulkanQCOM) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-10685")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, GLCompute execution model entry " + "points require either the TileShadingRateQCOM, LocalSize or " + "LocalSizeId execution mode or an object decorated with " + "WorkgroupSize " + "must be specified.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeBadQCOM) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpExecutionMode %main LocalSize 16 16 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If the TileShadingRateQCOM execution mode is used, " + "LocalSize and LocalSizeId must not be specified.")); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBadQCOM) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +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_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If the TileShadingRateQCOM execution mode is used, " + "LocalSize and LocalSizeId must not be specified.")); +} + +TEST_F(ValidateMode, NonCoherentTileAttachmentReadQCOMBad1) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main NonCoherentTileAttachmentReadQCOM +OpExecutionMode %main OriginUpperLeft +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: TileShadingQCOM")); +} + +TEST_F(ValidateMode, NonCoherentTileAttachmentReadQCOMBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 16 16 1 +OpExecutionMode %main NonCoherentTileAttachmentReadQCOM +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%uint = OpTypeInt 32 0 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The NonCoherentTileAttachmentQCOM execution mode must " + "not be used in any stage other than fragment")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad1) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%uint = OpTypeInt 32 0 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: TileShadingQCOM")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 2 3 +OpExecutionMode %main OriginUpperLeft +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingRateQCOM execution mode must not be " + "used in any stage other than compute")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 3 2 3 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingRateQCOM execution mode's x and y " + "values must be powers of 2")); +} + +TEST_F(ValidateMode, TileShadingRateQCOMBad4) { + const std::string spirv = R"( +OpCapability Shader +OpCapability TileShadingQCOM +OpExtension "SPV_QCOM_tile_shading" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main TileShadingRateQCOM 2 3 3 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The TileShadingRateQCOM execution mode's x and y " + "values must be powers of 2")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_non_uniform_test.cpp b/test/val/val_non_uniform_test.cpp index 530676d..b9ea5da 100644 --- a/test/val/val_non_uniform_test.cpp +++ b/test/val/val_non_uniform_test.cpp
@@ -958,7 +958,7 @@ ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Before SPIR-V 1.5, Id must be a constant instruction")); + HasSubstr("In SPIR-V 1.4 or earlier, Id must be a constant instruction")); } TEST_F(ValidateGroupNonUniform, BroadcastNonConstantSpv1p5) { @@ -1028,7 +1028,8 @@ ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Before SPIR-V 1.5, Index must be a constant instruction")); + HasSubstr( + "In SPIR-V 1.4 or earlier, Index must be a constant instruction")); } TEST_F(ValidateGroupNonUniform, QuadBroadcastNonConstantSpv1p5) {
diff --git a/test/val/val_opencl_test.cpp b/test/val/val_opencl_test.cpp index 9dab931..2f7526e 100644 --- a/test/val/val_opencl_test.cpp +++ b/test/val/val_opencl_test.cpp
@@ -43,23 +43,6 @@ "OpenCL\n")); } -TEST_F(ValidateOpenCL, NonOpenCLMemoryModelBad) { - std::string spirv = R"( - OpCapability Kernel - OpCapability Addresses - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Physical32 VulkanKHR -)"; - - CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Memory model must be OpenCL in the OpenCL environment.")); -} - TEST_F(ValidateOpenCL, NonVoidSampledTypeImageBad) { std::string spirv = R"( OpCapability Addresses
diff --git a/test/val/val_pipe_test.cpp b/test/val/val_pipe_test.cpp new file mode 100644 index 0000000..c05bc27 --- /dev/null +++ b/test/val/val_pipe_test.cpp
@@ -0,0 +1,493 @@ +// Copyright 2026 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "gmock/gmock.h" +#include "spirv-tools/libspirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidatePipe = spvtest::ValidateBase<bool>; + +const spv_target_env pipe_version = SPV_ENV_UNIVERSAL_1_1; + +std::string GenerateShaderCode(const std::string& body) { + std::ostringstream ss; + ss << R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Pipes +OpCapability GenericPointer +OpCapability PipeStorage +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" + +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 + +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_4 = OpConstant %uint 4 +%uint64_4 = OpConstant %uint64 4 +%uint_null = OpConstantNull %uint + +%_ptr_Generic_uint = OpTypePointer Generic %uint +%_ptr_Function_uint = OpTypePointer Function %uint + +%pipe_storage = OpTypePipeStorage +%reserved_id = OpTypeReserveId + +%void = OpTypeVoid +%read_pipe_type = OpTypePipe ReadOnly +%write_pipe_type = OpTypePipe WriteOnly +%read_write_pipe_type = OpTypePipe ReadWrite +%fn = OpTypeFunction %void %read_pipe_type %write_pipe_type %read_write_pipe_type + +%main = OpFunction %void None %fn +%read_pipe = OpFunctionParameter %read_pipe_type +%write_pipe = OpFunctionParameter %write_pipe_type +%read_write_pipe = OpFunctionParameter %read_write_pipe_type +%label = OpLabel + +%func_var = OpVariable %_ptr_Function_uint Function +OpStore %func_var %uint_null Aligned 4 +%generic_ptr = OpPtrCastToGeneric %_ptr_Generic_uint %func_var + +%const_pipe_storage = OpConstantPipeStorage %pipe_storage 4 4 32 +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + return ss.str(); +} + +TEST_F(ValidatePipe, PipeReadWriteGood) { + const std::string ss = R"( + %x = OpReadPipe %uint %read_pipe %generic_ptr %uint_4 %uint_4 + %y = OpWritePipe %uint %write_pipe %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, ReadPipeResultType) { + const std::string ss = R"( + %x = OpReadPipe %uint64 %read_pipe %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be a 32-bit int scalar")); +} + +TEST_F(ValidatePipe, ReadPipePipeType) { + const std::string ss = R"( + %x = OpReadPipe %uint %func_var %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pipe must be a type of OpTypePipe")); +} + +TEST_F(ValidatePipe, ReadPipeAccessQualifier) { + const std::string ss = R"( + %x = OpReadPipe %uint %write_pipe %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier")); +} + +TEST_F(ValidatePipe, WritePipeAccessQualifier) { + const std::string ss = R"( + %x = OpWritePipe %uint %read_pipe %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Pipe must have a OpTypePipe with WriteOnly access qualifier")); +} + +TEST_F(ValidatePipe, ReadPipePacketSizeInt64) { + const std::string ss = R"( + %x = OpReadPipe %uint %read_pipe %generic_ptr %uint64_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Size must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, ReadPipePacketAlignmentInt64) { + const std::string ss = R"( + %x = OpReadPipe %uint %read_pipe %generic_ptr %uint_4 %uint64_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Alignment must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, ReadPipePointerNotPoint) { + const std::string ss = R"( + %x = OpReadPipe %uint %read_pipe %read_pipe %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer must be a type of OpTypePointer")); +} + +TEST_F(ValidatePipe, ReadPipePointerNotGeneric) { + const std::string ss = R"( + %x = OpReadPipe %uint %read_pipe %func_var %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer must be a OpTypePointer with a Generic storage class")); +} + +TEST_F(ValidatePipe, PipeQueryGood) { + const std::string ss = R"( + %x = OpGetNumPipePackets %uint %read_pipe %uint_4 %uint_4 + %y = OpGetMaxPipePackets %uint %write_pipe %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, GetNumPipePacketsResult) { + const std::string ss = R"( + %x = OpGetNumPipePackets %uint64 %read_pipe %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be a 32-bit int scalar")); +} + +TEST_F(ValidatePipe, GetNumPipePacketsReadWrite) { + const std::string ss = R"( + %x = OpGetNumPipePackets %uint %read_write_pipe %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pipe must have a OpTypePipe with ReadOnly or " + "WriteOnly access qualifier")); +} + +TEST_F(ValidatePipe, GetNumPipePacketsPacketSize) { + const std::string ss = R"( + %x = OpGetNumPipePackets %uint %read_pipe %uint64_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Size must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, GetNumPipePacketsPacketAlignment) { + const std::string ss = R"( + %x = OpGetNumPipePackets %uint %read_pipe %uint_4 %uint64_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Alignment must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, PipeReservedReadWriteGood) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %w = OpReserveWritePipePackets %reserved_id %write_pipe %uint_1 %uint_4 %uint_4 + + %x = OpReservedReadPipe %uint %read_pipe %r %uint_null %generic_ptr %uint_4 %uint_4 + %y = OpReservedWritePipe %uint %write_pipe %w %uint_null %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, ReserveReadPipePacketsResult) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %uint %read_pipe %uint_1 %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be OpTypeReserveId")); +} + +TEST_F(ValidatePipe, ReserveReadPipePacketsWritePipe) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %write_pipe %uint_1 %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier")); +} + +TEST_F(ValidatePipe, ReserveReadPipePacketsNumPacks) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint64_4 %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Num Packets must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, ReservedReadPipeResult) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %x = OpReservedReadPipe %reserved_id %read_pipe %r %uint_null %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be a 32-bit int scalar")); +} + +TEST_F(ValidatePipe, ReservedReadPipeReserveType) { + const std::string ss = R"( + %x = OpReservedReadPipe %uint %read_pipe %func_var %uint_null %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reserve Id type must be OpTypeReserveId")); +} + +TEST_F(ValidatePipe, ReservedReadPipeIndexType) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %x = OpReservedReadPipe %uint %read_pipe %r %uint64_4 %generic_ptr %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, ReservedReadPipePacketAlignment) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %x = OpReservedReadPipe %uint %read_pipe %r %uint_null %generic_ptr %uint_4 %uint64_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Alignment must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, CommitPipeGood) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %w = OpReserveWritePipePackets %reserved_id %write_pipe %uint_1 %uint_4 %uint_4 + + OpCommitReadPipe %read_pipe %r %uint_4 %uint_4 + OpCommitWritePipe %write_pipe %w %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, CommitReadReservedId) { + const std::string ss = R"( + OpCommitReadPipe %read_pipe %func_var %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reserve Id type must be OpTypeReserveId")); +} + +TEST_F(ValidatePipe, CommitReadPacketSize) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + OpCommitReadPipe %read_pipe %r %uint64_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Size must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, IsValidReserveIdGood) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %x = OpIsValidReserveId %bool %r + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, IsValidReserveIdReservedId) { + const std::string ss = R"( + %x = OpIsValidReserveId %bool %func_var + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reserve Id type must be OpTypeReserveId")); +} + +TEST_F(ValidatePipe, IsValidReserveIdResult) { + const std::string ss = R"( + %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4 + %x = OpIsValidReserveId %uint %r + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be a bool scalar")); +} + +TEST_F(ValidatePipe, CreatePipeFromPipeStorageGood) { + const std::string ss = R"( + %x = OpCreatePipeFromPipeStorage %read_pipe_type %const_pipe_storage + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, CreatePipeFromPipeStorageType) { + const std::string ss = R"( + %x = OpCreatePipeFromPipeStorage %uint %const_pipe_storage + )"; + CompileSuccessfully(GenerateShaderCode(ss), SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be OpTypePipe")); +} + +TEST_F(ValidatePipe, ConstantPipeStorageType) { + const std::string ss = R"( + %bad = OpConstantPipeStorage %uint 4 4 32 + )"; + CompileSuccessfully(GenerateShaderCode(ss), SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be OpTypePipeStorage")); +} + +TEST_F(ValidatePipe, ConstantPipeStorageBlock) { + const std::string ss = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability Pipes + OpCapability PipeStorage + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %main "main" + %pipe_storage = OpTypePipeStorage + %const_pipe_storage = OpConstantPipeStorage %pipe_storage 4 4 32 + )"; + CompileSuccessfully(ss, pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ConstantPipeStorage must appear in a block")); +} + +TEST_F(ValidatePipe, GroupGood) { + const std::string ss = R"( + %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint_4 %uint_4 + %w = OpGroupReserveWritePipePackets %reserved_id %uint_2 %write_pipe %uint_1 %uint_4 %uint_4 + + OpGroupCommitReadPipe %uint_2 %read_pipe %r %uint_4 %uint_4 + OpGroupCommitWritePipe %uint_2 %write_pipe %w %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version)); +} + +TEST_F(ValidatePipe, GroupReserveReadPipePacketsResult) { + const std::string ss = R"( + %r = OpGroupReserveReadPipePackets %uint %uint_2 %read_pipe %uint_1 %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be OpTypeReserveId")); +} + +TEST_F(ValidatePipe, GroupReserveReadPipePacketsWrite) { + const std::string ss = R"( + %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_write_pipe %uint_1 %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier")); +} + +TEST_F(ValidatePipe, GroupReserveReadPipePacketsPacketSize) { + const std::string ss = R"( + %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint64_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Size must be a 32-bit scalar integer")); +} + +TEST_F(ValidatePipe, GroupCommitReadPipeWrite) { + const std::string ss = R"( + %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint_4 %uint_4 + OpGroupCommitReadPipe %uint_2 %write_pipe %r %uint_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier")); +} + +TEST_F(ValidatePipe, GroupCommitReadPipePacketSize) { + const std::string ss = R"( + %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint_4 %uint_4 + OpGroupCommitReadPipe %uint_2 %read_pipe %r %uint64_4 %uint_4 + )"; + CompileSuccessfully(GenerateShaderCode(ss), pipe_version); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Packet Size must be a 32-bit scalar integer")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_ray_query_test.cpp b/test/val/val_ray_query_test.cpp index e0eb067..7e86fe2 100644 --- a/test/val/val_ray_query_test.cpp +++ b/test/val/val_ray_query_test.cpp
@@ -30,20 +30,23 @@ using ValidateRayQuery = spvtest::ValidateBase<bool>; -std::string GenerateShaderCode( - const std::string& body, - const std::string& capabilities_and_extensions = "", - const std::string& declarations = "") { +std::string GenerateShaderCode(const std::string& body, + const std::string& capabilities = "", + const std::string& extensions = "", + const std::string& declarations = "") { std::ostringstream ss; ss << R"( OpCapability Shader OpCapability Int64 OpCapability Float64 OpCapability RayQueryKHR + )"; + ss << capabilities; + ss << R"( OpExtension "SPV_KHR_ray_query" )"; - ss << capabilities_and_extensions; + ss << extensions; ss << R"( OpMemoryModel Logical GLSL450 @@ -83,12 +86,15 @@ %u32_0 = OpConstant %u32 0 %u64_0 = OpConstant %u64 0 +%u32_2 = OpConstant %u32 2 +%arr2v3 = OpTypeArray %f32vec3 %u32_2 +%arr2f3 = OpTypeArray %f32 %u32_2 + %u32vec3_0 = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0 %f32vec3_0 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0 %f32vec4_0 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 -%ptr_rq = OpTypePointer Private %type_rq -%ray_query = OpVariable %ptr_rq Private +%ptr_rq = OpTypePointer Function %type_rq %ptr_as = OpTypePointer UniformConstant %type_as %top_level_as = OpVariable %ptr_as UniformConstant @@ -103,6 +109,7 @@ ss << R"( %main = OpFunction %void None %func %main_entry = OpLabel +%ray_query = OpVariable %ptr_rq Function )"; ss << body; @@ -398,7 +405,7 @@ OpRayQueryInitializeKHR %rq_param %as_2 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0 )"; - CompileSuccessfully(GenerateShaderCode(body, "", declaration).c_str()); + CompileSuccessfully(GenerateShaderCode(body, "", "", declaration).c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -626,6 +633,191 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateRayQuery, ClusterASNV) { + const std::string cap = R"( + OpCapability RayTracingClusterAccelerationStructureNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_cluster_acceleration_structure" + )"; + + const std::string body = R"( + %clusterid = OpRayQueryGetClusterIdNV %s32 %ray_query %s32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body, cap, ext).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +using RayQueryLSSNVCommon = spvtest::ValidateBase<std::string>; + +std::string RayQueryLSSNVResultType(std::string opcode, bool valid) { + if (opcode.compare("OpRayQueryGetIntersectionLSSPositionsNV") == 0) + return valid ? "%arr2v3" : "%f64"; + + if (opcode.compare("OpRayQueryGetIntersectionLSSRadiiNV") == 0) + return valid ? "%arr2f3" : "%f64"; + + if (opcode.compare("OpRayQueryGetIntersectionSphereRadiusNV") == 0 || + opcode.compare("OpRayQueryGetIntersectionLSSHitValueNV") == 0) { + return valid ? "%f32" : "%f64"; + } + + if (opcode.compare("OpRayQueryGetIntersectionSpherePositionNV") == 0) { + return valid ? "%f32vec3" : "%f64"; + } + + if (opcode.compare("OpRayQueryIsSphereHitNV") == 0 || + opcode.compare("OpRayQueryIsLSSHitNV") == 0) { + return valid ? "%bool" : "%f64"; + } + + return ""; +} + +TEST_P(RayQueryLSSNVCommon, Success) { + const std::string cap = R"( + OpCapability RayTracingSpheresGeometryNV + OpCapability RayTracingLinearSweptSpheresGeometryNV + )"; + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + std::string opcode = GetParam(); + std::ostringstream ss; + ss << "%result = "; + ss << " " << opcode << " "; + ss << RayQueryLSSNVResultType(opcode, true); + ss << " %ray_query "; + ss << " %s32_0 "; + CompileSuccessfully(GenerateShaderCode(ss.str(), cap, ext).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +INSTANTIATE_TEST_SUITE_P(ValidateRayQueryLSSNVCommon, RayQueryLSSNVCommon, + Values("OpRayQueryGetIntersectionSpherePositionNV", + "OpRayQueryGetIntersectionLSSPositionsNV", + "OpRayQueryGetIntersectionSphereRadiusNV", + "OpRayQueryGetIntersectionLSSRadiiNV", + "OpRayQueryGetIntersectionLSSHitValueNV", + "OpRayQueryIsSphereHitNV", + "OpRayQueryIsLSSHitNV")); + +TEST_F(ValidateRayQuery, RayQueryPositionFetchCapability) { + const std::string spirv = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpCapability RayTracingPositionFetchKHR + OpExtension "SPV_KHR_ray_query" + OpExtension "SPV_KHR_ray_tracing_position_fetch" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %rayQuery + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %6 = OpTypeRayQueryKHR +%_ptr_Private_6 = OpTypePointer Private %6 + %rayQuery = OpVariable %_ptr_Private_6 Private + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint_3 = OpConstant %uint 3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Function__arr_v3float_uint_3 = OpTypePointer Function %_arr_v3float_uint_3 + %main = OpFunction %void None %3 + %5 = OpLabel + %28 = OpRayQueryGetIntersectionTriangleVertexPositionsKHR %_arr_v3float_uint_3 %rayQuery %int_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("RayQueryGetIntersectionTriangleVertexPositionsKHR requires " + "one of these capabilities: RayQueryPositionFetchKHR")); +} + +TEST_F(ValidateRayQuery, RayQueryGetIntersectionTriangleVertexPositionsType) { + const std::string spirv = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpCapability RayQueryPositionFetchKHR + OpExtension "SPV_KHR_ray_query" + OpExtension "SPV_KHR_ray_tracing_position_fetch" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %rayQuery + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %6 = OpTypeRayQueryKHR +%_ptr_Private_6 = OpTypePointer Private %6 + %rayQuery = OpVariable %_ptr_Private_6 Private + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint_4 = OpConstant %uint 4 +%_arr_v3float_uint_4 = OpTypeArray %v3float %uint_4 +%_ptr_Function__arr_v3float_uint_4 = OpTypePointer Function %_arr_v3float_uint_4 + %main = OpFunction %void None %3 + %5 = OpLabel + %28 = OpRayQueryGetIntersectionTriangleVertexPositionsKHR %_arr_v3float_uint_4 %rayQuery %int_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 3 element array of 32-bit 3 component float " + "point vector as Result Type")); +} + +TEST_F(ValidateRayQuery, + RayQueryGetIntersectionTriangleVertexPositionsType32Bit) { + const std::string spirv = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpCapability RayQueryPositionFetchKHR + OpCapability Float64 + OpExtension "SPV_KHR_ray_query" + OpExtension "SPV_KHR_ray_tracing_position_fetch" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %rayQuery + OpExecutionMode %main LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %6 = OpTypeRayQueryKHR +%_ptr_Private_6 = OpTypePointer Private %6 + %rayQuery = OpVariable %_ptr_Private_6 Private + %uint = OpTypeInt 32 0 + %float64 = OpTypeFloat 64 + %v3float64 = OpTypeVector %float64 3 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint_3 = OpConstant %uint 3 +%_arr_v3float_uint_3 = OpTypeArray %v3float64 %uint_3 +%_ptr_Function__arr_v3float_uint_3 = OpTypePointer Function %_arr_v3float_uint_3 + %main = OpFunction %void None %3 + %5 = OpLabel + %28 = OpRayQueryGetIntersectionTriangleVertexPositionsKHR %_arr_v3float_uint_3 %rayQuery %int_1 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 3 element array of 32-bit 3 component float " + "point vector as Result Type")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_ray_tracing_reorder_test.cpp b/test/val/val_ray_tracing_reorder_test.cpp index 6038c38..4c6f4f0 100644 --- a/test/val/val_ray_tracing_reorder_test.cpp +++ b/test/val/val_ray_tracing_reorder_test.cpp
@@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Tests instructions from SPV_NV_shader_invocation_reorder. +// Tests instructions from SPV_NV_shader_invocation_reorder and +// SPV_EXT_shader_invocation_reorder. #include <sstream> #include <string> @@ -30,13 +31,21 @@ using ValidateRayTracingReorderNV = spvtest::ValidateBase<bool>; std::string GenerateReorderThreadCode(const std::string& body = "", - const std::string& declarations = "") { + const std::string& declarations = "", + const std::string& extensions = "", + const std::string& capabilities = "") { std::ostringstream ss; ss << R"( OpCapability RayTracingKHR OpCapability ShaderInvocationReorderNV + )"; + ss << capabilities; + ss << R"( OpExtension "SPV_KHR_ray_tracing" OpExtension "SPV_NV_shader_invocation_reorder" + )"; + ss << extensions; + ss << R"( %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint RayGenerationNV %main "main" %hObj @@ -593,6 +602,968 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateRayTracingReorderNV, ClusterASNV) { + const std::string cap = R"( + OpCapability RayTracingClusterAccelerationStructureNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_cluster_acceleration_structure" + )"; + + const std::string declarations = R"( + %int = OpTypeInt 32 1 + %_ptr_Function_int = OpTypePointer Function %int + )"; + + const std::string body = R"( + %id = OpVariable %_ptr_Function_int Function + %12 = OpHitObjectGetClusterIdNV %int %hObj + OpStore %id %12 + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderNV, LSSGetSpherePositionNV) { + const std::string cap = R"( + OpCapability RayTracingSpheresGeometryNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + + const std::string declarations = R"( + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %_ptr_Function_v3float = OpTypePointer Function %v3float + )"; + + const std::string body = R"( + %pos = OpVariable %_ptr_Function_v3float Function + %result = OpHitObjectGetSpherePositionNV %v3float %hObj + OpStore %pos %result + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderNV, LSSGetLSSPositionsNV) { + const std::string cap = R"( + OpCapability RayTracingSpheresGeometryNV + OpCapability RayTracingLinearSweptSpheresGeometryNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + + const std::string declarations = R"( + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %v3float = OpTypeVector %float 3 + %uint_2 = OpConstant %uint 2 + %arr = OpTypeArray %v3float %uint_2 + %_ptr_Function_v3float = OpTypePointer Function %arr + )"; + + const std::string body = R"( + %lsspos = OpVariable %_ptr_Function_v3float Function + %result = OpHitObjectGetLSSPositionsNV %arr %hObj + OpStore %lsspos %result + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderNV, LSSGetSphereRadiusNV) { + const std::string cap = R"( + OpCapability RayTracingSpheresGeometryNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + + const std::string declarations = R"( + %float = OpTypeFloat 32 + %_ptr_Function_float = OpTypePointer Function %float + )"; + + const std::string body = R"( + %rad = OpVariable %_ptr_Function_float Function + %result = OpHitObjectGetSphereRadiusNV %float %hObj + OpStore %rad %result + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderNV, LSSGetLSSRadiiNV) { + const std::string cap = R"( + OpCapability RayTracingLinearSweptSpheresGeometryNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + + const std::string declarations = R"( + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %arr = OpTypeArray %float %uint_2 + %_ptr_Function_float = OpTypePointer Function %arr + )"; + + const std::string body = R"( + %rad = OpVariable %_ptr_Function_float Function + %result = OpHitObjectGetLSSRadiiNV %arr %hObj + OpStore %rad %result + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderNV, LSSIsSphereHitNV) { + const std::string cap = R"( + OpCapability RayTracingSpheresGeometryNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + + const std::string declarations = R"( + %bool = OpTypeBool + %_ptr_Function_bool = OpTypePointer Function %bool + )"; + + const std::string body = R"( + %ishit = OpVariable %_ptr_Function_bool Function + %result = OpHitObjectIsSphereHitNV %bool %hObj + OpStore %ishit %result + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderNV, LSSIsLSSHitNV) { + const std::string cap = R"( + OpCapability RayTracingLinearSweptSpheresGeometryNV + )"; + + const std::string ext = R"( + OpExtension "SPV_NV_linear_swept_spheres" + )"; + + const std::string declarations = R"( + %bool = OpTypeBool + %_ptr_Function_bool = OpTypePointer Function %bool + )"; + + const std::string body = R"( + %ishit = OpVariable %_ptr_Function_bool Function + %result = OpHitObjectIsLSSHitNV %bool %hObj + OpStore %ishit %result + )"; + + CompileSuccessfully( + GenerateReorderThreadCode(body, declarations, ext, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +// EXT Extension Tests +using ValidateRayTracingReorderEXT = spvtest::ValidateBase<bool>; + +std::string GenerateReorderThreadCodeEXT(const std::string& body = "", + const std::string& declarations = "", + const std::string& extensions = "", + const std::string& capabilities = "") { + std::ostringstream ss; + ss << R"( + OpCapability RayTracingKHR + OpCapability ShaderInvocationReorderEXT + )"; + ss << capabilities; + ss << R"( + OpExtension "SPV_KHR_ray_tracing" + OpExtension "SPV_EXT_shader_invocation_reorder" + )"; + ss << extensions; + ss << R"( + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint RayGenerationNV %main "main" %hObj + OpSourceExtension "GL_EXT_ray_tracing" + OpSourceExtension "GL_EXT_shader_invocation_reorder" + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %6 = OpTypeHitObjectEXT +%_ptr_Private_6 = OpTypePointer Private %6 + %hObj = OpVariable %_ptr_Private_6 Private + )"; + ss << declarations; + + ss << R"( + %main = OpFunction %void None %3 + %5 = OpLabel + )"; + + ss << body; + + ss << R"( + OpReturn + OpFunctionEnd + )"; + return ss.str(); +} + +std::string GenerateReorderShaderCodeEXT(const std::string& body = "", + const std::string& declarations = "", + const std::string& extensions = "", + const std::string& capabilties = "") { + std::ostringstream ss; + ss << R"( + OpCapability RayTracingKHR + OpCapability ShaderInvocationReorderEXT + )"; + ss << capabilties; + ss << R"( OpExtension "SPV_KHR_ray_tracing" + OpExtension "SPV_EXT_shader_invocation_reorder" + )"; + ss << extensions; + ss << R"( + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint RayGenerationKHR %main "main" %attr %_ %hObj %payload %__0 %as %__1 + OpSource GLSL 460 + OpSourceExtension "GL_EXT_ray_tracing" + OpSourceExtension "GL_EXT_shader_invocation_reorder" + OpName %main "main" + OpName %attr "attr" + OpName %hBlock "hBlock" + OpMemberName %hBlock 0 "attrval" + OpName %_ "" + OpName %hObj "hObj" + OpName %payload "payload" + OpName %pBlock "pBlock" + OpMemberName %pBlock 0 "val1" + OpMemberName %pBlock 1 "val2" + OpName %__0 "" + OpName %as "as" + OpName %block "block" + OpMemberName %block 0 "op" + OpName %__1 "" + OpDecorate %hBlock Block + OpDecorate %pBlock Block + OpDecorate %as DescriptorSet 0 + OpDecorate %as Binding 0 + OpMemberDecorate %block 0 Offset 0 + OpDecorate %block Block + OpDecorate %__1 DescriptorSet 0 + OpDecorate %__1 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_HitObjectAttributeEXT_v2float = OpTypePointer HitObjectAttributeEXT %v2float + %attr = OpVariable %_ptr_HitObjectAttributeEXT_v2float HitObjectAttributeEXT + %float_1 = OpConstant %float 1 + %11 = OpConstantComposite %v2float %float_1 %float_1 + %hBlock = OpTypeStruct %float +%_ptr_HitObjectAttributeEXT_hBlock = OpTypePointer HitObjectAttributeEXT %hBlock + %_ = OpVariable %_ptr_HitObjectAttributeEXT_hBlock HitObjectAttributeEXT + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_2 = OpConstant %float 2 +%_ptr_HitObjectAttributeEXT_float = OpTypePointer HitObjectAttributeEXT %float + %20 = OpTypeHitObjectEXT + %_ptr_Private_20 = OpTypePointer Private %20 + %hObj = OpVariable %_ptr_Private_20 Private + %23 = OpTypeAccelerationStructureKHR + %_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 + %as = OpVariable %_ptr_UniformConstant_23 UniformConstant + %v4float = OpTypeVector %float 4 +%_ptr_RayPayloadKHR_v4float = OpTypePointer RayPayloadKHR %v4float + %payload = OpVariable %_ptr_RayPayloadKHR_v4float RayPayloadKHR + %pBlock = OpTypeStruct %v2float %v2float +%_ptr_RayPayloadKHR_pBlock = OpTypePointer RayPayloadKHR %pBlock + %__0 = OpVariable %_ptr_RayPayloadKHR_pBlock RayPayloadKHR + %block = OpTypeStruct %float +%_ptr_StorageBuffer_block = OpTypePointer StorageBuffer %block + %__1 = OpVariable %_ptr_StorageBuffer_block StorageBuffer + )"; + + ss << declarations; + + ss << R"( + %main = OpFunction %void None %3 + %5 = OpLabel + )"; + + ss << body; + + ss << R"( + OpReturn + OpFunctionEnd)"; + return ss.str(); +} + +TEST_F(ValidateRayTracingReorderEXT, ReorderThreadWithHintEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + )"; + + const std::string body = R"( + OpReorderThreadWithHintEXT %uint_4 %uint_4 + )"; + + CompileSuccessfully(GenerateReorderThreadCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, ReorderThreadWithHitObjectEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %uint_2 = OpConstant %uint 2 + )"; + + const std::string body = R"( + OpReorderThreadWithHitObjectEXT %hObj + OpReorderThreadWithHitObjectEXT %hObj %uint_4 %uint_2 + )"; + + CompileSuccessfully(GenerateReorderThreadCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceRayEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %v3float = OpTypeVector %float 3 + %float_0_5 = OpConstant %float 0.5 + %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %int_1 = OpConstant %int 1 + )"; + + const std::string body = R"( + OpStore %attr %11 + %26 = OpLoad %23 %as + OpHitObjectTraceRayEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceRayMotionEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %v3float = OpTypeVector %float 3 + %float_0_5 = OpConstant %float 0.5 + %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_10 = OpConstant %float 10 + %int_2 = OpConstant %int 2 + )"; + + const std::string body = R"( + OpStore %attr %11 + %26 = OpLoad %23 %as + OpHitObjectTraceRayMotionEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordEmptyEXT) { + const std::string body = R"( + OpHitObjectRecordEmptyEXT %hObj + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordMissEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %v3float = OpTypeVector %float 3 + %float_0_5 = OpConstant %float 0.5 + %29 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %float_1_5 = OpConstant %float 1.5 + %31 = OpConstantComposite %v3float %float_1_5 %float_1_5 %float_1_5 + %float_5 = OpConstant %float 5 + )"; + + const std::string body = R"( + OpHitObjectRecordMissEXT %hObj %uint_1 %uint_1 %29 %float_2 %31 %float_5 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectIsHitEXT) { + const std::string declarations = R"( + %bool = OpTypeBool + %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + )"; + + const std::string body = R"( + %26 = OpHitObjectIsHitEXT %bool %hObj + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %33 = OpAccessChain %_ptr_StorageBuffer_float %__1 %int_0 + OpStore %33 %float_1 + OpBranch %28 + %28 = OpLabel + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectIsMissEXT) { + const std::string declarations = R"( + %bool = OpTypeBool + %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + )"; + + const std::string body = R"( + %26 = OpHitObjectIsMissEXT %bool %hObj + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %33 = OpAccessChain %_ptr_StorageBuffer_float %__1 %int_0 + OpStore %33 %float_1 + OpBranch %28 + %28 = OpLabel + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectIsEmptyEXT) { + const std::string declarations = R"( + %bool = OpTypeBool + %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + )"; + + const std::string body = R"( + %26 = OpHitObjectIsEmptyEXT %bool %hObj + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %33 = OpAccessChain %_ptr_StorageBuffer_float %__1 %int_0 + OpStore %33 %float_1 + OpBranch %28 + %28 = OpLabel + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetGeometryIndexEXT) { + const std::string declarations = R"( + %_ptr_Function_int = OpTypePointer Function %int + )"; + + const std::string body = R"( + %id = OpVariable %_ptr_Function_int Function + %12 = OpHitObjectGetGeometryIndexEXT %int %hObj + OpStore %id %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetPrimitiveIndexEXT) { + const std::string declarations = R"( + %_ptr_Function_int = OpTypePointer Function %int + )"; + + const std::string body = R"( + %id = OpVariable %_ptr_Function_int Function + %12 = OpHitObjectGetPrimitiveIndexEXT %int %hObj + OpStore %id %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetInstanceIdEXT) { + const std::string declarations = R"( + %_ptr_Function_int = OpTypePointer Function %int + )"; + + const std::string body = R"( + %id = OpVariable %_ptr_Function_int Function + %12 = OpHitObjectGetInstanceIdEXT %int %hObj + OpStore %id %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetInstanceCustomIndexEXT) { + const std::string declarations = R"( + %_ptr_Function_int = OpTypePointer Function %int + )"; + + const std::string body = R"( + %id = OpVariable %_ptr_Function_int Function + %12 = OpHitObjectGetInstanceCustomIndexEXT %int %hObj + OpStore %id %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetHitKindEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %_ptr_Function_uint = OpTypePointer Function %uint + )"; + + const std::string body = R"( + %uid = OpVariable %_ptr_Function_uint Function + %12 = OpHitObjectGetHitKindEXT %uint %hObj + OpStore %uid %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetCurrentTimeEXT) { + const std::string declarations = R"( + %_ptr_Function_float = OpTypePointer Function %float + )"; + + const std::string body = R"( + %time = OpVariable %_ptr_Function_float Function + %12 = OpHitObjectGetCurrentTimeEXT %float %hObj + OpStore %time %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetObjectRayOriginEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %_ptr_Function_v3float = OpTypePointer Function %v3float + )"; + + const std::string body = R"( + %oorig = OpVariable %_ptr_Function_v3float Function + %13 = OpHitObjectGetObjectRayOriginEXT %v3float %hObj + OpStore %oorig %13 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetObjectRayDirectionEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %_ptr_Function_v3float = OpTypePointer Function %v3float + )"; + + const std::string body = R"( + %odir = OpVariable %_ptr_Function_v3float Function + %13 = OpHitObjectGetObjectRayDirectionEXT %v3float %hObj + OpStore %odir %13 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetRayTMaxEXT) { + const std::string declarations = R"( + %_ptr_Function_float = OpTypePointer Function %float + )"; + + const std::string body = R"( + %tmax = OpVariable %_ptr_Function_float Function + %12 = OpHitObjectGetRayTMaxEXT %float %hObj + OpStore %tmax %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetRayTMinEXT) { + const std::string declarations = R"( + %_ptr_Function_float = OpTypePointer Function %float + )"; + + const std::string body = R"( + %tmin = OpVariable %_ptr_Function_float Function + %12 = OpHitObjectGetRayTMinEXT %float %hObj + OpStore %tmin %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetRayFlagsEXT) { + const std::string declarations = R"( + %_ptr_Function_int = OpTypePointer Function %int + )"; + + const std::string body = R"( + %flags = OpVariable %_ptr_Function_int Function + %12 = OpHitObjectGetRayFlagsEXT %int %hObj + OpStore %flags %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetWorldRayOriginEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %_ptr_Function_v3float = OpTypePointer Function %v3float + )"; + + const std::string body = R"( + %orig = OpVariable %_ptr_Function_v3float Function + %13 = OpHitObjectGetWorldRayOriginEXT %v3float %hObj + OpStore %orig %13 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetWorldRayDirectionEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %_ptr_Function_v3float = OpTypePointer Function %v3float + )"; + + const std::string body = R"( + %dir = OpVariable %_ptr_Function_v3float Function + %13 = OpHitObjectGetWorldRayDirectionEXT %v3float %hObj + OpStore %dir %13 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetObjectToWorldEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %mat4v3float = OpTypeMatrix %v3float 4 + %_ptr_Function_mat4v3float = OpTypePointer Function %mat4v3float + )"; + + const std::string body = R"( + %otw = OpVariable %_ptr_Function_mat4v3float Function + %14 = OpHitObjectGetObjectToWorldEXT %mat4v3float %hObj + OpStore %otw %14 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetWorldToObjectEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %mat4v3float = OpTypeMatrix %v3float 4 + %_ptr_Function_mat4v3float = OpTypePointer Function %mat4v3float + )"; + + const std::string body = R"( + %wto = OpVariable %_ptr_Function_mat4v3float Function + %14 = OpHitObjectGetWorldToObjectEXT %mat4v3float %hObj + OpStore %wto %14 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetShaderRecordBufferHandleEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %_ptr_Function_v2uint = OpTypePointer Function %v2uint + )"; + + const std::string body = R"( + %handle = OpVariable %_ptr_Function_v2uint Function + %13 = OpHitObjectGetShaderRecordBufferHandleEXT %v2uint %hObj + OpStore %handle %13 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, + HitObjectGetShaderBindingTableRecordIndexEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %_ptr_Function_uint = OpTypePointer Function %uint + )"; + + const std::string body = R"( + %rid = OpVariable %_ptr_Function_uint Function + %12 = OpHitObjectGetShaderBindingTableRecordIndexEXT %uint %hObj + OpStore %rid %12 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, + HitObjectSetShaderBindingTableRecordIndexEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 + )"; + + const std::string body = R"( + OpHitObjectSetShaderBindingTableRecordIndexEXT %hObj %uint_5 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectGetAttributesEXT) { + const std::string body = R"( + OpHitObjectGetAttributesEXT %hObj %attr + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectExecuteShaderEXT) { + const std::string body = R"( + OpHitObjectExecuteShaderEXT %hObj %payload + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, + HitObjectGetIntersectionTriangleVertexPositionsEXT) { + const std::string declarations = R"( + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %arr_3_v3float = OpTypeArray %v3float %uint_3 + %_ptr_Function_arr_3_v3float = OpTypePointer Function %arr_3_v3float + )"; + + const std::string body = R"( + %vertices = OpVariable %_ptr_Function_arr_3_v3float Function + %result = OpHitObjectGetIntersectionTriangleVertexPositionsEXT %arr_3_v3float %hObj + OpStore %vertices %result + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordFromQueryEXT) { + const std::string cap = R"( + OpCapability RayQueryKHR + )"; + const std::string extensions = R"( + OpExtension "SPV_KHR_ray_query" + )"; + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 + %rayquery_type = OpTypeRayQueryKHR + %_ptr_Function_rayquery = OpTypePointer Function %rayquery_type + )"; + + const std::string body = R"( + %ray_query = OpVariable %_ptr_Function_rayquery Function + OpHitObjectRecordFromQueryEXT %hObj %ray_query %uint_5 %attr + )"; + + CompileSuccessfully( + GenerateReorderShaderCodeEXT(body, declarations, extensions, cap).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordMissMotionEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %v3float = OpTypeVector %float 3 + %float_0_5 = OpConstant %float 0.5 + %29 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %float_1_5 = OpConstant %float 1.5 + %31 = OpConstantComposite %v3float %float_1_5 %float_1_5 %float_1_5 + %float_5 = OpConstant %float 5 + %float_10 = OpConstant %float 10 + )"; + + const std::string body = R"( + OpHitObjectRecordMissMotionEXT %hObj %uint_1 %uint_1 %29 %float_2 %31 %float_5 %float_10 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +// Fused Hit Object Instructions Tests + +TEST_F(ValidateRayTracingReorderEXT, HitObjectReorderExecuteShaderEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %uint_2 = OpConstant %uint 2 + )"; + + const std::string body = R"( + OpHitObjectReorderExecuteShaderEXT %hObj %payload + OpHitObjectReorderExecuteShaderEXT %hObj %payload %uint_4 %uint_2 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceReorderExecuteEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + %uint_2 = OpConstant %uint 2 + %v3float = OpTypeVector %float 3 + %float_0_5 = OpConstant %float 0.5 + %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %int_1 = OpConstant %int 1 + )"; + + const std::string body = R"( + OpStore %attr %11 + %26 = OpLoad %23 %as + OpHitObjectTraceReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload + OpHitObjectTraceReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload %uint_4 %uint_2 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceMotionReorderExecuteEXT) { + const std::string declarations = R"( + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + %uint_2 = OpConstant %uint 2 + %v3float = OpTypeVector %float 3 + %float_0_5 = OpConstant %float 0.5 + %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_10 = OpConstant %float 10 + %int_2 = OpConstant %int 2 + )"; + + const std::string body = R"( + OpStore %attr %11 + %26 = OpLoad %23 %as + OpHitObjectTraceMotionReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0 + OpHitObjectTraceMotionReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0 %uint_4 %uint_2 + )"; + + CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_ray_tracing_test.cpp b/test/val/val_ray_tracing_test.cpp index 60f2f89..476b228 100644 --- a/test/val/val_ray_tracing_test.cpp +++ b/test/val/val_ray_tracing_test.cpp
@@ -667,6 +667,37 @@ "IncomingCallableDataKHR storage class in the interface")); } +TEST_F(ValidateRayTracing, RayTracingPositionFetchCapability) { + const std::string spirv = R"( + OpCapability RayTracingKHR + OpCapability RayQueryPositionFetchKHR + OpExtension "SPV_KHR_ray_tracing" + OpExtension "SPV_KHR_ray_tracing_position_fetch" + OpMemoryModel Logical GLSL450 + OpEntryPoint AnyHitKHR %main "main" %gl_HitTriangleVertexPositionsEXT + OpDecorate %gl_HitTriangleVertexPositionsEXT BuiltIn HitTriangleVertexPositionsKHR + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 +%gl_HitTriangleVertexPositionsEXT = OpVariable %_ptr_Input__arr_v3float_uint_3 Input + %main = OpFunction %void None %3 + %5 = OpLabel + OpTerminateRayKHR + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_VULKAN_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Decorate requires one of these capabilities: " + "RayTracingPositionFetchKHR")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index d4170e6..b583fe3 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp
@@ -249,6 +249,194 @@ HasSubstr("OpFunctionCall Argument <id> '")); } +TEST_F(ValidateStorage, TileAttachmentQCOMBad1) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 1D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Any OpTypeImage variable in the TileAttachmentQCOM " + "Storage Class must have 2D as its dimension")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad2) { + const std::string spirv = R"( + OpCapability Shader + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 Binding 2 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Any variable in the TileAttachmentQCOM Storage Class " + "must be decorated with DescriptorSet and Binding")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad3) { + const std::string spirv = R"( + OpCapability Shader + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Any variable in the TileAttachmentQCOM Storage Class " + "must be decorated with DescriptorSet and Binding")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad4) { + const std::string spirv = R"( + OpCapability Shader + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 0 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04657")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled must be 1 or 2 in the Vulkan environment")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad5) { + const std::string spirv = R"( + OpCapability Shader + OpCapability ImageQuery + OpCapability TileShadingQCOM + OpExtension "SPV_QCOM_tile_shading" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_2 = OpConstant %int 2 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %main = OpFunction %void None %3 + %5 = OpLabel + %154 = OpLoad %44 %color1 + %156 = OpImageQuerySizeLod %v2int %154 %int_2 + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Any variable in the TileAttachmentQCOM Storage Class " + "must not be consumed by an OpImageQuery* instruction")); +} + +TEST_F(ValidateStorage, TileAttachmentQCOMBad6) { + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %color1 Binding 2 + OpDecorate %color1 DescriptorSet 0 + %void = OpTypeVoid + %int = OpTypeInt 32 1 + %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i +%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44 + %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_VULKAN_1_4; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these capabilities: TileShadingQCOM")); +} + std::string GenerateExecutionModelCode(const std::string& execution_model, const std::string& storage_class, bool store) { @@ -265,7 +453,8 @@ OpMemoryModel Logical GLSL450 OpEntryPoint )" << execution_model << R"( %func "func" %var - )" << mode << R"( + )" + << mode << R"( OpDecorate %var Location 0 %intt = OpTypeInt 32 0 %int0 = OpConstant %intt 0 @@ -273,10 +462,12 @@ %vfunct = OpTypeFunction %voidt %ptr = OpTypePointer )" << storage_class << R"( %intt -%var = OpVariable %ptr )" << storage_class << R"( +%var = OpVariable %ptr )" + << storage_class << R"( %func = OpFunction %voidt None %vfunct %funcl = OpLabel - )" << operation << R"( + )" + << operation << R"( OpReturn OpFunctionEnd )";
diff --git a/test/val/val_tensor_test.cpp b/test/val/val_tensor_test.cpp new file mode 100644 index 0000000..b9bf424 --- /dev/null +++ b/test/val/val_tensor_test.cpp
@@ -0,0 +1,1192 @@ +// Copyright (c) 2023-2025 Arm Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::ContainsRegex; +using ::testing::HasSubstr; + +using ValidateTensor = spvtest::ValidateBase<std::string>; + +constexpr spv_target_env SPVENV = SPV_ENV_VULKAN_1_3; + +std::string GenerateModule(const std::string& body) { + const std::string header = R"( + OpCapability Shader + OpCapability VulkanMemoryModel + OpCapability Int8 + OpCapability TensorsARM + OpExtension "SPV_ARM_tensors" + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %fnep "main" + OpExecutionMode %fnep LocalSize 1 1 1 + OpDecorate %tensor_var DescriptorSet 0 + OpDecorate %tensor_var Binding 0 + OpDecorate %tensor_var_float_unranked DescriptorSet 0 + OpDecorate %tensor_var_float_unranked Binding 1 + OpDecorate %tensor_var_spec_rank DescriptorSet 0 + OpDecorate %tensor_var_spec_rank Binding 2 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %fnty = OpTypeFunction %void + %uint_vec4 = OpTypeVector %uint 4 + %uint_0 = OpConstant %uint 0 + %uint_0_spec = OpSpecConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 + %uint_42 = OpConstant %uint 42 + %uint_1_bis = OpConstant %uint 1 + %float_1 = OpConstant %float 1 +%uint_vec4_1_1_1_1 = OpConstantComposite %uint_vec4 %uint_1 %uint_1 %uint_1 %uint_1 + %uint_arr1 = OpTypeArray %uint %uint_1 + %uint_arr2 = OpTypeArray %uint %uint_2 + %uint_arr3 = OpTypeArray %uint %uint_3 + %uint_arr4 = OpTypeArray %uint %uint_4 + %float_arr4 = OpTypeArray %float %uint_4 +%uint_arr4_1_1_1_1 = OpConstantComposite %uint_arr4 %uint_1 %uint_1 %uint_1 %uint_1 +%uint_arr4_0_0_0_0_spec = OpSpecConstantComposite %uint_arr4 %uint_0_spec %uint_0_spec %uint_0_spec %uint_0_spec + %uint_arr2_1_1 = OpConstantComposite %uint_arr2 %uint_1 %uint_1 +%float_arr4_1_1_1_1 = OpConstantComposite %float_arr4 %float_1 %float_1 %float_1 %float_1 +%uint_arr4_1_1_0_1 = OpConstantComposite %uint_arr4 %uint_1 %uint_1 %uint_0 %uint_1 + %uint_ptr_Private = OpTypePointer Private %uint +%uint_arr4_ptr_Private = OpTypePointer Private %uint_arr4 + %uint_var_1 = OpVariable %uint_ptr_Private Private %uint_1 +%var_uint_arr4_1_1_1_1 = OpVariable %uint_arr4_ptr_Private Private %uint_arr4_1_1_1_1 + %tensor_uint_4 = OpTypeTensorARM %uint %uint_4 + %tensor_uint_spec = OpTypeTensorARM %uint %uint_0_spec + %tensor_float = OpTypeTensorARM %float +%tensor_uint_4_ptr_UniformConstant = OpTypePointer UniformConstant %tensor_uint_4 +%tensor_var = OpVariable %tensor_uint_4_ptr_UniformConstant UniformConstant +%tensor_float_ptr_UniformConstant = OpTypePointer UniformConstant %tensor_float +%tensor_var_float_unranked = OpVariable %tensor_float_ptr_UniformConstant UniformConstant +%tensor_uint_spec_ptr_UniformConstant = OpTypePointer UniformConstant %tensor_uint_spec +%tensor_var_spec_rank = OpVariable %tensor_uint_spec_ptr_UniformConstant UniformConstant +)"; + const std::string footer = R"( + %fnep = OpFunction %void None %fnty + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + return header + body + footer; +} + +// +// Type tests +// + +TEST_F(ValidateTensor, ValidTypeElementTypeOnly) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTypeTensorElementTypeNotScalar) { + const std::string src = R"( + %test_type = OpTypeTensorARM %void +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpTypeTensorARM Element Type <id> " + "'.*' is not a scalar type.*")); +} + +TEST_F(ValidateTensor, InvalidTypeDuplicateElementTypeTensorType) { + const std::string src = R"( + %typeA = OpTypeTensorARM %uint + %typeB = OpTypeTensorARM %uint +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Duplicate non-aggregate type declarations are not allowed.")); +} + +TEST_F(ValidateTensor, ValidTypeElementTypeAndRank) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTypeElementTypeAndRankUsingSpecConstant) { + const std::string src = R"( + %rank_spec = OpSpecConstant %uint 0 + %test_type = OpTypeTensorARM %uint %rank_spec +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTypeDuplicateRankedTensorType) { + const std::string src = R"( + %typeA = OpTypeTensorARM %uint %uint_1 + %typeB = OpTypeTensorARM %uint %uint_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Duplicate non-aggregate type declarations are not allowed.")); +} + +TEST_F(ValidateTensor, ValidTypeEquivalentRankedTensorType) { + const std::string src = R"( + %typeA = OpTypeTensorARM %uint %uint_1 + %typeB = OpTypeTensorARM %uint %uint_1_bis +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTypeRankNotConstant) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_var_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpTypeTensorARM Rank <id> '.*' " + "is not a constant instruction.*")); +} + +TEST_F(ValidateTensor, InvalidTypeRankConstantButNotIntegerType) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %float_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpTypeTensorARM Rank <id> '.*' " + "does not have a scalar integer type.*")); +} + +TEST_F(ValidateTensor, InvalidTypeRankConstantIntegerTypeButNotScalar) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_vec4_1_1_1_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpTypeTensorARM Rank <id> '.*' " + "does not have a scalar integer type.*")); +} + +TEST_F(ValidateTensor, InvalidTypeRank0) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_0 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpTypeTensorARM Rank <id> '.*' must " + "define a value greater than 0.*")); +} + +TEST_F(ValidateTensor, ValidTypeElementTypeAndRankAndShape) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_4 %uint_arr4_1_1_1_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTypeElementTypeAndRankAndShapeUsingSpecConstant) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_0_spec %uint_arr4_0_0_0_0_spec +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTypeShapeNotConstant) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_4 %var_uint_arr4_1_1_1_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpTypeTensorARM Shape <id> '.*' is not " + "a constant instruction.*")); +} + +TEST_F(ValidateTensor, InvalidTypeShapeConstantButNotArrayOfInteger) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_4 %uint_vec4_1_1_1_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex( + "OpTypeTensorARM Shape <id> '.*' is not " + "an array of integer type whose Length is equal to Rank.*")); +} + +TEST_F(ValidateTensor, InvalidTypeShapeConstantArrayOfIntegerWrongLength) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_1 %uint_arr4_1_1_1_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex( + "OpTypeTensorARM Shape <id> '.*' is not " + "an array of integer type whose Length is equal to Rank.*")); +} + +TEST_F(ValidateTensor, + InvalidTypeShapeConstantArrayOfIntegerWithZeroConstituent) { + const std::string src = R"( + %test_type = OpTypeTensorARM %uint %uint_4 %uint_arr4_1_1_0_1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex( + "OpTypeTensorARM Shape constituent 2 is not greater than 0.*")); +} + +// +// Constants tests +// +TEST_F(ValidateTensor, ValidNullTensorConstantRank1) { + const std::string src = R"( + %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4 + %cst = OpConstantNull %ts_uint_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidNullTensorConstantRank4) { + const std::string src = R"( +%uint_arr4_2_2_2_2 = OpConstantComposite %uint_arr4 %uint_2 %uint_2 %uint_2 %uint_2 + %ts_uint_r4 = OpTypeTensorARM %uint %uint_4 %uint_arr4_2_2_2_2 + %cst = OpConstantNull %ts_uint_r4 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidNullTensorConstantRankedButNotShaped) { + const std::string src = R"( + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 + %cst = OpConstantNull %ts_uint_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpConstantNull Result Type <id> '.*' " + "cannot have a null value.*")); +} + +TEST_F(ValidateTensor, InvalidNullTensorConstantNotRanked) { + const std::string src = R"( + %ts_uint_r1 = OpTypeTensorARM %uint + %cst = OpConstantNull %ts_uint_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpConstantNull Result Type <id> '.*' " + "cannot have a null value.*")); +} + +TEST_F(ValidateTensor, ValidTensorConstantRank1) { + const std::string src = R"( + %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4 + %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42 %uint_42 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorConstantRank1SpecConstant) { + const std::string src = R"( + %uint_arr1_4 = OpSpecConstantComposite %uint_arr1 %uint_0_spec + %ts_uint_r1 = OpTypeTensorARM %uint %uint_0_spec %uint_arr1_4 + %cst = OpSpecConstantComposite %ts_uint_r1 %uint_0_spec %uint_0_spec %uint_0_spec %uint_0_spec +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTensorConstantRank1NotEnoughConstituents) { + const std::string src = R"( + %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4 + %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex("OpConstantComposite Constituent count does not match the " + "shape of Result Type <id> '.*' " + "along its outermost dimension, expected 4 but got 3.*")); +} + +TEST_F(ValidateTensor, InvalidTensorConstantRank1TooManyConstituents) { + const std::string src = R"( + %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4 + %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42 %uint_42 %uint_42 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex("OpConstantComposite Constituent count does not match the " + "shape of Result Type <id> '.*' " + "along its outermost dimension, expected 4 but got 5.*")); +} + +TEST_F(ValidateTensor, InvalidTensorConstantRank1WrongConstituentType) { + const std::string src = R"( + %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4 + %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %float_1 %uint_42 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex("OpConstantComposite Constituent <id> '.*' " + "type does not match the element type of the tensor " + ".*")); +} + +TEST_F(ValidateTensor, ValidTensorConstantRank4) { + const std::string src = R"( + %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2 + %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2 + %uint_arr3_2_2_2 = OpConstantComposite %uint_arr3 %uint_2 %uint_2 %uint_2 +%uint_arr4_2_2_2_2 = OpConstantComposite %uint_arr4 %uint_2 %uint_2 %uint_2 %uint_2 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_2 + %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2 + %ts_uint_r3 = OpTypeTensorARM %uint %uint_3 %uint_arr3_2_2_2 + %ts_uint_r4 = OpTypeTensorARM %uint %uint_4 %uint_arr4_2_2_2_2 + %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 + %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1 + %cst_r3 = OpConstantComposite %ts_uint_r3 %cst_r2 %cst_r2 + %cst_r4 = OpConstantComposite %ts_uint_r4 %cst_r3 %cst_r3 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorConstantRank2SpecConstantConstituent) { + const std::string src = R"( + %uint_arr1_2 = OpSpecConstantComposite %uint_arr1 %uint_0_spec + %uint_arr2_2_2 = OpSpecConstantComposite %uint_arr2 %uint_0_spec %uint_0_spec + %ts_uint_r1 = OpTypeTensorARM %uint %uint_0_spec %uint_arr1_2 + %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2 + %cst_r1 = OpSpecConstantComposite %ts_uint_r1 %uint_0_spec %uint_0_spec + %cst_r2 = OpSpecConstantComposite %ts_uint_r2 %cst_r1 %cst_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTensorConstantRank2ConstituentNotATensor) { + const std::string src = R"( + %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2 + %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_2 + %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2 + %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 + %cst_r2 = OpConstantComposite %ts_uint_r2 %uint_1 %cst_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpConstantComposite Constituent <id> " + "'.*' must be an OpTypeTensorARM.*")); +} + +TEST_F(ValidateTensor, + InvalidTensorConstantRank2ConstituentTensorWrongElementType) { + const std::string src = R"( + %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2 + %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2 + %ts_float_r1 = OpTypeTensorARM %float %uint_1 %uint_arr1_2 + %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2 + %cst_r1 = OpConstantComposite %ts_float_r1 %float_1 %float_1 + %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpConstantComposite Constituent <id> " + "'.*' must have the same Element Type " + "as Result Type <id> .*")); +} + +TEST_F(ValidateTensor, InvalidTensorConstantRank2ConstituentTensorWrongRank) { + const std::string src = R"( + %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2 + %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_2 + %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2 + %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 + %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1 + %cst = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r2 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + ContainsRegex("OpConstantComposite Constituent <id> " + "'.*' must have a Rank that is " + "1 less than the Rank of Result Type <id> " + "'.*', expected 1 but got 2.*")); +} + +TEST_F(ValidateTensor, InvalidTensorConstantRank2ConstituentTensorWrongShape) { + const std::string src = R"( + %uint_arr1_3 = OpConstantComposite %uint_arr1 %uint_3 + %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2 + %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_3 + %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2 + %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42 + %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1 +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + ContainsRegex( + "OpConstantComposite Constituent <id> " + "'.*' must have a Shape that matches " + "that of Result Type <id> '.*' along " + "all inner dimensions of Result Type, expected 2 for dimension 0 of " + "Constituent but got 3.*")); +} + +// +// Read tests +// + +TEST_F(ValidateTensor, ValidTensorReadScalar) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorReadArray) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint_arr2 %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorReadSpecConstantRank) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_spec %tensor_var_spec_rank + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTensorReadResultTypeVoid) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %void %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Result Type to be a scalar type or array of scalar type")); +} + +TEST_F(ValidateTensor, InvalidTensorReadResultTypeVector) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint_vec4 %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Result Type to be a scalar type or array of scalar type")); +} + +TEST_F(ValidateTensor, + InvalidTensorReadResultTypeDoesNotMatchTensorElementType) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %float %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Result Type to be the same as the Element Type of Tensor")); +} + +TEST_F(ValidateTensor, InvalidTensorReadTensorNotRanked) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_float %tensor_var_float_unranked + %val = OpTensorReadARM %float %tensor %uint_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Tensor to be an OpTypeTensorARM whose Rank is specified")); +} + +TEST_F(ValidateTensor, InvalidTensorReadCoordinatesNotArray) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor.")); +} + +TEST_F(ValidateTensor, InvalidTensorReadCoordinatesArrayNotInteger) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %float_arr4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor.")); +} + +TEST_F(ValidateTensor, InvalidTensorReadCoordinatesArrayIntegerWrongLength) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr2_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor.")); +} + +TEST_F(ValidateTensor, ValidTensorReadScalarWithNoneTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 NoneARM + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorReadScalarWithNontemporalTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 NontemporalARM + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorReadScalarOutOfBoundsValueTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 OutOfBoundsValueARM %uint_42 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, + InvalidTensorReadScalarOutOfBoundsValueTensorOperandsValueWrongType) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 OutOfBoundsValueARM %float_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected the type of the OutOfBoundsValueARM value to be the same " + "as the Element Type of Tensor.")); +} + +TEST_F(ValidateTensor, + InvalidTensorReadScalarMakeElementAvailableTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 MakeElementAvailableARM %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "MakeElementAvailableARM cannot be used with OpTensorReadARM.")); +} + +TEST_F(ValidateTensor, + ValidTensorReadScalarWithMakeElementVisibleTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 MakeElementVisibleARM|NonPrivateElementARM %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F( + ValidateTensor, + InvalidTensorReadScalarMakeElementVisibleTensorOperandsWithoutNonPrivateElement) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 MakeElementVisibleARM %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MakeElementAvailableARM requires NonPrivateElementARM")); +} + +// +// Write tests +// + +TEST_F(ValidateTensor, ValidTensorWriteScalar) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorWriteArray) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_arr2_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorWriteSpecConstantRank) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_spec %tensor_var_spec_rank + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTensorWriteObjectNotScalarOrArrayOfScalar) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_vec4_1_1_1_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Object to be a scalar type or array of scalar type " + "that is the same as the Element Type of Tensor.")); +} + +TEST_F(ValidateTensor, InvalidTensorWriteObjectDoesNotMatchTensorElementType) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %float_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Object to be a scalar type or array of scalar type " + "that is the same as the Element Type of Tensor.")); +} + +TEST_F(ValidateTensor, InvalidTensorWriteTensorNotRanked) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_float %tensor_var_float_unranked + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %float_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Tensor to be an OpTypeTensorARM whose Rank is specified")); +} + +TEST_F(ValidateTensor, InvalidTensorWriteCoordinatesNotArray) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor.")); +} + +TEST_F(ValidateTensor, InvalidTensorWriteCoordinatesArrayNotInteger) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %float_arr4_1_1_1_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor.")); +} + +TEST_F(ValidateTensor, InvalidTensorWriteCoordinatesArrayIntegerWrongLength) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr2_1_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Coordinates to be an array whose Element Type is an " + "integer type and whose Length is equal to the Rank of Tensor.")); +} + +TEST_F(ValidateTensor, ValidTensorWriteScalarWithNoneTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 NoneARM + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorWriteScalarWithNontemporalTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 NontemporalARM + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, + ValidTensorWriteScalarWithMakeElementAvailableTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 MakeElementAvailableARM|NonPrivateElementARM %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, + InvalidTensorWriteScalarWithOutOfVoundsValueTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 OutOfBoundsValueARM %uint_42 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OutOfBoundsValue Tensor Operand not allowed with " + "OpTensorWriteARM.")); +} + +TEST_F(ValidateTensor, + InvalidTensorWriteScalarWithMakeElementVisibleTensorOperands) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 MakeElementVisibleARM %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MakeElementVisibleARM not allowed with OpTensorWriteARM.")); +} + +TEST_F( + ValidateTensor, + InvalidTensorWriteScalarWithMakeElementAvailableTensorOperandsWithoutNonPrivateElement) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 MakeElementAvailableARM %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MakeElementAvailableARM requires NonPrivateElementARM.")); +} + +// +// Query Size tests +// + +TEST_F(ValidateTensor, ValidTensorQuerySize) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %size = OpTensorQuerySizeARM %uint %tensor %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, ValidTensorQuerySizeSpecConstant) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_spec %tensor_var_spec_rank + %size = OpTensorQuerySizeARM %uint %tensor %uint_0_spec + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV)); +} + +TEST_F(ValidateTensor, InvalidTensorQuerySizeTensorNotRanked) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_float %tensor_var_float_unranked + %size = OpTensorQuerySizeARM %uint %tensor %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Tensor to be an OpTypeTensorARM whose Rank is specified")); +} + +TEST_F(ValidateTensor, InvalidTensorQuerySizeResultNotIntegerScalarType) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_float %tensor_var_float_unranked + %size = OpTensorQuerySizeARM %float %tensor %uint_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be an integer type scalar")); +} + +TEST_F(ValidateTensor, InvalidTensorQuerySizeDimensionNotIntegerType) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %size = OpTensorQuerySizeARM %uint %tensor %float_1 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dimension must come from a constant instruction of " + "scalar integer type.")); +} + +TEST_F(ValidateTensor, InvalidTensorQuerySizeDimensionNotConstant) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %dims = OpLoad %uint_arr4 %var_uint_arr4_1_1_1_1 + %dim = OpCompositeExtract %uint %dims 0 + %size = OpTensorQuerySizeARM %uint %tensor %dim + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dimension must come from a constant instruction of " + "scalar integer type.")); +} + +TEST_F(ValidateTensor, InvalidTensorQuerySizeDimension) { + const std::string src = R"( + %fn = OpFunction %void None %fnty + %label1 = OpLabel + %tensor = OpLoad %tensor_uint_4 %tensor_var + %size = OpTensorQuerySizeARM %uint %tensor %uint_42 + OpReturn + OpFunctionEnd +)"; + std::string spvasm = GenerateModule(src); + CompileSuccessfully(spvasm, SPVENV); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Dimension (42) must be less than the Rank of Tensor (4)")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_type_unique_test.cpp b/test/val/val_type_unique_test.cpp index 00e2e7f..289e341 100644 --- a/test/val/val_type_unique_test.cpp +++ b/test/val/val_type_unique_test.cpp
@@ -1,4 +1,6 @@ // Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -288,6 +290,28 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); } +TEST_F(ValidateTypeUnique, DuplicateNodePayloadArrayType) { + std::string str = R"( +OpCapability Shader +OpCapability ShaderEnqueueAMDX +OpCapability Linkage +OpExtension "SPV_AMDX_shader_enqueue" +OpMemoryModel Logical GLSL450 +%floatt = OpTypeFloat 32 +%struct = OpTypeStruct %floatt +%npat1 = OpTypeNodePayloadArrayAMDX %struct +%npat2 = OpTypeNodePayloadArrayAMDX %struct +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a6736ca..b1fad95 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt
@@ -24,12 +24,21 @@ # LIBS lib_target1 lib_target2 # ) function(add_spvtools_tool) + if (SPIRV_TOOLS_USE_MIMALLOC) + list(APPEND SRCS mimalloc.cpp) + endif() + set(one_value_args TARGET) set(multi_value_args SRCS LIBS) cmake_parse_arguments( ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) add_executable(${ARG_TARGET} ${ARG_SRCS}) + + if (SPIRV_TOOLS_USE_MIMALLOC) + target_link_libraries(${ARG_TARGET} PRIVATE mimalloc-static) + endif() + spvtools_default_compile_options(${ARG_TARGET}) target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS}) target_include_directories(${ARG_TARGET} PRIVATE @@ -68,7 +77,7 @@ target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR} ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt - spirv-cfg spirv-link spirv-lint) + spirv-cfg spirv-link spirv-lint spirv-diff) if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Android")) add_spvtools_tool(TARGET spirv-objdump
diff --git a/tools/as/as.cpp b/tools/as/as.cpp index 8e82170..833e929 100644 --- a/tools/as/as.cpp +++ b/tools/as/as.cpp
@@ -28,8 +28,10 @@ Usage: %s [options] [<filename>] -The SPIR-V assembly text is read from <filename>. If no file is specified, -or if the filename is "-", then the assembly text is read from standard input. +The SPIR-V assembly text is read from <filename>. +Use "-" as the filename to read from standard input. +Use "./-" to read from the file named "-". + The SPIR-V binary module is written to file "out.spv", unless the -o option is used. @@ -84,11 +86,13 @@ options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS; } - if (flags::positional_arguments.size() != 1) { - fprintf(stderr, "error: exactly one input file must be specified.\n"); + if (flags::positional_arguments.size() > 1) { + fprintf(stderr, "error: one input file at most must be specified.\n"); return 1; } - std::string inFile = flags::positional_arguments[0]; + std::string inFile = flags::positional_arguments.size() == 1 + ? flags::positional_arguments[0] + : "-"; std::vector<char> contents; if (!ReadTextFile(inFile.c_str(), &contents)) return 1;
diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp index 40a7dc4..85fa1e6 100644 --- a/tools/cfg/bin_to_dot.cpp +++ b/tools/cfg/bin_to_dot.cpp
@@ -172,7 +172,6 @@ // diagnostics. These are programmer errors, not user errors. if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; const spvtools::AssemblyGrammar grammar(context); - if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; spvtools::FriendlyNameMapper friendly_mapper(context, words, num_words); DotConverter converter(friendly_mapper.GetNameMapper(), out);
diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp index d60edb2..349ec09 100644 --- a/tools/diff/diff.cpp +++ b/tools/diff/diff.cpp
@@ -87,7 +87,8 @@ return spvtools::BuildModule( kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, std::string(contents.begin(), contents.end()), - spvtools::SpirvTools::kDefaultAssembleOption | + static_cast<spv_text_to_binary_options_t>( + spvtools::SpirvTools::kDefaultAssembleOption) | SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); }
diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp index c294d03..74cd03c 100644 --- a/tools/dis/dis.cpp +++ b/tools/dis/dis.cpp
@@ -68,6 +68,13 @@ --offsets Show byte offsets for each instruction. --comment Add comments to make reading easier + + --handle-unknown-opcodes + Emit unknown opcodes and unknown extended instruction + numbers as OpUnknown with raw integer operands instead of + failing. The output can be re-assembled with spirv-as. + Note: the ID bound in the reassembled module may be + incorrect if any unknown instruction defines a result ID. )"; // clang-format off @@ -84,6 +91,7 @@ FLAG_LONG_bool (reorder_blocks, /* default_value= */ false, /* required= */ false); FLAG_LONG_bool (offsets, /* default_value= */ false, /* required= */ false); FLAG_LONG_bool (comment, /* default_value= */ false, /* required= */ false); +FLAG_LONG_bool (handle_unknown_opcodes, /* default_value= */ false, /* required= */ false); // clang-format on static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; @@ -141,6 +149,9 @@ if (flags::comment.value()) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT; + if (flags::handle_unknown_opcodes.value()) + options |= SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES; + if (flags::o.value() == "-") { // Print to standard output. options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 2f47dd4..9a512d4 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp
@@ -72,6 +72,15 @@ be invalid. --verify-ids Verify that IDs in the resulting modules are truly unique. + --fnvar-targets + CSV file specifying OpSpecConditionalTarget parameters for each + linked module. + --fnvar-architectures + CSV file specifying OpSpecConditionalArchitecture parameters for + each linked module. + --fnvar-capabilities + For each function variant, include its required capabilities + under OpSpecConditionalCapabilitiesINTEL. --version Display linker version information. )", @@ -90,6 +99,9 @@ FLAG_LONG_bool( allow_pointer_mismatch, /* default_value= */ false, /* required= */ false); FLAG_SHORT_string(o, /* default_value= */ "", /* required= */ false); FLAG_LONG_string( target_env, /* default_value= */ kDefaultEnvironment, /* required= */ false); +FLAG_LONG_string( fnvar_targets, /* default_value= */ "", /* required= */ false); +FLAG_LONG_string( fnvar_architectures, /* default_value= */ "", /* required= */ false); +FLAG_LONG_bool( fnvar_capabilities, /* default_value= */ false, /* required= */ false); FLAG_LONG_bool( use_highest_version, /* default_value= */ false, /* required= */ false); // clang-format on @@ -140,6 +152,33 @@ fprintf(stderr, "error: No input file specified\n"); return 1; } + options.SetInFiles(inFiles); + + if (!flags::fnvar_targets.value().empty()) { + std::vector<char> fn_variants_csv_chars; + const bool ret = ReadTextFile(flags::fnvar_targets.value().c_str(), + &fn_variants_csv_chars); + if (!ret) { + return 1; + } + const std::string csv_text(fn_variants_csv_chars.begin(), + fn_variants_csv_chars.end()); + options.SetFnVarTargetsCsv(csv_text); + } + + if (!flags::fnvar_architectures.value().empty()) { + std::vector<char> fn_variants_csv_chars; + const bool ret = ReadTextFile(flags::fnvar_architectures.value().c_str(), + &fn_variants_csv_chars); + if (!ret) { + return 1; + } + const std::string csv_text(fn_variants_csv_chars.begin(), + fn_variants_csv_chars.end()); + options.SetFnVarArchitecturesCsv(csv_text); + } + + options.SetHasFnVarCapabilities(flags::fnvar_capabilities.value()); std::vector<std::vector<uint32_t>> contents(inFiles.size()); for (size_t i = 0u; i < inFiles.size(); ++i) {
diff --git a/tools/objdump/extract_source.cpp b/tools/objdump/extract_source.cpp index 0295952..d6d49d9 100644 --- a/tools/objdump/extract_source.cpp +++ b/tools/objdump/extract_source.cpp
@@ -19,9 +19,9 @@ #include <unordered_map> #include <vector> +#include "source/latest_version_spirv_header.h" #include "source/opt/log.h" #include "spirv-tools/libspirv.hpp" -#include "spirv/unified1/spirv.hpp" #include "tools/util/cli_consumer.h" namespace { @@ -66,7 +66,7 @@ const spv_parsed_instruction_t& instruction, std::string* output) { assert(output != nullptr); - assert(instruction.opcode == spv::Op::OpString); + assert(instruction.opcode == static_cast<unsigned>(spv::Op::OpString)); if (instruction.num_operands != 2) { spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc, "Missing operands for OpString."); @@ -85,7 +85,8 @@ const spv_position_t& loc, const spv_parsed_instruction_t& instruction, std::string* output) { assert(output != nullptr); - assert(instruction.opcode == spv::Op::OpSourceContinued); + assert(instruction.opcode == + static_cast<unsigned>(spv::Op::OpSourceContinued)); if (instruction.num_operands != 1) { spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc, "Missing operands for OpSourceContinued."); @@ -104,7 +105,7 @@ const spv_parsed_instruction_t& instruction, spv::Id* filename, std::string* code) { assert(filename != nullptr && code != nullptr); - assert(instruction.opcode == spv::Op::OpSource); + assert(instruction.opcode == static_cast<unsigned>(spv::Op::OpSource)); // OpCode [ Source Language | Version | File (optional) | Source (optional) ] if (instruction.num_words < 3) { spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc, @@ -146,7 +147,7 @@ std::unordered_map<uint32_t, std::string> stringMap; std::vector<std::pair<spv::Id, std::string>> sources; - spv::Op lastOpcode = spv::Op::OpMax; + spv::Op lastOpcode = spv::Op::Max; size_t instructionIndex = 0; spvtools::InstructionParser instructionParser = @@ -155,20 +156,22 @@ const spv_position_t loc = {0, 0, instructionIndex + 1}; spv_result_t result = SPV_SUCCESS; - if (instruction.opcode == spv::Op::OpString) { + if (instruction.opcode == static_cast<unsigned>(spv::Op::OpString)) { std::string content; result = extractOpString(loc, instruction, &content); if (result == SPV_SUCCESS) { stringMap.emplace(instruction.result_id, std::move(content)); } - } else if (instruction.opcode == spv::Op::OpSource) { + } else if (instruction.opcode == + static_cast<unsigned>(spv::Op::OpSource)) { spv::Id filenameId; std::string code; result = extractOpSource(loc, instruction, &filenameId, &code); if (result == SPV_SUCCESS) { sources.emplace_back(std::make_pair(filenameId, std::move(code))); } - } else if (instruction.opcode == spv::Op::OpSourceContinued) { + } else if (instruction.opcode == + static_cast<unsigned>(spv::Op::OpSourceContinued)) { if (lastOpcode != spv::Op::OpSource) { spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc, "OpSourceContinued MUST follow an OpSource.");
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 07a5725..19852e6 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp
@@ -279,6 +279,10 @@ option --relax-logical-pointer to the validator.)", GetLegalizationPasses().c_str()); printf(R"( + --legalize-multidim-array + Replace multidimensional arrays of resources with single-dimensional + arrays. Run combine-access-chains before this pass.)"); + printf(R"( --local-redundancy-elimination Looks for instructions in the same basic block that compute the same value, and deletes the redundant ones.)"); @@ -446,6 +450,15 @@ Forwards this option to the validator. See the validator help for details.)"); printf(R"( + --canonicalize-ids + Canonicalize IDs to improve compression of SPIR-V binary files. The resulting + modules have an increased ID range (IDs are not as tightly packed + around zero), but will compress better when multiple modules are + compressed together, since the compressor's dictionary can find better + cross module commonality. This pass should be run after most optimization + passes except for --strip-debug because this pass will use OpName to + canonicalize IDs. i.e. Run --strip-debug after this pass.)"); + printf(R"( --relax-struct-store Forwards this option to the validator. See the validator help for details.)"); @@ -455,9 +468,9 @@ instructions.)"); printf(R"( --remove-unused-interface-variables - Removes variables referenced on the |OpEntryPoint| instruction - that are not referenced in the entry point function or any function - in its call tree. Note that this could cause the shader interface + Removes variables referenced on the |OpEntryPoint| instruction + that are not referenced in the entry point function or any function + in its call tree. Note that this could cause the shader interface to no longer match other shader stages.)"); printf(R"( --replace-invalid-opcode @@ -465,6 +478,12 @@ but not for the current shader stage. To have an effect, all entry points must have the same execution model.)"); printf(R"( + --resolve-binding-conflicts + Renumber bindings to avoid conflicts. + When an image and sampler share the same desriptor set and binding, + increment the binding number of the sampler. Recursively ripple + to higher-numbered bindings until all conflicts resolved resolved.)"); + printf(R"( --ssa-rewrite Replace loads and stores to function local variables with operations on SSA IDs.)"); @@ -501,6 +520,11 @@ is invalid, the optimizer may fail or generate incorrect code. This options should be used rarely, and with caution.)"); printf(R"( + --split-combined-image-sampler + Replace combined image sampler variables and parameters into + pairs of images and samplers. New variables have the same + bindings as the original variable.)"); + printf(R"( --strength-reduction Replaces instructions with equivalent and less expensive ones.)"); printf(R"( @@ -728,7 +752,7 @@ spvtools::ValidatorOptions* validator_options, spvtools::OptimizerOptions* optimizer_options) { std::vector<std::string> pass_flags; - bool preserve_interface = true; + bool preserve_interface = false; for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; if ('-' == cur_arg[0]) {
diff --git a/tools/sva/.eslintrc.json b/tools/sva/.eslintrc.json deleted file mode 100644 index 2f07726..0000000 --- a/tools/sva/.eslintrc.json +++ /dev/null
@@ -1,25 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "node": true, - "mocha": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "rules": { - "block-scoped-var": "error", - "consistent-return": "error", - "eqeqeq": ["error", "always"], - "indent": [ "error", 2 ], - "linebreak-style": [ "error", "unix" ], - "no-eval": "error", - "no-shadow": "error", - "no-shadow-restricted-names": "error", - "quotes": [ "error", "double" ], - "semi": [ "error", "always" ] - } -}
diff --git a/tools/sva/.gitignore b/tools/sva/.gitignore deleted file mode 100644 index 88e64c3..0000000 --- a/tools/sva/.gitignore +++ /dev/null
@@ -1,6 +0,0 @@ -.DS_Store -node_modules -third_party/spirv-headers -o.sva -build -yarn-error.log
diff --git a/tools/sva/README.md b/tools/sva/README.md deleted file mode 100644 index cd3d13c..0000000 --- a/tools/sva/README.md +++ /dev/null
@@ -1,41 +0,0 @@ -# SVA - -SPIR-V Assembler for WebGPU. The SPIR-V Assembler is a JavaScript library to -convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a -SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has -the following limitations. - - * Only 32 bit integers and floats supported - * Only GLSL accepted as an extended instruction set - * Doesn't support ! syntax for integers - * Doesn't support hex encoding for float - -```shell -yarn install -yarn test -``` - -You can also use `yarn watch` to watch all of the files and re-run tests as -needed. - -## Webserver -Using `yarn serve` will start a webserver on localhost:5000. If you load the -`tests/index.html` file this will load the SVA files into browser. - -## Command Line -There is a simple assembler binary with can be executed from the command line. - -```shell -yarn sva tests/simple.spv_asm -``` - -The above will generate a `o.sva` file in the current directory. - -## Update spirv.data.json - -If there is a new spirv-headers release update the externals folder checkout -and then: - -```shell -./tools/process_grammar.rb > src/spirv.data.json -```
diff --git a/tools/sva/bin/sva.js b/tools/sva/bin/sva.js deleted file mode 100755 index e2448d6..0000000 --- a/tools/sva/bin/sva.js +++ /dev/null
@@ -1,32 +0,0 @@ -#!/usr/bin/env node -// -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -"use strict"; - -const fs = require("fs"); - -import SVA from "../src/sva.js"; - -let input = fs.readFileSync(process.argv[2], "utf-8"); -let u = SVA.assemble(input); - -if (typeof u === "string") { - console.log(u); -} else { - fs.writeFileSync("o.sva", new Buffer(u.buffer), (err) => { - console.log(["ERROR", err]); - }); -}
diff --git a/tools/sva/mocha.opts b/tools/sva/mocha.opts deleted file mode 100644 index 4a52320..0000000 --- a/tools/sva/mocha.opts +++ /dev/null
@@ -1 +0,0 @@ ---recursive
diff --git a/tools/sva/package.json b/tools/sva/package.json deleted file mode 100644 index cd6ea67..0000000 --- a/tools/sva/package.json +++ /dev/null
@@ -1,25 +0,0 @@ -{ - "name": "sva", - "version": "0.1.0", - "description": "SPIR-V Assembler", - "main": "index.js", - "author": "dan sinclair <dsinclair@google.com>", - "license": "Apache-2.0", - "private": true, - "scripts": { - "sva": "node -r esm bin/sva.js", - "lint": "eslint --fix --ext .js .", - "test": "mocha --require esm src/**/*_test.js", - "watch": "mocha --require esm --watch --watch-extension js \"src/**/*_test.js\"", - "serve": "serve", - "bundle": "rollup -c" - }, - "devDependencies": { - "chai": "^4.3.7", - "eslint": "^8.41.0", - "esm": "^3.2.25", - "mocha": "^10.2.0", - "rollup": "^3.29.5", - "serve": "^14.2.0" - } -}
diff --git a/tools/sva/rollup.config.js b/tools/sva/rollup.config.js deleted file mode 100644 index 2056e16..0000000 --- a/tools/sva/rollup.config.js +++ /dev/null
@@ -1,7 +0,0 @@ -export default { - input: 'src/sva.js', - output: { - file: 'build/sva.js', - format: 'esm', - } -}
diff --git a/tools/sva/src/assembler.js b/tools/sva/src/assembler.js deleted file mode 100644 index 7bc208e..0000000 --- a/tools/sva/src/assembler.js +++ /dev/null
@@ -1,98 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -export default class Assembler { - static get GENERATOR_ID() { return 0; } - - /** - * @param {AST} the AST to build the SPIR-V from - */ - constructor(ast) { - this.ast_ = ast; - } - - /** - * Assembles the AST into binary SPIR-V. - * @return {Uint32Array} The SPIR-V binary data. - */ - assemble() { - let total_size = 5; - for (const inst of this.ast_.instructions()) { - total_size += 1; - for (const op of inst.operands()) { - total_size += op.length(); - } - } - - let u = new Uint32Array(total_size); - u[0] = 0x07230203; // Magic - u[1] = 0x00010500; // Version 1.5 - u[2] = Assembler.GENERATOR_ID; // Generator magic number - u[3] = this.ast_.getIdBounds(); // ID bounds - u[4] = 0; // Reserved - - let idx = 5; - for (const inst of this.ast_.instructions()) { - let op_size = 1; - for (const op of inst.operands()) { - op_size += op.length(); - } - - u[idx++] = op_size << 16 | inst.opcode(); - for (const op of inst.operands()) { - idx = this.processOp(u, idx, op); - } - } - - return u; - } - - processOp(u, idx, op) { - if (op.type() === "string") { - let len = 0; - let v = 0; - for (const ch of op.value()) { - v = v | (ch.charCodeAt(0) << (len * 8)); - len += 1; - - if (len === 4) { - u[idx++] = v; - len = 0; - v = 0; - } - } - // Make sure either the terminating 0 byte is written or the last - // partial word is written. - u[idx++] = v; - - } else if (op.type() === "float") { - // TODO(dsinclair): Handle 64 bit floats ... - let b = new ArrayBuffer(4); - let f = new Float32Array(b); - f[0] = op.value(); - - let u2 = new Uint32Array(b); - - u[idx++] = u2[0]; - } else { - u[idx++] = op.value(); - } - - for (const param of op.params()) { - idx = this.processOp(u, idx, param); - } - - return idx; - } -}
diff --git a/tools/sva/src/assembler_test.js b/tools/sva/src/assembler_test.js deleted file mode 100644 index a23d211..0000000 --- a/tools/sva/src/assembler_test.js +++ /dev/null
@@ -1,165 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { assert } from "chai"; -import Lexer from "./lexer"; -import Parser from "./parser"; -import grammar from "./spirv.data.js"; -import Assembler from "./assembler"; - -describe("assembler", () => { - it("generates SPIR-V magic number", () => { - let input = `; SPIR-V -; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 7 -; Bound: 6 -; Schema: 0 - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 440 - OpName %main "main" - %void = OpTypeVoid - %3 = OpTypeFunction %void - %main = OpFunction %void None %3 - %5 = OpLabel - OpReturn - OpFunctionEnd`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - assert.equal(res[0], 0x07230203); - }); - - it("assembles enumerant params", () => { - let input = "OpExecutionMode %main LocalSize 2 3 4"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - - assert.lengthOf(res, 11); - assert.equal(res[5], (6 /* word count */ << 16) | 16 /* opcode */); - assert.equal(res[6], 1 /* %main */); - assert.equal(res[7], 17 /* LocalSize */); - assert.equal(res[8], 2); - assert.equal(res[9], 3); - assert.equal(res[10], 4); - }); - - it("assembles float 32 values", () => { - let input = `%float = OpTypeFloat 32 - %float1 = OpConstant %float 0.400000006`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - - assert.lengthOf(res, 12); - assert.equal(res[8], (4 /* word count */ << 16) | 43 /* opcode */); - assert.equal(res[9], 1 /* %float */); - assert.equal(res[10], 2 /* %float */); - assert.equal(res[11], 0x3ecccccd /* 0.400000006 */); - }); - - describe("strings", () => { - it("assembles 'abcd'", () => { - let input = `OpName %mains "abcd"`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - - assert.lengthOf(res, 9); - assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); - assert.equal(res[6], 1 /* %mains */); - assert.equal(res[7], 0x64636261 /* food */); - assert.equal(res[8], 0x00000000 /* null byte */); - }); - - it("assembles 'abcde'", () => { - let input = `OpName %mains "abcde"`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - - assert.lengthOf(res, 9); - assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); - assert.equal(res[6], 1 /* %mains */); - assert.equal(res[7], 0x64636261 /* abcd */); - assert.equal(res[8], 0x00000065 /* e */); - }); - - it("assembles 'abcdef'", () => { - let input = `OpName %mains "abcdef"`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - - assert.lengthOf(res, 9); - assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); - assert.equal(res[6], 1 /* %mains */); - assert.equal(res[7], 0x64636261 /* abcd */); - assert.equal(res[8], 0x00006665 /* ef */); - }); - - it("assembles 'abcdefg'", () => { - let input = `OpName %mains "abcdefg"`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - - let a = new Assembler(ast); - let res = a.assemble(); - - assert.lengthOf(res, 9); - assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); - assert.equal(res[6], 1 /* %mains */); - assert.equal(res[7], 0x64636261 /* abcd */); - assert.equal(res[8], 0x00676665 /* efg */); - }); - }); -});
diff --git a/tools/sva/src/ast.js b/tools/sva/src/ast.js deleted file mode 100644 index d396d2f..0000000 --- a/tools/sva/src/ast.js +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -class Module { - constructor() { - this.instructions_ = []; - this.next_id_ = 1; - - /** - * Maps {string, hash} where the string is the type name and the hash is: - * type- 'float' or 'int' - * width- number of bits needed to store number - * signed- the sign of the number - */ - this.types_ = {}; - - /** - * Maps {string, number} where the string is the type name and the number is - * the id value. - */ - this.assigned_ids_ = {}; - } - - instructions() { return this.instructions_; } - - instruction(val) { return this.instructions_[val]; } - - addInstruction(inst) { - this.instructions_.push(inst); - - // Record type information - if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") { - let is_int = inst.name() === "OpTypeInt"; - - this.types_[inst.operand(0).name()] = { - type: is_int ? "int" : "float", - width: inst.operand(1).value(), - signed: is_int ? inst.operand(2).value() : 1 - }; - } - - // Record operand result id's - inst.operands().forEach((op) => { - if (op.rawValue() !== undefined && op.type() === "result_id") { - this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1); - } - }); - } - - getType(name) { return this.types_[name]; } - - getId(name) { - if (this.assigned_ids_[name] !== undefined) { - return this.assigned_ids_[name]; - } - - let next = this.next_id_; - this.assigned_ids_[name] = next; - - this.next_id_ += 1; - return next; - } - - getIdBounds() { return this.next_id_; } -} - -class Instruction { - constructor(name, opcode, operands) { - this.name_ = name; - this.opcode_ = opcode; - this.operands_ = operands; - } - - name() { return this.name_; } - - opcode() { return this.opcode_; } - - operands() { return this.operands_; } - - operand(val) { return this.operands_[val]; } -} - -class Operand { - constructor(mod, name, type, value, params) { - this.module_ = mod; - this.name_ = name; - this.type_ = type; - this.value_ = value; - this.params_ = params; - } - - name() { return this.name_; } - - length() { - // Get the value just to force it to be filled. - this.value(); - - if (this.type_ === "string") { - return Math.ceil((this.value_.length + 1) / 4); - } - - let size = 1; - for (const param of this.params_) { - size += param.length(); - } - return size; - } - - type() { return this.type_; } - - rawValue() { return this.value_; } - - // This method should only be called on ResultId's after the full parse is - // complete. This is because the AST will only have the maximum seen numeric - // ResultId when the parse is done. - value() { - if (this.value_ === undefined) { - this.value_ = this.module_.getId(this.name_); - } - return this.value_; - } - - params() { return this.params_; } -} - -export { - Module, - Instruction, - Operand -};
diff --git a/tools/sva/src/lexer.js b/tools/sva/src/lexer.js deleted file mode 100644 index b39f93a..0000000 --- a/tools/sva/src/lexer.js +++ /dev/null
@@ -1,363 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Token, TokenType } from "./token.js"; - -export default class Lexer { - /** - * @param {String} input The input string to tokenize. - */ - constructor(input) { - this.input_ = input; - this.len_ = input.length; - this.cur_pos_ = 0; - this.cur_line_ = 1; - - this.num_regex_ = /^[0-9]+$/; - this.alpha_regex_ = /^[a-zA-Z_]+$/; - this.op_regex_ = /^Op[A-Z][^\s]*$/; - this.hex_regex_ = /^[0-9a-fA-F]$/; - } - - /** - * Parses the next token from the input stream. - * @return {Token} the next token. - */ - next() { - this.skipWhitespace(); - this.skipComments(); - - if (this.cur_pos_ >= this.len_) - return new Token(TokenType.kEOF, this.cur_line_); - - let n = this.tryHexInteger(); - if (n !== undefined) - return n; - - n = this.tryFloat(); - if (n !== undefined) - return n; - - n = this.tryInteger(); - if (n !== undefined) - return n; - - n = this.tryString(); - if (n !== undefined) - return n; - - n = this.tryOp(); - if (n !== undefined) - return n; - - n = this.tryPunctuation(); - if (n !== undefined) - return n; - - n = this.tryResultId(); - if (n !== undefined) - return n; - - n = this.tryIdent(); - if (n !== undefined) - return n; - - return new Token(TokenType.kError, this.cur_line_, "Failed to match token"); - } - - is(str) { - if (this.len_ <= this.cur_pos_ + (str.length - 1)) - return false; - - for (let i = 0; i < str.length; ++i) { - if (this.input_[this.cur_pos_ + i] !== str[i]) - return false; - } - - return true; - } - - isNum(ch) { - return ch.match(this.num_regex_); - } - - isAlpha(ch) { - return ch.match(this.alpha_regex_); - } - - isAlphaNum(ch) { - return this.isNum(ch) || this.isAlpha(ch); - } - - isHex(char) { - return char.match(this.hex_regex_); - } - - isCurWhitespace() { - return this.is(" ") || this.is("\t") || this.is("\r") || this.is("\n"); - } - - skipWhitespace() { - for(;;) { - let cur_pos = this.cur_pos_; - while (this.cur_pos_ < this.len_ && - this.isCurWhitespace()) { - if (this.is("\n")) - this.cur_line_ += 1; - - this.cur_pos_ += 1; - } - - this.skipComments(); - - // Cursor didn't move so no whitespace matched. - if (cur_pos === this.cur_pos_) - break; - } - } - - skipComments() { - if (!this.is(";")) - return; - - while (this.cur_pos_ < this.len_ && !this.is("\n")) - this.cur_pos_ += 1; - } - - /** - * Attempt to parse the next part of the input as a float. - * @return {Token|undefined} returns a Token if a float is matched, - * undefined otherwise. - */ - tryFloat() { - let start = this.cur_pos_; - let end = start; - - if (this.cur_pos_ >= this.len_) - return undefined; - if (this.input_[end] === "-") - end += 1; - - while (end < this.len_ && this.isNum(this.input_[end])) - end += 1; - - // Must have a "." in a float - if (end >= this.len_ || this.input_[end] !== ".") - return undefined; - - end += 1; - while (end < this.len_ && this.isNum(this.input_[end])) - end += 1; - - let substr = this.input_.substr(start, end - start); - if (substr === "." || substr === "-.") - return undefined; - - this.cur_pos_ = end; - - return new Token(TokenType.kFloatLiteral, this.cur_line_, parseFloat(substr)); - } - - /** - * Attempt to parse a hex encoded integer. - * @return {Token|undefined} returns a Token if a Hex number is matched, - * undefined otherwise. - */ - tryHexInteger() { - let start = this.cur_pos_; - let end = start; - - if (this.cur_pos_ >= this.len_) - return undefined; - if (end + 2 >= this.len_ || this.input_[end] !== "0" || - this.input_[end + 1] !== "x") { - return undefined; - } - - end += 2; - - while (end < this.len_ && this.isHex(this.input_[end])) - end += 1; - - this.cur_pos_ = end; - - let val = parseInt(this.input_.substr(start, end - start), 16); - return new Token(TokenType.kIntegerLiteral, this.cur_line_, val); - } - - /** - * Attempt to parse an encoded integer. - * @return {Token|undefined} returns a Token if a number is matched, - * undefined otherwise. - */ - tryInteger() { - let start = this.cur_pos_; - let end = start; - - if (this.cur_pos_ >= this.len_) - return undefined; - if (this.input_[end] === "-") - end += 1; - - if (end >= this.len_ || !this.isNum(this.input_[end])) - return undefined; - - while (end < this.len_ && this.isNum(this.input_[end])) - end += 1; - - this.cur_pos_ = end; - - let val = parseInt(this.input_.substr(start, end - start), 10); - return new Token(TokenType.kIntegerLiteral, this.cur_line_, val); - } - - /** - * Attempt to parse a result id. - * @return {Token|undefined} returns a Token if a result id is matched, - * undefined otherwise. - */ - tryResultId() { - let start = this.cur_pos_; - if (start >= this.len_) - return undefined; - if (!this.is("%")) - return undefined; - - start += 1; - this.cur_pos_ += 1; - while (this.cur_pos_ < this.len_ && - (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) { - this.cur_pos_ += 1; - } - - let ident = this.input_.substr(start, this.cur_pos_ - start); - let value = undefined; - if (ident.match(this.num_regex_)) - value = parseInt(ident, 10); - - return new Token(TokenType.kResultId, this.cur_line_, { - name: ident, - val: value - }); - } - - /** - * Attempt to parse an identifier. - * @return {Token|undefined} returns a Token if an identifier is matched, - * undefined otherwise. - */ - tryIdent() { - let start = this.cur_pos_; - if (start >= this.len_) - return undefined; - - while (this.cur_pos_ < this.len_ && - (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) { - this.cur_pos_ += 1; - } - - let ident = this.input_.substr(start, this.cur_pos_ - start); - return new Token(TokenType.kIdentifier, this.cur_line_, ident); - } - - /** - * Attempt to parse an Op command. - * @return {Token|undefined} returns a Token if an Op command is matched, - * undefined otherwise. - */ - tryOp() { - let start = this.cur_pos_; - if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_)) - return undefined; - - if (this.input_[this.cur_pos_] !== "O" || - this.input_[this.cur_pos_ + 1] !== "p") { - return undefined; - } - - while (this.cur_pos_ < this.len_ && - !this.isCurWhitespace()) { - this.cur_pos_ += 1; - } - - return new Token(TokenType.kOp, this.cur_line_, { - name: this.input_.substr(start, this.cur_pos_ - start) - }); - } - - /** - * Attempts to match punctuation strings against the input - * @return {Token|undefined} Returns the Token for the punctuation or - * undefined if no matches found. - */ - tryPunctuation() { - let type = undefined; - if (this.is("=")) - type = TokenType.kEqual; - else if (this.is("|")) - type = TokenType.kPipe; - - if (type === undefined) - return undefined; - - this.cur_pos_ += type.length; - return new Token(type, this.cur_line_, type); - } - - /** - * Attempts to match strings against the input - * @return {Token|undefined} Returns the Token for the string or undefined - * if no match found. - */ - tryString() { - let start = this.cur_pos_; - - // Must have at least 2 chars for a string. - if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_)) - return undefined; - if (!this.is("\"")) - return undefined; - - this.cur_pos_ += 1; - let str = ""; - while (this.cur_pos_ <= this.len_) { - if (this.is("\"")) - break; - - if (this.is("\\")) { - this.cur_pos_ += 1; - if (this.cur_pos_ >= this.len_) - return undefined; - - if (this.is("\\")) { - str += "\\"; - } else if (this.is("\"")) { - str += '"'; - } else { - str += this.input_[this.cur_pos_]; - } - } else { - str += this.input_[this.cur_pos_]; - } - this.cur_pos_ += 1; - } - - if (this.cur_pos_ >= this.len_) - return undefined; - - this.cur_pos_ += 1; - - return new Token(TokenType.kStringLiteral, this.cur_line_, str); - } -}
diff --git a/tools/sva/src/lexer_test.js b/tools/sva/src/lexer_test.js deleted file mode 100644 index 32b24c7..0000000 --- a/tools/sva/src/lexer_test.js +++ /dev/null
@@ -1,191 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { assert } from "chai"; -import Lexer from "./lexer"; -import { TokenType } from "./token"; - -describe("lexer", () => { - describe("skipped content", () => { - it("skips whitespace", () => { - let input = " \t\r\n\t \tOpKill\t\n\t \r "; - let l = new Lexer(input); - - let t = l.next(); - assert.equal(t.type, TokenType.kOp); - assert.equal(t.line, 2); - assert.equal(t.data.name, "OpKill"); - - t = l.next(); - assert.equal(t.type, TokenType.kEOF); - assert.equal(t.line, 3); - }); - - it("skips ; comments", () => { - let input = `; start with comment -OpKill ; end of line comment -; another comment -%1`; - - let l = new Lexer(input); - let t = l.next(); - assert.equal(t.type, TokenType.kOp); - assert.equal(t.data.name, "OpKill"); - assert.equal(t.line, 2); - - t = l.next(); - assert.equal(t.type, TokenType.kResultId); - assert.equal(t.data.name, "1"); - assert.equal(t.data.val, 1); - assert.equal(t.line, 4); - }); - }); - - describe("numerics", () => { - it("parses floats", () => { - let input = ["0.0", "0.", ".0", "5.7", "5.", ".7", "-0.0", "-.0", - "-0.", "-5.7", "-5.", "-.7"]; - - let results = [0.0, 0.0, 0.0, 5.7, 5.0, 0.7, 0.0, 0.0, 0.0, -5.7, -5.0, - -0.7]; - input.forEach((val, idx) => { - let l = new Lexer(val); - let t = l.next(); - - assert.equal(t.type, TokenType.kFloatLiteral, - `expected ${val} to be a float got ${t.type}`); - assert.equal(t.data, results[idx], - `expected ${results[idx]} === ${t.data}`); - - t = l.next(); - assert.equal(t.type, TokenType.kEOF); - assert.equal(t.data, undefined); - }); - }); - - it("handles invalid floats", () => { - let input = [".", "-."]; - input.forEach((val) => { - let l = new Lexer(val); - let t = l.next(); - - assert.notEqual(t.type, TokenType.kFloatLiteral, - `expect ${val} to not match type float`); - }); - }); - - it("parses integers", () => { - let input = ["0", "-0", "123", "-123", "2147483647", "-2147483648", - "4294967295", "0x00", "0x24"]; - let results = [0, 0, 123, -123,2147483647, -2147483648, 4294967295, - 0x0, 0x24]; - - input.forEach((val, idx) => { - let l = new Lexer(val); - let t = l.next(); - - assert.equal(t.type, TokenType.kIntegerLiteral, - `expected ${val} to be an integer got ${t.type}`); - assert.equal(t.data, results[idx], - `expected ${results[idx]} === ${t.data}`); - - t = l.next(); - assert.equal(t.type, TokenType.kEOF); - assert.equal(t.data, undefined); - }); - }); - }); - - it("matches result_ids", () => { - let input = `%123 -%001 -%main -%_a_b_c`; - - let result = [ - {name: "123", val: 123}, - {name: "001", val: 1}, - {name: "main", val: undefined}, - {name: "_a_b_c", val: undefined} - ]; - - let l = new Lexer(input); - for (let i = 0; i < result.length; ++i) { - let t = l.next(); - assert.equal(t.type, TokenType.kResultId); - assert.equal(t.data.name, result[i].name); - assert.equal(t.data.val, result[i].val); - } - }); - - it("matches punctuation", () => { - let input = "="; - let results = [TokenType.kEqual]; - - let l = new Lexer(input); - for (let i = 0; i < results.length; ++i) { - let t = l.next(); - assert.equal(t.type, results[i]); - assert.equal(t.line, i + 1); - } - - let t = l.next(); - assert.equal(t.type, TokenType.kEOF); - }); - - describe("strings", () => { - it("matches strings", () => { - let input = "\"GLSL.std.450\""; - - let l = new Lexer(input); - let t = l.next(); - assert.equal(t.type, TokenType.kStringLiteral); - assert.equal(t.data, "GLSL.std.450"); - }); - - it("handles unfinished strings", () => { - let input = "\"GLSL.std.450"; - - let l = new Lexer(input); - let t = l.next(); - assert.equal(t.type, TokenType.kError); - }); - - it("handles escapes", () => { - let input = `"embedded\\"quote" -"embedded\\\\slash" -"embedded\\nchar"`; - let results = [`embedded\"quote`, `embedded\\slash`, `embeddednchar`]; - - let l = new Lexer(input); - for (let i = 0; i < results.length; ++i) { - let t = l.next(); - assert.equal(t.type, TokenType.kStringLiteral, results[i]); - assert.equal(t.data, results[i]); - } - }); - }); - - it("matches keywords", () => { - let input = "GLSL Function"; - let results = ["GLSL", "Function"]; - - let l = new Lexer(input); - for (let i = 0; i < results.length; ++i) { - let t = l.next(); - assert.equal(t.type, TokenType.kIdentifier, results[i]); - assert.equal(t.data, results[i]); - } - }); -});
diff --git a/tools/sva/src/parser.js b/tools/sva/src/parser.js deleted file mode 100644 index ccf872a..0000000 --- a/tools/sva/src/parser.js +++ /dev/null
@@ -1,283 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { TokenType } from "./token.js"; -import * as AST from "./ast.js"; - -export default class Parser { - /** - * @param {Hash} The SPIR-V grammar - * @param {Lexer} The lexer - * @return {AST} Attempts to build an AST from the tokens returned by the - * given lexer - */ - constructor(grammar, lexer) { - this.grammar_ = grammar; - this.lexer_ = lexer; - - this.peek_ = []; - this.error_ = ""; - } - - get error() { return this.error_; } - - next() { - return this.peek_.shift() || this.lexer_.next(); - } - - peek(idx) { - while (this.peek_.length <= idx) { - this.peek_.push(this.lexer_.next()); - } - return this.peek_[idx]; - } - - /** - * Executes the parser. - * - * @return {AST|undefined} returns a parsed AST on success or undefined - * on error. The error message can be retrieved by - * calling error(). - */ - parse() { - let ast = new AST.Module(); - for(;;) { - let token = this.next(); - if (token === TokenType.kError) { - this.error_ = token.line() + ": " + token.data(); - return undefined; - } - if (token.type === TokenType.kEOF) - break; - - let result_id = undefined; - if (token.type === TokenType.kResultId) { - result_id = token; - - token = this.next(); - if (token.type !== TokenType.kEqual) { - this.error_ = token.line + ": expected = after result id"; - return undefined; - } - - token = this.next(); - } - - if (token.type !== TokenType.kOp) { - this.error_ = token.line + ": expected Op got " + token.type; - return undefined; - } - - let name = token.data.name; - let data = this.getInstructionData(name); - let operands = []; - let result_type = undefined; - - for (let operand of data.operands) { - if (operand.kind === "IdResult") { - if (result_id === undefined) { - this.error_ = token.line + ": expected result id"; - return undefined; - } - let o = new AST.Operand(ast, result_id.data.name, "result_id", - result_id.data.val, []); - if (o === undefined) { - return undefined; - } - operands.push(o); - } else { - if (operand.quantifier === "?") { - if (this.nextIsNewInstr()) { - break; - } - } else if (operand.quantifier === "*") { - while (!this.nextIsNewInstr()) { - let o = this.extractOperand(ast, result_type, operand); - if (o === undefined) { - return undefined; - } - operands.push(o); - } - break; - } - - let o = this.extractOperand(ast, result_type, operand); - if (o === undefined) { - return undefined; - } - - // Store the result type away so we can use it for context dependent - // numbers if needed. - if (operand.kind === "IdResultType") { - result_type = ast.getType(o.name()); - } - - operands.push(o); - } - } - - // Verify only GLSL extended instructions are used - if (name === "OpExtInstImport" && operands[1].value() !== "GLSL.std.450") { - this.error_ = token.line + ": Only GLSL.std.450 external instructions supported"; - return undefined; - } - - let inst = new AST.Instruction(name, data.opcode, operands); - - ast.addInstruction(inst); - } - return ast; - } - - getInstructionData(name) { - return this.grammar_["instructions"][name]; - } - - nextIsNewInstr() { - let n0 = this.peek(0); - if (n0.type === TokenType.kOp || n0.type === TokenType.kEOF) { - return true; - } - - let n1 = this.peek(1); - if (n1.type === TokenType.kEOF) { - return false; - } - if (n0.type === TokenType.kResultId && n1.type === TokenType.kEqual) - return true; - - return false; - } - - extractOperand(ast, result_type, data) { - let t = this.next(); - - let name = undefined; - let kind = undefined; - let value = undefined; - let params = []; - - // TODO(dsinclair): There are a bunch of missing types here. See - // https://github.com/KhronosGroup/SPIRV-Tools/blob/master/source/text.cpp#L210 - // - // LiteralSpecConstantOpInteger - // PairLiteralIntegerIdRef - // PairIdRefLiteralInteger - // PairIdRefIdRef - if (data.kind === "IdResult" || data.kind === "IdRef" - || data.kind === "IdResultType" || data.kind === "IdScope" - || data.kind === "IdMemorySemantics") { - if (t.type !== TokenType.kResultId) { - this.error_ = t.line + ": expected result id"; - return undefined; - } - - name = t.data.name; - kind = "result_id"; - value = t.data.val; - } else if (data.kind === "LiteralString") { - if (t.type !== TokenType.kStringLiteral) { - this.error_ = t.line + ": expected string not found"; - return undefined; - } - - name = t.data; - kind = "string"; - value = t.data; - } else if (data.kind === "LiteralInteger") { - if (t.type !== TokenType.kIntegerLiteral) { - this.error_ = t.line + ": expected integer not found"; - return undefined; - } - - name = "" + t.data; - kind = t.type; - value = t.data; - } else if (data.kind === "LiteralContextDependentNumber") { - if (result_type === undefined) { - this.error_ = t.line + - ": missing result type for context dependent number"; - return undefined; - } - if (t.type !== TokenType.kIntegerLiteral - && t.type !== TokenType.kFloatLiteral) { - this.error_ = t.line + ": expected number not found"; - return undefined; - } - - name = "" + t.data; - kind = result_type.type; - value = t.data; - - } else if (data.kind === "LiteralExtInstInteger") { - if (t.type !== TokenType.kIdentifier) { - this.error_ = t.line + ": expected instruction identifier"; - return undefined; - } - - if (this.grammar_.ext[t.data] === undefined) { - this.error_ = t.line + `: unable to find extended instruction (${t.data})`; - return undefined; - } - - name = t.data; - kind = "integer"; - value = this.grammar_.ext[t.data]; - - } else { - let d = this.grammar_.operand_kinds[data.kind]; - if (d === undefined) { - this.error_ = t.line + ": expected " + data.kind + " not found"; - return undefined; - } - - let val = d.values[t.data]["value"]; - let names = [t.data]; - if (d.type === "BitEnum") { - for(;;) { - let tmp = this.peek(0); - if (tmp.type !== TokenType.kPipe) { - break; - } - - this.next(); // skip pipe - tmp = this.next(); - - if (tmp.type !== TokenType.kIdentifier) { - this.error_ = tmp.line() + ": expected identifier"; - return undefined; - } - - val |= d.values[tmp.data]["value"]; - names.push(tmp.data); - } - } - - name = names.join("|"); - kind = d.type; - value = val; - - for (const op_name of names) { - if (d.values[op_name]['params'] === undefined) { - continue; - } - - for (const param of d.values[op_name]["params"]) { - params.push(this.extractOperand(ast, result_type, { kind: param })); - } - } - } - return new AST.Operand(ast, name, kind, value, params); - } -}
diff --git a/tools/sva/src/parser_test.js b/tools/sva/src/parser_test.js deleted file mode 100644 index dffc0b3..0000000 --- a/tools/sva/src/parser_test.js +++ /dev/null
@@ -1,489 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { assert } from "chai"; -import Lexer from "./lexer"; -import Parser from "./parser"; -import grammar from "./spirv.data.js"; - -describe("parser", () => { - it("parses an opcode", () => { - let input = "OpKill"; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpKill"); - assert.equal(inst.opcode(), 252); - assert.lengthOf(inst.operands, 0); - }); - - it("parses an opcode with an identifier", () => { - let input = "OpCapability Shader"; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpCapability"); - assert.equal(inst.opcode(), 17); - assert.lengthOf(inst.operands(), 1); - - let op = inst.operand(0); - assert.equal(op.name(), "Shader"); - assert.equal(op.type(), "ValueEnum"); - assert.equal(op.value(), 1); - }); - - it("parses an opcode with a result", () => { - let input = "%void = OpTypeVoid"; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpTypeVoid"); - assert.equal(inst.opcode(), 19); - assert.lengthOf(inst.operands(), 1); - - let op = inst.operand(0); - assert.equal(op.name(), "void"); - assert.equal(op.value(), 1); - }); - - it("sets module bounds based on numeric result", () => { - let input = "%3 = OpTypeVoid"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.equal(ast.getId("next"), 4); - }); - - it("returns the same value for a named result_id", () => { - let input = "%3 = OpTypeFunction %int %int"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - let op1 = inst.operand(1); - assert.equal(op1.name(), "int"); - assert.equal(op1.value(), 4); - - let op2 = inst.operand(2); - assert.equal(op2.name(), "int"); - assert.equal(op2.value(), 4); - }); - - it("parses an opcode with a string", () => { - let input = "OpEntryPoint Fragment %main \"main\""; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - let op = inst.operand(2); - assert.equal(op.name(), "main"); - assert.equal(op.value(), "main"); - }); - - describe("numerics", () => { - describe("integers", () => { - it("parses an opcode with an integer", () => { - let input = "OpSource GLSL 440"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - let op0 = inst.operand(0); - assert.equal(op0.name(), "GLSL"); - assert.equal(op0.type(), "ValueEnum"); - assert.equal(op0.value(), 2); - - let op1 = inst.operand(1); - assert.equal(op1.name(), "440"); - assert.equal(op1.value(), 440); - }); - - it("parses an opcode with a hex integer", () => { - let input = "OpSource GLSL 0x440"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - let op0 = inst.operand(0); - assert.equal(op0.name(), "GLSL"); - assert.equal(op0.type(), "ValueEnum"); - assert.equal(op0.value(), 2); - - let op1 = inst.operand(1); - assert.equal(op1.name(), "1088"); - assert.equal(op1.value(), 0x440); - }); - - it.skip("parses immediate integers", () => { - // TODO(dsinclair): Support or skip? - }); - }); - - describe("floats", () => { - it("parses floats", () => { - let input = `%float = OpTypeFloat 32 - %float1 = OpConstant %float 0.400000006`; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(1); - let op2 = inst.operand(2); - assert.equal(op2.value(), 0.400000006); - }); - - // TODO(dsinclair): Make hex encoded floats parse ... - it.skip("parses hex floats", () => { - let input = `%float = OpTypeFloat 32 - %nfloat = OpConstant %float -0.4p+2 - %pfloat = OpConstant %float 0.4p-2 - %inf = OpConstant %float32 0x1p+128 - %neginf = OpConstant %float32 -0x1p+128 - %aNaN = OpConstant %float32 0x1.8p+128 - %moreNaN = OpConstant %float32 -0x1.0002p+128`; - - let results = [-40.0, .004, 0x00000, 0x00000, 0x7fc00000, 0xff800100]; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 7); - - for (const idx in results) { - let inst = ast.instruction(idx); - let op2 = inst.operand(2); - assert.equal(op2.value(), results[idx]); - } - }); - - it("parses a float that looks like an int", () => { - let input = `%float = OpTypeFloat 32 - %float1 = OpConstant %float 1`; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(1); - let op2 = inst.operand(2); - assert.equal(op2.value(), 1); - assert.equal(op2.type(), "float"); - }); - }); - }); - - describe("enums", () => { - it("parses enum values", () => { - let input = `%1 = OpTypeFloat 32 - %30 = OpImageSampleExplicitLod %1 %20 %18 Grad|ConstOffset %22 %24 %29`; - - let vals = [{val: 1, name: "1"}, - {val: 30, name: "30"}, - {val: 20, name: "20"}, - {val: 18, name: "18"}, - {val: 12, name: "Grad|ConstOffset"}]; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(1); - for (let idx in vals) { - let op = inst.operand(idx); - assert.equal(op.name(), vals[idx].name); - assert.equal(op.value(), vals[idx].val); - } - - // BitEnum - let params = inst.operand(4).params(); - assert.lengthOf(params, 3); - assert.equal(params[0].name(), "22"); - assert.equal(params[0].value(), 22); - assert.equal(params[1].name(), "24"); - assert.equal(params[1].value(), 24); - assert.equal(params[2].name(), "29"); - assert.equal(params[2].value(), 29); - }); - - it("parses enumerants with parameters", () => { - let input ="OpExecutionMode %main LocalSize 2 3 4"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpExecutionMode"); - assert.lengthOf(inst.operands(), 2); - assert.equal(inst.operand(0).name(), "main"); - assert.equal(inst.operand(1).name(), "LocalSize"); - - let params = inst.operand(1).params(); - assert.lengthOf(params, 3); - assert.equal(params[0].name(), "2"); - assert.equal(params[1].name(), "3"); - assert.equal(params[2].name(), "4"); - }); - }); - - it("parses result into second operand if needed", () => { - let input = `%int = OpTypeInt 32 1 - %int_3 = OpConstant %int 3`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(1); - assert.equal(inst.name(), "OpConstant"); - assert.equal(inst.opcode(), 43); - assert.lengthOf(inst.operands(), 3); - - let op0 = inst.operand(0); - assert.equal(op0.name(), "int"); - assert.equal(op0.value(), 1); - - let op1 = inst.operand(1); - assert.equal(op1.name(), "int_3"); - assert.equal(op1.value(), 2); - - let op2 = inst.operand(2); - assert.equal(op2.name(), "3"); - assert.equal(op2.value(), 3); - }); - - describe("quantifiers", () => { - describe("?", () => { - it("skips if missing", () => { - let input = `OpImageWrite %1 %2 %3 -OpKill`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpImageWrite"); - assert.lengthOf(inst.operands(), 3); - }); - - it("skips if missing at EOF", () => { - let input = "OpImageWrite %1 %2 %3"; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpImageWrite"); - assert.lengthOf(inst.operands(), 3); - }); - - it("extracts if available", () => { - let input = `OpImageWrite %1 %2 %3 ConstOffset %2 -OpKill`; - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpImageWrite"); - assert.lengthOf(inst.operands(), 4); - assert.equal(inst.operand(3).name(), "ConstOffset"); - }); - }); - - describe("*", () => { - it("skips if missing", () => { - let input = `OpEntryPoint Fragment %main "main" -OpKill`; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpEntryPoint"); - assert.lengthOf(inst.operands(), 3); - assert.equal(inst.operand(2).name(), "main"); - }); - - it("extracts one if available", () => { - let input = `OpEntryPoint Fragment %main "main" %2 -OpKill`; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpEntryPoint"); - assert.lengthOf(inst.operands(), 4); - assert.equal(inst.operand(3).name(), "2"); - }); - - it("extracts multiple if available", () => { - let input = `OpEntryPoint Fragment %main "main" %2 %3 %4 %5 -OpKill`; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(0); - assert.equal(inst.name(), "OpEntryPoint"); - assert.lengthOf(inst.operands(), 7); - assert.equal(inst.operand(3).name(), "2"); - assert.equal(inst.operand(4).name(), "3"); - assert.equal(inst.operand(5).name(), "4"); - assert.equal(inst.operand(6).name(), "5"); - }); - }); - }); - - describe("extended instructions", () => { - it("errors on non-glsl extensions", () => { - let input = "%1 = OpExtInstImport \"OpenCL.std.100\""; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - assert.isUndefined(p.parse()); - }); - - it("handles extended instructions", () => { - let input = `%1 = OpExtInstImport "GLSL.std.450" - %44 = OpExtInst %7 %1 Sqrt %43`; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 2); - - let inst = ast.instruction(1); - assert.lengthOf(inst.operands(), 5); - assert.equal(inst.operand(3).value(), 31); - assert.equal(inst.operand(3).name(), "Sqrt"); - assert.equal(inst.operand(4).value(), 43); - assert.equal(inst.operand(4).name(), "43"); - }); - }); - - it.skip("handles spec constant ops", () => { - // let input = "%sum = OpSpecConstantOp %i32 IAdd %a %b"; - }); - - it("handles OpCopyMemory", () => { - let input = "OpCopyMemory %1 %2 " + - "Volatile|Nontemporal|MakePointerVisible %3 " + - "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4"; - - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - assert.exists(ast, p.error); - assert.lengthOf(ast.instructions(), 1); - - let inst = ast.instruction(0); - assert.lengthOf(inst.operands(), 4); - assert.equal(inst.operand(0).value(), 1); - assert.equal(inst.operand(1).value(), 2); - - assert.equal(inst.operand(2).name(), - "Volatile|Nontemporal|MakePointerVisible"); - assert.equal(inst.operand(2).value(), 21); - assert.lengthOf(inst.operand(2).params(), 1); - assert.equal(inst.operand(2).params()[0].value(), 3); - - assert.equal(inst.operand(3).name(), - "Aligned|MakePointerAvailable|NonPrivatePointer"); - assert.equal(inst.operand(3).value(), 42); - assert.lengthOf(inst.operand(3).params(), 2); - assert.equal(inst.operand(3).params()[0].value(), 16); - assert.equal(inst.operand(3).params()[1].value(), 4); - }); -});
diff --git a/tools/sva/src/spirv.data.js b/tools/sva/src/spirv.data.js deleted file mode 100644 index 67c0966..0000000 --- a/tools/sva/src/spirv.data.js +++ /dev/null
@@ -1,4570 +0,0 @@ -/*Copyright (c) 2014-2016 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.*/ - -// THIS FILE IS GENERATED WITH tools/process_grammar.rb - -export default { - "magic": "0x07230203", - "version": [ - 1, - 5 - ], - "instructions": { - "OpNop": { - "opcode": 0, - "operands": [ - - ] - }, - "OpUndef": { - "opcode": 1, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpSourceContinued": { - "opcode": 2, - "operands": [ - { - "kind": "LiteralString" - } - ] - }, - "OpSource": { - "opcode": 3, - "operands": [ - { - "kind": "SourceLanguage" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "IdRef", - "quantifier": "?" - }, - { - "kind": "LiteralString", - "quantifier": "?" - } - ] - }, - "OpSourceExtension": { - "opcode": 4, - "operands": [ - { - "kind": "LiteralString" - } - ] - }, - "OpName": { - "opcode": 5, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "LiteralString" - } - ] - }, - "OpMemberName": { - "opcode": 6, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "LiteralString" - } - ] - }, - "OpString": { - "opcode": 7, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "LiteralString" - } - ] - }, - "OpLine": { - "opcode": 8, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "LiteralInteger" - } - ] - }, - "OpExtension": { - "opcode": 10, - "operands": [ - { - "kind": "LiteralString" - } - ] - }, - "OpExtInstImport": { - "opcode": 11, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "LiteralString" - } - ] - }, - "OpExtInst": { - "opcode": 12, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralExtInstInteger" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpMemoryModel": { - "opcode": 14, - "operands": [ - { - "kind": "AddressingModel" - }, - { - "kind": "MemoryModel" - } - ] - }, - "OpEntryPoint": { - "opcode": 15, - "operands": [ - { - "kind": "ExecutionModel" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralString" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpExecutionMode": { - "opcode": 16, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "ExecutionMode" - } - ] - }, - "OpCapability": { - "opcode": 17, - "operands": [ - { - "kind": "Capability" - } - ] - }, - "OpTypeVoid": { - "opcode": 19, - "operands": [ - { - "kind": "IdResult" - } - ] - }, - "OpTypeBool": { - "opcode": 20, - "operands": [ - { - "kind": "IdResult" - } - ] - }, - "OpTypeInt": { - "opcode": 21, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "LiteralInteger" - } - ] - }, - "OpTypeFloat": { - "opcode": 22, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "LiteralInteger" - } - ] - }, - "OpTypeVector": { - "opcode": 23, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - } - ] - }, - "OpTypeMatrix": { - "opcode": 24, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - } - ] - }, - "OpTypeImage": { - "opcode": 25, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "Dim" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "ImageFormat" - }, - { - "kind": "AccessQualifier", - "quantifier": "?" - } - ] - }, - "OpTypeSampler": { - "opcode": 26, - "operands": [ - { - "kind": "IdResult" - } - ] - }, - "OpTypeSampledImage": { - "opcode": 27, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpTypeArray": { - "opcode": 28, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpTypeRuntimeArray": { - "opcode": 29, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpTypeStruct": { - "opcode": 30, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpTypePointer": { - "opcode": 32, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "StorageClass" - }, - { - "kind": "IdRef" - } - ] - }, - "OpTypeFunction": { - "opcode": 33, - "operands": [ - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpConstantTrue": { - "opcode": 41, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpConstantFalse": { - "opcode": 42, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpConstant": { - "opcode": 43, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "LiteralContextDependentNumber" - } - ] - }, - "OpConstantComposite": { - "opcode": 44, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpConstantNull": { - "opcode": 46, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpSpecConstantTrue": { - "opcode": 48, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpSpecConstantFalse": { - "opcode": 49, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpSpecConstant": { - "opcode": 50, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "LiteralContextDependentNumber" - } - ] - }, - "OpSpecConstantComposite": { - "opcode": 51, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpSpecConstantOp": { - "opcode": 52, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "LiteralSpecConstantOpInteger" - } - ] - }, - "OpFunction": { - "opcode": 54, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "FunctionControl" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFunctionParameter": { - "opcode": 55, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - } - ] - }, - "OpFunctionEnd": { - "opcode": 56, - "operands": [ - - ] - }, - "OpFunctionCall": { - "opcode": 57, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpVariable": { - "opcode": 59, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "StorageClass" - }, - { - "kind": "IdRef", - "quantifier": "?" - } - ] - }, - "OpImageTexelPointer": { - "opcode": 60, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpLoad": { - "opcode": 61, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "MemoryAccess", - "quantifier": "?" - } - ] - }, - "OpStore": { - "opcode": 62, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "MemoryAccess", - "quantifier": "?" - } - ] - }, - "OpCopyMemory": { - "opcode": 63, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "MemoryAccess", - "quantifier": "?" - }, - { - "kind": "MemoryAccess", - "quantifier": "?" - } - ] - }, - "OpAccessChain": { - "opcode": 65, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpInBoundsAccessChain": { - "opcode": 66, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpArrayLength": { - "opcode": 68, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - } - ] - }, - "OpDecorate": { - "opcode": 71, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "Decoration" - } - ] - }, - "OpMemberDecorate": { - "opcode": 72, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "Decoration" - } - ] - }, - "OpDecorationGroup": { - "opcode": 73, - "operands": [ - { - "kind": "IdResult" - } - ] - }, - "OpGroupDecorate": { - "opcode": 74, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpGroupMemberDecorate": { - "opcode": 75, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "PairIdRefLiteralInteger", - "quantifier": "*" - } - ] - }, - "OpVectorExtractDynamic": { - "opcode": 77, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpVectorInsertDynamic": { - "opcode": 78, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpVectorShuffle": { - "opcode": 79, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger", - "quantifier": "*" - } - ] - }, - "OpCompositeConstruct": { - "opcode": 80, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef", - "quantifier": "*" - } - ] - }, - "OpCompositeExtract": { - "opcode": 81, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger", - "quantifier": "*" - } - ] - }, - "OpCompositeInsert": { - "opcode": 82, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger", - "quantifier": "*" - } - ] - }, - "OpCopyObject": { - "opcode": 83, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpTranspose": { - "opcode": 84, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSampledImage": { - "opcode": 86, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpImageSampleImplicitLod": { - "opcode": 87, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageSampleExplicitLod": { - "opcode": 88, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands" - } - ] - }, - "OpImageSampleDrefImplicitLod": { - "opcode": 89, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageSampleDrefExplicitLod": { - "opcode": 90, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands" - } - ] - }, - "OpImageSampleProjImplicitLod": { - "opcode": 91, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageSampleProjExplicitLod": { - "opcode": 92, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands" - } - ] - }, - "OpImageSampleProjDrefImplicitLod": { - "opcode": 93, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageSampleProjDrefExplicitLod": { - "opcode": 94, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands" - } - ] - }, - "OpImageFetch": { - "opcode": 95, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageGather": { - "opcode": 96, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageDrefGather": { - "opcode": 97, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageRead": { - "opcode": 98, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImageWrite": { - "opcode": 99, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "ImageOperands", - "quantifier": "?" - } - ] - }, - "OpImage": { - "opcode": 100, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpImageQuerySizeLod": { - "opcode": 103, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpImageQuerySize": { - "opcode": 104, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpImageQueryLod": { - "opcode": 105, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpImageQueryLevels": { - "opcode": 106, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpImageQuerySamples": { - "opcode": 107, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpConvertFToU": { - "opcode": 109, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpConvertFToS": { - "opcode": 110, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpConvertSToF": { - "opcode": 111, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpConvertUToF": { - "opcode": 112, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpUConvert": { - "opcode": 113, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSConvert": { - "opcode": 114, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFConvert": { - "opcode": 115, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpQuantizeToF16": { - "opcode": 116, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitcast": { - "opcode": 124, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSNegate": { - "opcode": 126, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFNegate": { - "opcode": 127, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpIAdd": { - "opcode": 128, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFAdd": { - "opcode": 129, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpISub": { - "opcode": 130, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFSub": { - "opcode": 131, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpIMul": { - "opcode": 132, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFMul": { - "opcode": 133, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpUDiv": { - "opcode": 134, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSDiv": { - "opcode": 135, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFDiv": { - "opcode": 136, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpUMod": { - "opcode": 137, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSRem": { - "opcode": 138, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSMod": { - "opcode": 139, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFRem": { - "opcode": 140, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFMod": { - "opcode": 141, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpVectorTimesScalar": { - "opcode": 142, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpMatrixTimesScalar": { - "opcode": 143, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpVectorTimesMatrix": { - "opcode": 144, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpMatrixTimesVector": { - "opcode": 145, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpMatrixTimesMatrix": { - "opcode": 146, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpOuterProduct": { - "opcode": 147, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDot": { - "opcode": 148, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpIAddCarry": { - "opcode": 149, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpISubBorrow": { - "opcode": 150, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpUMulExtended": { - "opcode": 151, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSMulExtended": { - "opcode": 152, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAny": { - "opcode": 154, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAll": { - "opcode": 155, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpIsNan": { - "opcode": 156, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpIsInf": { - "opcode": 157, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpLogicalEqual": { - "opcode": 164, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpLogicalNotEqual": { - "opcode": 165, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpLogicalOr": { - "opcode": 166, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpLogicalAnd": { - "opcode": 167, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpLogicalNot": { - "opcode": 168, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSelect": { - "opcode": 169, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpIEqual": { - "opcode": 170, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpINotEqual": { - "opcode": 171, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpUGreaterThan": { - "opcode": 172, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSGreaterThan": { - "opcode": 173, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpUGreaterThanEqual": { - "opcode": 174, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSGreaterThanEqual": { - "opcode": 175, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpULessThan": { - "opcode": 176, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSLessThan": { - "opcode": 177, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpULessThanEqual": { - "opcode": 178, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpSLessThanEqual": { - "opcode": 179, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFOrdEqual": { - "opcode": 180, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFUnordEqual": { - "opcode": 181, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFOrdNotEqual": { - "opcode": 182, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFUnordNotEqual": { - "opcode": 183, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFOrdLessThan": { - "opcode": 184, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFUnordLessThan": { - "opcode": 185, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFOrdGreaterThan": { - "opcode": 186, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFUnordGreaterThan": { - "opcode": 187, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFOrdLessThanEqual": { - "opcode": 188, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFUnordLessThanEqual": { - "opcode": 189, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFOrdGreaterThanEqual": { - "opcode": 190, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFUnordGreaterThanEqual": { - "opcode": 191, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpShiftRightLogical": { - "opcode": 194, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpShiftRightArithmetic": { - "opcode": 195, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpShiftLeftLogical": { - "opcode": 196, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitwiseOr": { - "opcode": 197, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitwiseXor": { - "opcode": 198, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitwiseAnd": { - "opcode": 199, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpNot": { - "opcode": 200, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitFieldInsert": { - "opcode": 201, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitFieldSExtract": { - "opcode": 202, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitFieldUExtract": { - "opcode": 203, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitReverse": { - "opcode": 204, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpBitCount": { - "opcode": 205, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDPdx": { - "opcode": 207, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDPdy": { - "opcode": 208, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFwidth": { - "opcode": 209, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDPdxFine": { - "opcode": 210, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDPdyFine": { - "opcode": 211, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFwidthFine": { - "opcode": 212, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDPdxCoarse": { - "opcode": 213, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDPdyCoarse": { - "opcode": 214, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpFwidthCoarse": { - "opcode": 215, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpControlBarrier": { - "opcode": 224, - "operands": [ - { - "kind": "IdScope" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - } - ] - }, - "OpMemoryBarrier": { - "opcode": 225, - "operands": [ - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - } - ] - }, - "OpAtomicLoad": { - "opcode": 227, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - } - ] - }, - "OpAtomicStore": { - "opcode": 228, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicExchange": { - "opcode": 229, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicCompareExchange": { - "opcode": 230, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicIIncrement": { - "opcode": 232, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - } - ] - }, - "OpAtomicIDecrement": { - "opcode": 233, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - } - ] - }, - "OpAtomicIAdd": { - "opcode": 234, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicISub": { - "opcode": 235, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicSMin": { - "opcode": 236, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicUMin": { - "opcode": 237, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicSMax": { - "opcode": 238, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicUMax": { - "opcode": 239, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicAnd": { - "opcode": 240, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicOr": { - "opcode": 241, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpAtomicXor": { - "opcode": 242, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdScope" - }, - { - "kind": "IdMemorySemantics" - }, - { - "kind": "IdRef" - } - ] - }, - "OpPhi": { - "opcode": 245, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "PairIdRefIdRef", - "quantifier": "*" - } - ] - }, - "OpLoopMerge": { - "opcode": 246, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "LoopControl" - } - ] - }, - "OpSelectionMerge": { - "opcode": 247, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "SelectionControl" - } - ] - }, - "OpLabel": { - "opcode": 248, - "operands": [ - { - "kind": "IdResult" - } - ] - }, - "OpBranch": { - "opcode": 249, - "operands": [ - { - "kind": "IdRef" - } - ] - }, - "OpBranchConditional": { - "opcode": 250, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger", - "quantifier": "*" - } - ] - }, - "OpSwitch": { - "opcode": 251, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - }, - { - "kind": "PairLiteralIntegerIdRef", - "quantifier": "*" - } - ] - }, - "OpKill": { - "opcode": 252, - "operands": [ - - ] - }, - "OpReturn": { - "opcode": 253, - "operands": [ - - ] - }, - "OpReturnValue": { - "opcode": 254, - "operands": [ - { - "kind": "IdRef" - } - ] - }, - "OpUnreachable": { - "opcode": 255, - "operands": [ - - ] - }, - "OpNoLine": { - "opcode": 317, - "operands": [ - - ] - }, - "OpModuleProcessed": { - "opcode": 330, - "operands": [ - { - "kind": "LiteralString" - } - ] - }, - "OpExecutionModeId": { - "opcode": 331, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "ExecutionMode" - } - ] - }, - "OpDecorateId": { - "opcode": 332, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "Decoration" - } - ] - }, - "OpCopyLogical": { - "opcode": 400, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - } - ] - }, - "OpPtrEqual": { - "opcode": 401, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpPtrNotEqual": { - "opcode": 402, - "operands": [ - { - "kind": "IdResultType" - }, - { - "kind": "IdResult" - }, - { - "kind": "IdRef" - }, - { - "kind": "IdRef" - } - ] - }, - "OpDecorateString": { - "opcode": 5632, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "Decoration" - } - ] - }, - "OpDecorateStringGOOGLE": { - "opcode": 5632, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "Decoration" - } - ] - }, - "OpMemberDecorateString": { - "opcode": 5633, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "Decoration" - } - ] - }, - "OpMemberDecorateStringGOOGLE": { - "opcode": 5633, - "operands": [ - { - "kind": "IdRef" - }, - { - "kind": "LiteralInteger" - }, - { - "kind": "Decoration" - } - ] - } - }, - "operand_kinds": { - "ImageOperands": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - }, - "Bias": { - "value": 1, - "params": [ - "IdRef" - ] - }, - "Lod": { - "value": 2, - "params": [ - "IdRef" - ] - }, - "Grad": { - "value": 4, - "params": [ - "IdRef", - "IdRef" - ] - }, - "ConstOffset": { - "value": 8, - "params": [ - "IdRef" - ] - }, - "Sample": { - "value": 64, - "params": [ - "IdRef" - ] - }, - "MakeTexelAvailable": { - "value": 256, - "params": [ - "IdScope" - ] - }, - "MakeTexelAvailableKHR": { - "value": 256, - "params": [ - "IdScope" - ] - }, - "MakeTexelVisible": { - "value": 512, - "params": [ - "IdScope" - ] - }, - "MakeTexelVisibleKHR": { - "value": 512, - "params": [ - "IdScope" - ] - }, - "NonPrivateTexel": { - "value": 1024 - }, - "NonPrivateTexelKHR": { - "value": 1024 - }, - "VolatileTexel": { - "value": 2048 - }, - "VolatileTexelKHR": { - "value": 2048 - }, - "SignExtend": { - "value": 4096 - }, - "ZeroExtend": { - "value": 8192 - } - } - }, - "FPFastMathMode": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - } - } - }, - "SelectionControl": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - }, - "Flatten": { - "value": 1 - }, - "DontFlatten": { - "value": 2 - } - } - }, - "LoopControl": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - }, - "Unroll": { - "value": 1 - }, - "DontUnroll": { - "value": 2 - }, - "DependencyInfinite": { - "value": 4 - }, - "DependencyLength": { - "value": 8, - "params": [ - "LiteralInteger" - ] - }, - "MinIterations": { - "value": 16, - "params": [ - "LiteralInteger" - ] - }, - "MaxIterations": { - "value": 32, - "params": [ - "LiteralInteger" - ] - }, - "IterationMultiple": { - "value": 64, - "params": [ - "LiteralInteger" - ] - }, - "PeelCount": { - "value": 128, - "params": [ - "LiteralInteger" - ] - }, - "PartialCount": { - "value": 256, - "params": [ - "LiteralInteger" - ] - } - } - }, - "FunctionControl": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - }, - "Inline": { - "value": 1 - }, - "DontInline": { - "value": 2 - }, - "Pure": { - "value": 4 - }, - "Const": { - "value": 8 - } - } - }, - "MemorySemantics": { - "type": "BitEnum", - "values": { - "Relaxed": { - "value": 0 - }, - "None": { - "value": 0 - }, - "Acquire": { - "value": 2 - }, - "Release": { - "value": 4 - }, - "AcquireRelease": { - "value": 8 - }, - "SequentiallyConsistent": { - "value": 16 - }, - "UniformMemory": { - "value": 64 - }, - "SubgroupMemory": { - "value": 128 - }, - "WorkgroupMemory": { - "value": 256 - }, - "CrossWorkgroupMemory": { - "value": 512 - }, - "ImageMemory": { - "value": 2048 - }, - "OutputMemory": { - "value": 4096 - }, - "OutputMemoryKHR": { - "value": 4096 - }, - "MakeAvailable": { - "value": 8192 - }, - "MakeAvailableKHR": { - "value": 8192 - }, - "MakeVisible": { - "value": 16384 - }, - "MakeVisibleKHR": { - "value": 16384 - }, - "Volatile": { - "value": 32768 - } - } - }, - "MemoryAccess": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - }, - "Volatile": { - "value": 1 - }, - "Aligned": { - "value": 2, - "params": [ - "LiteralInteger" - ] - }, - "Nontemporal": { - "value": 4 - }, - "MakePointerAvailable": { - "value": 8, - "params": [ - "IdScope" - ] - }, - "MakePointerAvailableKHR": { - "value": 8, - "params": [ - "IdScope" - ] - }, - "MakePointerVisible": { - "value": 16, - "params": [ - "IdScope" - ] - }, - "MakePointerVisibleKHR": { - "value": 16, - "params": [ - "IdScope" - ] - }, - "NonPrivatePointer": { - "value": 32 - }, - "NonPrivatePointerKHR": { - "value": 32 - } - } - }, - "KernelProfilingInfo": { - "type": "BitEnum", - "values": { - "None": { - "value": 0 - } - } - }, - "SourceLanguage": { - "type": "ValueEnum", - "values": { - "Unknown": { - "value": 0 - }, - "ESSL": { - "value": 1 - }, - "GLSL": { - "value": 2 - }, - "OpenCL_C": { - "value": 3 - }, - "OpenCL_CPP": { - "value": 4 - }, - "HLSL": { - "value": 5 - } - } - }, - "ExecutionModel": { - "type": "ValueEnum", - "values": { - "Vertex": { - "value": 0 - }, - "Fragment": { - "value": 4 - }, - "GLCompute": { - "value": 5 - } - } - }, - "AddressingModel": { - "type": "ValueEnum", - "values": { - "Logical": { - "value": 0 - } - } - }, - "MemoryModel": { - "type": "ValueEnum", - "values": { - "Simple": { - "value": 0 - }, - "GLSL450": { - "value": 1 - }, - "Vulkan": { - "value": 3 - }, - "VulkanKHR": { - "value": 3 - } - } - }, - "ExecutionMode": { - "type": "ValueEnum", - "values": { - "PixelCenterInteger": { - "value": 6 - }, - "OriginUpperLeft": { - "value": 7 - }, - "OriginLowerLeft": { - "value": 8 - }, - "EarlyFragmentTests": { - "value": 9 - }, - "DepthReplacing": { - "value": 12 - }, - "DepthGreater": { - "value": 14 - }, - "DepthLess": { - "value": 15 - }, - "DepthUnchanged": { - "value": 16 - }, - "LocalSize": { - "value": 17, - "params": [ - "LiteralInteger", - "LiteralInteger", - "LiteralInteger" - ] - }, - "LocalSizeId": { - "value": 38, - "params": [ - "IdRef", - "IdRef", - "IdRef" - ] - } - } - }, - "StorageClass": { - "type": "ValueEnum", - "values": { - "UniformConstant": { - "value": 0 - }, - "Input": { - "value": 1 - }, - "Uniform": { - "value": 2 - }, - "Output": { - "value": 3 - }, - "Workgroup": { - "value": 4 - }, - "CrossWorkgroup": { - "value": 5 - }, - "Private": { - "value": 6 - }, - "Function": { - "value": 7 - }, - "PushConstant": { - "value": 9 - }, - "Image": { - "value": 11 - }, - "StorageBuffer": { - "value": 12 - } - } - }, - "Dim": { - "type": "ValueEnum", - "values": { - "1D": { - "value": 0 - }, - "2D": { - "value": 1 - }, - "3D": { - "value": 2 - }, - "Cube": { - "value": 3 - } - } - }, - "ImageFormat": { - "type": "ValueEnum", - "values": { - "Unknown": { - "value": 0 - }, - "Rgba32f": { - "value": 1 - }, - "Rgba16f": { - "value": 2 - }, - "R32f": { - "value": 3 - }, - "Rgba8": { - "value": 4 - }, - "Rgba8Snorm": { - "value": 5 - }, - "Rgba32i": { - "value": 21 - }, - "Rgba16i": { - "value": 22 - }, - "Rgba8i": { - "value": 23 - }, - "R32i": { - "value": 24 - }, - "Rgba32ui": { - "value": 30 - }, - "Rgba16ui": { - "value": 31 - }, - "Rgba8ui": { - "value": 32 - }, - "R32ui": { - "value": 33 - } - } - }, - "FPRoundingMode": { - "type": "ValueEnum", - "values": { - "RTE": { - "value": 0 - }, - "RTZ": { - "value": 1 - }, - "RTP": { - "value": 2 - }, - "RTN": { - "value": 3 - } - } - }, - "Decoration": { - "type": "ValueEnum", - "values": { - "RelaxedPrecision": { - "value": 0 - }, - "SpecId": { - "value": 1, - "params": [ - "LiteralInteger" - ] - }, - "Block": { - "value": 2 - }, - "BufferBlock": { - "value": 3 - }, - "RowMajor": { - "value": 4 - }, - "ColMajor": { - "value": 5 - }, - "ArrayStride": { - "value": 6, - "params": [ - "LiteralInteger" - ] - }, - "MatrixStride": { - "value": 7, - "params": [ - "LiteralInteger" - ] - }, - "GLSLShared": { - "value": 8 - }, - "GLSLPacked": { - "value": 9 - }, - "BuiltIn": { - "value": 11, - "params": [ - "BuiltIn" - ] - }, - "NoPerspective": { - "value": 13 - }, - "Flat": { - "value": 14 - }, - "Centroid": { - "value": 16 - }, - "Invariant": { - "value": 18 - }, - "Restrict": { - "value": 19 - }, - "Aliased": { - "value": 20 - }, - "Volatile": { - "value": 21 - }, - "Coherent": { - "value": 23 - }, - "NonWritable": { - "value": 24 - }, - "NonReadable": { - "value": 25 - }, - "Uniform": { - "value": 26 - }, - "UniformId": { - "value": 27, - "params": [ - "IdScope" - ] - }, - "Location": { - "value": 30, - "params": [ - "LiteralInteger" - ] - }, - "Component": { - "value": 31, - "params": [ - "LiteralInteger" - ] - }, - "Index": { - "value": 32, - "params": [ - "LiteralInteger" - ] - }, - "Binding": { - "value": 33, - "params": [ - "LiteralInteger" - ] - }, - "DescriptorSet": { - "value": 34, - "params": [ - "LiteralInteger" - ] - }, - "Offset": { - "value": 35, - "params": [ - "LiteralInteger" - ] - }, - "FPRoundingMode": { - "value": 39, - "params": [ - "FPRoundingMode" - ] - }, - "NoContraction": { - "value": 42 - }, - "NoSignedWrap": { - "value": 4469 - }, - "NoUnsignedWrap": { - "value": 4470 - }, - "ExplicitInterpAMD": { - "value": 4999 - }, - "CounterBuffer": { - "value": 5634, - "params": [ - "IdRef" - ] - }, - "HlslCounterBufferGOOGLE": { - "value": 5634, - "params": [ - "IdRef" - ] - }, - "UserSemantic": { - "value": 5635, - "params": [ - "LiteralString" - ] - }, - "HlslSemanticGOOGLE": { - "value": 5635, - "params": [ - "LiteralString" - ] - }, - "UserTypeGOOGLE": { - "value": 5636, - "params": [ - "LiteralString" - ] - } - } - }, - "BuiltIn": { - "type": "ValueEnum", - "values": { - "Position": { - "value": 0 - }, - "PointSize": { - "value": 1 - }, - "VertexId": { - "value": 5 - }, - "InstanceId": { - "value": 6 - }, - "FragCoord": { - "value": 15 - }, - "PointCoord": { - "value": 16 - }, - "FrontFacing": { - "value": 17 - }, - "SampleMask": { - "value": 20 - }, - "FragDepth": { - "value": 22 - }, - "HelperInvocation": { - "value": 23 - }, - "NumWorkgroups": { - "value": 24 - }, - "WorkgroupSize": { - "value": 25 - }, - "WorkgroupId": { - "value": 26 - }, - "LocalInvocationId": { - "value": 27 - }, - "GlobalInvocationId": { - "value": 28 - }, - "LocalInvocationIndex": { - "value": 29 - }, - "VertexIndex": { - "value": 42 - }, - "InstanceIndex": { - "value": 43 - }, - "BaryCoordNoPerspAMD": { - "value": 4992 - }, - "BaryCoordNoPerspCentroidAMD": { - "value": 4993 - }, - "BaryCoordNoPerspSampleAMD": { - "value": 4994 - }, - "BaryCoordSmoothAMD": { - "value": 4995 - }, - "BaryCoordSmoothCentroidAMD": { - "value": 4996 - }, - "BaryCoordSmoothSampleAMD": { - "value": 4997 - }, - "BaryCoordPullModelAMD": { - "value": 4998 - } - } - }, - "Scope": { - "type": "ValueEnum", - "values": { - "CrossDevice": { - "value": 0 - }, - "Device": { - "value": 1 - }, - "Workgroup": { - "value": 2 - }, - "Subgroup": { - "value": 3 - }, - "Invocation": { - "value": 4 - }, - "QueueFamily": { - "value": 5 - }, - "QueueFamilyKHR": { - "value": 5 - } - } - }, - "Capability": { - "type": "ValueEnum", - "values": { - "Matrix": { - "value": 0 - }, - "Shader": { - "value": 1 - }, - "Geometry": { - "value": 2 - }, - "Tessellation": { - "value": 3 - }, - "Addresses": { - "value": 4 - }, - "Linkage": { - "value": 5 - }, - "Kernel": { - "value": 6 - }, - "Float16": { - "value": 9 - }, - "Float64": { - "value": 10 - }, - "Int64": { - "value": 11 - }, - "Groups": { - "value": 18 - }, - "AtomicStorage": { - "value": 21 - }, - "Int16": { - "value": 22 - }, - "ImageGatherExtended": { - "value": 25 - }, - "StorageImageMultisample": { - "value": 27 - }, - "UniformBufferArrayDynamicIndexing": { - "value": 28 - }, - "SampledImageArrayDynamicIndexing": { - "value": 29 - }, - "StorageBufferArrayDynamicIndexing": { - "value": 30 - }, - "StorageImageArrayDynamicIndexing": { - "value": 31 - }, - "ClipDistance": { - "value": 32 - }, - "CullDistance": { - "value": 33 - }, - "SampleRateShading": { - "value": 35 - }, - "SampledRect": { - "value": 37 - }, - "Int8": { - "value": 39 - }, - "InputAttachment": { - "value": 40 - }, - "SparseResidency": { - "value": 41 - }, - "MinLod": { - "value": 42 - }, - "Sampled1D": { - "value": 43 - }, - "Image1D": { - "value": 44 - }, - "SampledCubeArray": { - "value": 45 - }, - "SampledBuffer": { - "value": 46 - }, - "ImageMSArray": { - "value": 48 - }, - "StorageImageExtendedFormats": { - "value": 49 - }, - "ImageQuery": { - "value": 50 - }, - "DerivativeControl": { - "value": 51 - }, - "InterpolationFunction": { - "value": 52 - }, - "TransformFeedback": { - "value": 53 - }, - "StorageImageReadWithoutFormat": { - "value": 55 - }, - "StorageImageWriteWithoutFormat": { - "value": 56 - }, - "GroupNonUniform": { - "value": 61 - }, - "ShaderLayer": { - "value": 69 - }, - "ShaderViewportIndex": { - "value": 70 - }, - "SubgroupBallotKHR": { - "value": 4423 - }, - "DrawParameters": { - "value": 4427 - }, - "SubgroupVoteKHR": { - "value": 4431 - }, - "StorageBuffer16BitAccess": { - "value": 4433 - }, - "StorageUniformBufferBlock16": { - "value": 4433 - }, - "StoragePushConstant16": { - "value": 4435 - }, - "StorageInputOutput16": { - "value": 4436 - }, - "DeviceGroup": { - "value": 4437 - }, - "MultiView": { - "value": 4439 - }, - "VariablePointersStorageBuffer": { - "value": 4441 - }, - "AtomicStorageOps": { - "value": 4445 - }, - "SampleMaskPostDepthCoverage": { - "value": 4447 - }, - "StorageBuffer8BitAccess": { - "value": 4448 - }, - "StoragePushConstant8": { - "value": 4450 - }, - "DenormPreserve": { - "value": 4464 - }, - "DenormFlushToZero": { - "value": 4465 - }, - "SignedZeroInfNanPreserve": { - "value": 4466 - }, - "RoundingModeRTE": { - "value": 4467 - }, - "RoundingModeRTZ": { - "value": 4468 - }, - "Float16ImageAMD": { - "value": 5008 - }, - "ImageGatherBiasLodAMD": { - "value": 5009 - }, - "FragmentMaskAMD": { - "value": 5010 - }, - "StencilExportEXT": { - "value": 5013 - }, - "ImageReadWriteLodAMD": { - "value": 5015 - }, - "ShaderClockKHR": { - "value": 5055 - }, - "QuadControlKHR": { - "value": 5087 - }, - "FragmentFullyCoveredEXT": { - "value": 5265 - }, - "MeshShadingNV": { - "value": 5266 - }, - "ImageFootprintNV": { - "value": 5282 - }, - "FragmentBarycentricNV": { - "value": 5284 - }, - "ComputeDerivativeGroupQuadsNV": { - "value": 5288 - }, - "FragmentDensityEXT": { - "value": 5291 - }, - "ShadingRateNV": { - "value": 5291 - }, - "GroupNonUniformPartitionedNV": { - "value": 5297 - }, - "ShaderNonUniform": { - "value": 5301 - }, - "ShaderNonUniformEXT": { - "value": 5301 - }, - "RuntimeDescriptorArray": { - "value": 5302 - }, - "RuntimeDescriptorArrayEXT": { - "value": 5302 - }, - "RayTracingNV": { - "value": 5340 - }, - "VulkanMemoryModel": { - "value": 5345 - }, - "VulkanMemoryModelKHR": { - "value": 5345 - }, - "VulkanMemoryModelDeviceScope": { - "value": 5346 - }, - "VulkanMemoryModelDeviceScopeKHR": { - "value": 5346 - }, - "PhysicalStorageBufferAddresses": { - "value": 5347 - }, - "PhysicalStorageBufferAddressesEXT": { - "value": 5347 - }, - "ComputeDerivativeGroupLinearNV": { - "value": 5350 - }, - "CooperativeMatrixNV": { - "value": 5357 - }, - "FragmentShaderSampleInterlockEXT": { - "value": 5363 - }, - "FragmentShaderShadingRateInterlockEXT": { - "value": 5372 - }, - "ShaderSMBuiltinsNV": { - "value": 5373 - }, - "FragmentShaderPixelInterlockEXT": { - "value": 5378 - }, - "DemoteToHelperInvocationEXT": { - "value": 5379 - }, - "SubgroupShuffleINTEL": { - "value": 5568 - }, - "SubgroupBufferBlockIOINTEL": { - "value": 5569 - }, - "SubgroupImageBlockIOINTEL": { - "value": 5570 - }, - "SubgroupImageMediaBlockIOINTEL": { - "value": 5579 - }, - "IntegerFunctions2INTEL": { - "value": 5584 - }, - "SubgroupAvcMotionEstimationINTEL": { - "value": 5696 - }, - "SubgroupAvcMotionEstimationIntraINTEL": { - "value": 5697 - }, - "SubgroupAvcMotionEstimationChromaINTEL": { - "value": 5698 - } - } - } - }, - "ext": { - "Round": 1, - "RoundEven": 2, - "Trunc": 3, - "FAbs": 4, - "SAbs": 5, - "FSign": 6, - "SSign": 7, - "Floor": 8, - "Ceil": 9, - "Fract": 10, - "Radians": 11, - "Degrees": 12, - "Sin": 13, - "Cos": 14, - "Tan": 15, - "Asin": 16, - "Acos": 17, - "Atan": 18, - "Sinh": 19, - "Cosh": 20, - "Tanh": 21, - "Asinh": 22, - "Acosh": 23, - "Atanh": 24, - "Atan2": 25, - "Pow": 26, - "Exp": 27, - "Log": 28, - "Exp2": 29, - "Log2": 30, - "Sqrt": 31, - "InverseSqrt": 32, - "Determinant": 33, - "MatrixInverse": 34, - "Modf": 35, - "ModfStruct": 36, - "FMin": 37, - "UMin": 38, - "SMin": 39, - "FMax": 40, - "UMax": 41, - "SMax": 42, - "FClamp": 43, - "UClamp": 44, - "SClamp": 45, - "FMix": 46, - "IMix": 47, - "Step": 48, - "SmoothStep": 49, - "Fma": 50, - "Frexp": 51, - "FrexpStruct": 52, - "Ldexp": 53, - "PackSnorm4x8": 54, - "PackUnorm4x8": 55, - "PackSnorm2x16": 56, - "PackUnorm2x16": 57, - "PackHalf2x16": 58, - "PackDouble2x32": 59, - "UnpackSnorm2x16": 60, - "UnpackUnorm2x16": 61, - "UnpackHalf2x16": 62, - "UnpackSnorm4x8": 63, - "UnpackUnorm4x8": 64, - "UnpackDouble2x32": 65, - "Length": 66, - "Distance": 67, - "Cross": 68, - "Normalize": 69, - "FaceForward": 70, - "Reflect": 71, - "Refract": 72, - "FindILsb": 73, - "FindSMsb": 74, - "FindUMsb": 75, - "InterpolateAtCentroid": 76, - "InterpolateAtSample": 77, - "InterpolateAtOffset": 78, - "NMin": 79, - "NMax": 80, - "NClamp": 81 - } -}
diff --git a/tools/sva/src/sva.js b/tools/sva/src/sva.js deleted file mode 100644 index c76ed29..0000000 --- a/tools/sva/src/sva.js +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Parser from "./parser.js"; -import Lexer from "./lexer.js"; -import Assembler from "./assembler.js"; - -import grammar from "./spirv.data.js"; - -export default class SVA { - /** - * Attempts to convert |input| SPIR-V assembly into SPIR-V binary. - * - * @param {String} the input string containing the assembly - * @return {Uint32Array|string} returns a Uint32Array containing the binary - * SPIR-V or a string on error. - */ - static assemble(input) { - let l = new Lexer(input); - let p = new Parser(grammar, l); - - let ast = p.parse(); - if (ast === undefined) - return p.error; - - let a = new Assembler(ast); - return a.assemble(); - } -}
diff --git a/tools/sva/src/token.js b/tools/sva/src/token.js deleted file mode 100644 index 3813191..0000000 --- a/tools/sva/src/token.js +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const TokenType = { - kEOF: "end of file", - kError: "error", - - kIdentifier: "identifier", - - kIntegerLiteral: "integer_literal", - kFloatLiteral: "float_literal", - kStringLiteral: "string_literal", - kResultId: "result_id", - - kOp: "Op", - kEqual: "=", - kPipe: "|", -}; - -class Token { - /** - * @param {TokenType} type The type of token - * @param {Integer} line The line number this token was on - * @param {Any} data Data attached to the token - * @param {Integer} bits If the type is a float or integer the bit width - */ - constructor(type, line, data) { - this.type_ = type; - this.line_ = line; - this.data_ = data; - this.bits_ = 0; - } - - get type() { return this.type_; } - get line() { return this.line_; } - - get data() { return this.data_; } - set data(val) { this.data_ = val; } - - get bits() { return this.bits_; } - set bits(val) { this.bits_ = val; } -} - -export {Token, TokenType};
diff --git a/tools/sva/tests/empty_main.spv_asm b/tools/sva/tests/empty_main.spv_asm deleted file mode 100644 index ad6e64b..0000000 --- a/tools/sva/tests/empty_main.spv_asm +++ /dev/null
@@ -1,18 +0,0 @@ -; SPIR-V -; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 7 -; Bound: 6 -; Schema: 0 - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 440 - OpName %main "main" - %void = OpTypeVoid - %3 = OpTypeFunction %void - %main = OpFunction %void None %3 - %5 = OpLabel - OpReturn - OpFunctionEnd
diff --git a/tools/sva/tests/index.html b/tools/sva/tests/index.html deleted file mode 100644 index dd02847..0000000 --- a/tools/sva/tests/index.html +++ /dev/null
@@ -1,23 +0,0 @@ -<!doctype html> -<html> -<head> - <meta charset='utf-8'> -</head> -<body> - <pre id='code'><code></code></pre> - - <script type="module"> - let c = document.getElementById('code'); - - import SVA from "/build/sva.js"; - - let assembly = SVA.assemble("OpCapability Shader"); - if (typeof assembly === "string") { - c.innerText = assembly; - } else { - c.innerText = Array.from(assembly) - .map(b => b.toString(16).padStart(8, "0")).join(" "); - } - </script> -</body> -</html>
diff --git a/tools/sva/tests/simple.spv_asm b/tools/sva/tests/simple.spv_asm deleted file mode 100644 index b4b3f67..0000000 --- a/tools/sva/tests/simple.spv_asm +++ /dev/null
@@ -1,30 +0,0 @@ -; SPIR-V -; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 7 -; Bound: 14 -; Schema: 0 - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %gl_FragColor - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 330 - OpName %main "main" - OpName %gl_FragColor "gl_FragColor" - OpDecorate %gl_FragColor Location 0 - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -%float_0_400000006 = OpConstant %float 0.400000006 -%float_0_800000012 = OpConstant %float 0.800000012 - %float_1 = OpConstant %float 1 - %13 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1 - %main = OpFunction %void None %3 - %5 = OpLabel - OpStore %gl_FragColor %13 - OpReturn - OpFunctionEnd -
diff --git a/tools/sva/tools/process_grammar.rb b/tools/sva/tools/process_grammar.rb deleted file mode 100755 index 1bbff68..0000000 --- a/tools/sva/tools/process_grammar.rb +++ /dev/null
@@ -1,119 +0,0 @@ -#!/usr/bin/env ruby - -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'json' - -GRAMMAR = "../../external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json" -GLSL = "../../external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json" - -CAPABILITIES = %w( - Matrix - Shader - Sampled1D - Image1D - DerivativeControl - ImageQuery - VulkanMemoryModel -) - -g = JSON.parse(File.open(GRAMMAR).read) -magic = g['magic_number'] -vers = [g['major_version'], g['minor_version']] -instructions = {} - -g['instructions'].each do |inst| - if (inst.has_key?('capabilities')) - skip = true - inst['capabilities'].each do |cap| - if CAPABILITIES.include?(cap) - skip = false - break - end - end - next if skip - end - - op = { - opcode: inst['opcode'], - operands: [] - } - - if !inst['operands'].nil? - inst['operands'].each do |operand| - operand.delete('name') - op[:operands] << operand - end - end - - instructions[inst['opname']] = op -end - -operand_kinds = {} -g['operand_kinds'].each do |op_kind| - next if op_kind['category'] !~ /Enum/ - - kind = { - type: op_kind['category'], - values: {} - } - - op_kind['enumerants'].each do |enum| - if (enum.has_key?('capabilities')) - skip = true - enum['capabilities'].each do |cap| - if CAPABILITIES.include?(cap) - skip = false - break - end - end - next if skip - end - - v = if op_kind['category'] == 'BitEnum' - enum['value'].to_i(16) - else - enum['value'].to_i - end - params = [] - if enum.has_key?('parameters') - enum['parameters'].each do |param| - params << param['kind'] - end - end - kind[:values][enum['enumerant']] = {value: v} - kind[:values][enum['enumerant']][:params] = params unless params.empty? - end - - next if kind[:values].empty? - operand_kinds[op_kind['kind']] = kind -end - -# We only support GLSL extensions at the moment. -ext = {} -glsl = JSON.parse(File.open(GLSL).read) -glsl['instructions'].each do |inst| - ext[inst['opname']] = inst['opcode'] -end - -puts "/*#{g['copyright'].join("\n")}*/" -puts "\n// THIS FILE IS GENERATED WITH tools/process_grammar.rb\n\n" -puts "export default " + JSON.pretty_generate({ - magic: magic, - version: vers, - instructions: instructions, - operand_kinds: operand_kinds, - ext: ext -})
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock deleted file mode 100644 index 38788e2..0000000 --- a/tools/sva/yarn.lock +++ /dev/null
@@ -1,1474 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.4.0": - version "4.5.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" - integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== - -"@eslint/eslintrc@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" - integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.5.2" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.41.0": - version "8.41.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.41.0.tgz#080321c3b68253522f7646b55b577dd99d2950b3" - integrity sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA== - -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@zeit/schemas@2.29.0": - version "2.29.0" - resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.29.0.tgz#a59ae6ebfdf4ddc66a876872dd736baa58b6696c" - integrity sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA== - -accepts@~1.3.5: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -ajv@8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ajv@^6.10.0, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-align@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arch@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== - -arg@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -boxen@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.0.tgz#9e5f8c26e716793fc96edcf7cf754cdf5e3fbf32" - integrity sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg== - dependencies: - ansi-align "^3.0.1" - camelcase "^7.0.0" - chalk "^5.0.1" - cli-boxes "^3.0.0" - string-width "^5.1.2" - type-fest "^2.13.0" - widest-line "^4.0.1" - wrap-ansi "^8.0.1" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -camelcase@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" - integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== - -chai@^4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" - pathval "^1.1.1" - type-detect "^4.0.5" - -chalk-template@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.4.0.tgz#692c034d0ed62436b9062c1707fadcd0f753204b" - integrity sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg== - dependencies: - chalk "^4.1.2" - -chalk@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" - integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== - -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^5.0.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" - integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== - -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== - -chokidar@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -cli-boxes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" - integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== - -clipboardy@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-3.0.0.tgz#f3876247404d334c9ed01b6f269c11d09a5e3092" - integrity sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg== - dependencies: - arch "^2.2.0" - execa "^5.1.1" - is-wsl "^2.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== - -cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4.3.4, debug@^4.1.1, debug@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -deep-eql@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" - integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== - dependencies: - type-detect "^4.0.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== - -eslint@^8.41.0: - version "8.41.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.41.0.tgz#3062ca73363b4714b16dbc1e60f035e6134b6f1c" - integrity sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.3" - "@eslint/js" "8.41.0" - "@humanwhocodes/config-array" "^0.11.8" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.1" - espree "^9.5.2" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - -espree@^9.5.2: - version "9.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" - integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -execa@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fast-url-parser@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== - dependencies: - punycode "^1.3.2" - -fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== - dependencies: - reusify "^1.0.4" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -find-up@5.0.0, find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-func-name@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" - integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== - dependencies: - type-fest "^0.20.2" - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-port-reachable@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-port-reachable/-/is-port-reachable-4.0.0.tgz#dac044091ef15319c8ab2f34604d8794181f8c2d" - integrity sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig== - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -js-yaml@4.1.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -log-symbols@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -loupe@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== - dependencies: - get-func-name "^2.0.0" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.0: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -mocha@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" - integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== - dependencies: - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.4" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.2.0" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "5.0.1" - ms "2.1.3" - nanoid "3.3.3" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - workerpool "6.2.1" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" - integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-is-inside@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-to-regexp@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b" - integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw== - -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -punycode@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== - -punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -range-parser@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== - -rc@^1.0.1, rc@^1.1.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -registry-auth-token@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" - integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA== - dependencies: - rc "^1.0.1" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rollup@^3.29.5: - version "3.29.5" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54" - integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w== - optionalDependencies: - fsevents "~2.3.2" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.0.1, safe-buffer@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -serve-handler@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375" - integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg== - dependencies: - bytes "3.0.0" - content-disposition "0.5.2" - fast-url-parser "1.1.3" - mime-types "2.1.18" - minimatch "3.1.2" - path-is-inside "1.0.2" - path-to-regexp "3.3.0" - range-parser "1.2.0" - -serve@^14.2.0: - version "14.2.0" - resolved "https://registry.yarnpkg.com/serve/-/serve-14.2.0.tgz#3d768e88fa13ad8644f2393599189707176e66b8" - integrity sha512-+HOw/XK1bW8tw5iBilBz/mJLWRzM8XM6MPxL4J/dKzdxq1vfdEWSwhaR7/yS8EJp5wzvP92p1qirysJvnEtjXg== - dependencies: - "@zeit/schemas" "2.29.0" - ajv "8.11.0" - arg "5.0.2" - boxen "7.0.0" - chalk "5.0.1" - chalk-template "0.4.0" - clipboardy "3.0.0" - compression "1.7.4" - is-port-reachable "4.0.0" - serve-handler "6.1.5" - update-check "1.5.4" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-detect@^4.0.0, type-detect@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^2.13.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" - integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== - -update-check@1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.4.tgz#5b508e259558f1ad7dbc8b4b0457d4c9d28c8743" - integrity sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ== - dependencies: - registry-auth-token "3.3.2" - registry-url "3.1.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -widest-line@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" - integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== - dependencies: - string-width "^5.0.1" - -word-wrap@^1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" - integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== - -workerpool@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" - integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/tools/util/flags.cpp b/tools/util/flags.cpp index 11b8967..9c99677 100644 --- a/tools/util/flags.cpp +++ b/tools/util/flags.cpp
@@ -14,6 +14,8 @@ #include "flags.h" +#include <algorithm> +#include <cerrno> #include <cstdlib> #include <cstring> #include <iostream>
diff --git a/tools/val/val.cpp b/tools/val/val.cpp index c75c617..377fd0b 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp
@@ -15,6 +15,7 @@ #include <cassert> #include <cstdio> #include <cstring> +#include <filesystem> #include <iostream> #include <vector> @@ -27,12 +28,15 @@ void print_usage(char* argv0) { std::string target_env_list = spvTargetEnvList(36, 105); printf( - R"(%s - Validate a SPIR-V binary file. + R"(%s - Validate a SPIR-V binary file(s). -USAGE: %s [options] [<filename>] +USAGE: %s [options] [<path>] -The SPIR-V binary is read from <filename>. If no file is specified, -or if the filename is "-", then the binary is read from standard input. +The SPIR-V binary is read from <path>. If no path is specified, +or if the path is "-", then the binary is read from standard input. +The <path> parameter may also specify a directory; in this case, +the tool will recursively process all regular files with the .spv +extension within that directory. NOTE: The validator is a work in progress. @@ -66,6 +70,10 @@ members. --allow-localsizeid Allow use of the LocalSizeId decoration where it would otherwise not be allowed by the target environment. + --allow-offset-texture-operand Allow use of the Offset texture operands where it would otherwise not + be allowed by the target environment. + --allow-vulkan-32-bit-bitwise Allow use of non-32 bit for the Base operand 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. @@ -75,6 +83,53 @@ argv0, argv0, target_env_list.c_str()); } +bool process_single_file(const char* filename, spv_target_env& target_env, + spvtools::ValidatorOptions& options, + bool use_default_msg_consumer) { + std::vector<uint32_t> contents; + if (!ReadBinaryFile(filename, &contents)) return false; + + spvtools::SpirvTools tools(target_env); + + // Use a lambda expression here so filename can be captured. Messages use a + // fairly standard notation of `filename:line`. + auto CLIMessageConsumerWithFilename = + [filename](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + const char* pretty_filename = filename; + if (!filename || 0 == strcmp(filename, "-")) { + pretty_filename = "stdin"; + } + + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: " << pretty_filename << ":" << position.index + << ": " << message << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: " << pretty_filename << ":" << position.index + << ": " << message << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: " << pretty_filename << ":" << position.index + << ": " << message << std::endl; + break; + default: + break; + } + }; + + if (use_default_msg_consumer) { + tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + } else { + tools.SetMessageConsumer(CLIMessageConsumerWithFilename); + } + + return tools.Validate(contents.data(), contents.size(), options); +} + int main(int argc, char** argv) { const char* inFile = nullptr; spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; @@ -111,7 +166,7 @@ printf("%s\n", spvSoftwareVersionDetailsString()); printf( "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " - "%s\n %s\n %s\n %s\n", + "%s\n %s\n %s\n %s %s\n", spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), @@ -124,7 +179,8 @@ spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), spvTargetEnvDescription(SPV_ENV_VULKAN_1_2), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_3)); + spvTargetEnvDescription(SPV_ENV_VULKAN_1_3), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_4)); continue_processing = false; return_code = 0; } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { @@ -160,6 +216,10 @@ options.SetSkipBlockLayout(true); } else if (0 == strcmp(cur_arg, "--allow-localsizeid")) { options.SetAllowLocalSizeId(true); + } else if (0 == strcmp(cur_arg, "--allow-offset-texture-operand")) { + options.SetAllowOffsetTextureOperand(true); + } else if (0 == strcmp(cur_arg, "--allow-vulkan-32-bit-bitwise")) { + options.SetAllowVulkan32BitBitwise(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { options.SetRelaxStructStore(true); } else if (0 == cur_arg[1]) { @@ -192,13 +252,35 @@ return return_code; } - std::vector<uint32_t> contents; - if (!ReadBinaryFile(inFile, &contents)) return 1; + if (inFile && + std::filesystem::is_directory(std::filesystem::status(inFile))) { + const std::filesystem::path dir(inFile); + bool succeed = true; + for (auto const& entry : + std::filesystem::recursive_directory_iterator(dir)) { + if (!entry.is_regular_file()) { + continue; + } - spvtools::SpirvTools tools(target_env); - tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + std::filesystem::path filepath = entry.path(); - bool succeed = tools.Validate(contents.data(), contents.size(), options); + if (filepath.extension() != ".spv") continue; - return !succeed; + // Copy the string, because in C++20 the result type of + // std::filesystem::path::u8string changes type from std::string to + // std::u8string, and the pointer type ends up incompatible. Normalize + // to std::string first via copying. + const auto filepath_u8str = filepath.u8string(); + const std::string filepath_str(filepath_u8str.begin(), + filepath_u8str.end()); + if (!process_single_file(filepath_str.c_str(), target_env, options, + false)) { + succeed = false; + } + } + + return !succeed; + } + + return !process_single_file(inFile, target_env, options, true); }
diff --git a/utils/Table/Context.py b/utils/Table/Context.py new file mode 100755 index 0000000..086ab8d --- /dev/null +++ b/utils/Table/Context.py
@@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#from typing import * +from enum import IntEnum +from typing import Dict, List +from . IndexRange import * +from . StringList import * + + +class Context(): + """ + Contains global tables for strings, and lists-of-strings. + It contains: + + - string_buffer: A list of null-terminated strings. The list is + partitioned into contiguous segments dedicated to a specific + kind of string, e.g.: + - all instruction opcodes + - all extension names + - all aliases for a given enum + - all enum names for a first operand type + - all enum names for a second operand type, etc. + Strings are sorted within each segment. + + - string_total_len: The sum of lengths of strings in string_buffer. + + - strings: Maps a string to an IndexRange indicating where the string + can be found in the (future) concatenation of all strings in the + string_buffer. + + - range_buffer: A dictionary mapping a string kind to a list IndexRange + objects R. Each R is one of: + + - An index range referencing one string as it appears in the + (future) concatenation of all strings in the string_buffer. + In this case R represents a single string. + + - An index range referencing earlier elements in range_buffer[kind] + itself. In this case R represents a list of strings. + + - ranges: A dictionary mapping a string kind and lists-of-strings + to its encoding in the range_buffer array. + + It is a two-level mapping of Python type: + + Dict[str,dict[StringList,IndexRange]] + + where + + ranges[kind][list of strings] = an IndexRange + + The 'kind' string encodes a purpose including: + - opcodes: the list of instruction opcode strings. + - the list of aliases for an opcode, or an enum + - an operand type such as 'SPV_OPERAND_TYPE_DIMENSIONALITY': + the list of operand type names, e.g. '2D', '3D', 'Cube', + 'Rect', etc. in the case of SPV_OPERAND_TYPE_DIMENSIONALITY. + By convention, the 'kind' string should be a singular noun for + the type of object named by each member of the list. + + The IndexRange leaf value encodes a list of strings as in the + second case described for 'range_buffer'. + + """ + def __init__(self) -> None: + self.string_total_len: int = 0 # Sum of lengths of all strings in string_buffer + self.string_buffer: List[str] = [] + self.strings: Dict[str, IndexRange] = {} + self.ir_to_string: Dict[IndexRange, str] = {} # Inverse of self.strings + + self.range_buffer: Dict[str,List[IndexRange]] = {} + # We need StringList here because it's hashable, and so it + # can be used as the key for a dict. + self.ranges: Dict[str,Dict[StringList,IndexRange]] = {} + + def GetString(self, ir: IndexRange) -> str: + if ir in self.ir_to_string: + return self.ir_to_string[ir] + raise Exception("unregistered index range {}".format(str(ir))) + + def AddString(self, s: str) -> IndexRange: + """ + Adds or finds a string in the string_buffer. + Returns its IndexRange. + """ + if s in self.strings: + return self.strings[s] + # Allocate space, including for the terminating null. + s_space: int = len(s) + 1 + ir = IndexRange(self.string_total_len, s_space) + self.strings[s] = ir + self.ir_to_string[ir] = s + self.string_total_len += s_space + self.string_buffer.append(s) + return ir + + def AddStringList(self, kind: str, words: List[str]) -> IndexRange: + """ + Ensures a list of strings is recorded in range_buffer[kind], and + returns its location in the range_buffer[kind]. + As a side effect, also ensures each string in the list is in + the string_buffer. + """ + l = StringList(words) + + entry: Dict[StringList, IndexRange] = self.ranges.get(kind, {}) + if kind not in self.ranges: + self.ranges[kind] = entry + self.range_buffer[kind] = [] + + if l in entry: + return entry[l] + new_ranges = [self.AddString(s) for s in l] + ir = IndexRange(len(self.range_buffer[kind]), len(new_ranges)) + self.range_buffer[kind].extend(new_ranges) + entry[l] = ir + return ir + + def dump(self) -> None: + print("string_total_len: {}".format(self.string_total_len)) + + sbi = 0 + print("string_buffer:") + for sb in self.string_buffer: + print(" {}: '{}'".format(sbi, sb)) + sbi += len(sb) + 1 + print("") + + s = [] + for k,v in self.strings.items(): + s.append("'{}': {}".format(k,str(v))) + print("strings:\n {}\n".format('\n '.join(s))) + + for rbk, rbv in self.range_buffer.items(): + print("range_buffer[{}]:".format(rbk)) + i: int = 0 + for r in rbv: + print(" {} {}: {}".format(rbk, i, str(r))) + i += 1 + print("") + + for rk, rv in self.ranges.items(): + for key,val in rv.items(): + print("ranges[{}][{}]: {}".format(str(rk),str(key), str(val))) + print("") + +
diff --git a/utils/Table/Context_test.py b/utils/Table/Context_test.py new file mode 100755 index 0000000..a168bf7 --- /dev/null +++ b/utils/Table/Context_test.py
@@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from . Context import Context +from . IndexRange import IndexRange +from . StringList import StringList + +class TestCreate(unittest.TestCase): + def test_creation(self) -> None: + x = Context() + self.assertIsInstance(x.string_total_len, int) + self.assertIsInstance(x.string_buffer, list) + self.assertIsInstance(x.strings, dict) + self.assertEqual(x.string_total_len, 0) + self.assertEqual(x.string_buffer, []) + self.assertEqual(x.strings, {}) + +class TestString(unittest.TestCase): + def test_AddString_new(self) -> None: + x = Context() + abc_ir = x.AddString("abc") + self.assertEqual(abc_ir, IndexRange(0,4)) + self.assertEqual(x.string_total_len, 4) + self.assertEqual(x.string_buffer, ["abc"]) + self.assertEqual(x.strings, {"abc": IndexRange(0,4)}) + + qz_ir = x.AddString("qz") + self.assertEqual(qz_ir, IndexRange(4,3)) + self.assertEqual(x.string_total_len, 7) + self.assertEqual(x.string_buffer, ["abc", "qz"]) + self.assertEqual(x.strings, {"abc": IndexRange(0,4), "qz": IndexRange(4,3)}) + + empty_ir = x.AddString("") + self.assertEqual(empty_ir, IndexRange(7,1)) + self.assertEqual(x.string_total_len, 8) + self.assertEqual(x.string_buffer, ["abc", "qz", ""]) + self.assertEqual(x.strings, {"abc": IndexRange(0,4), "qz": IndexRange(4,3), "": IndexRange(7,1)}) + + def test_AddString_idempotent(self) -> None: + x = Context() + abc_ir = x.AddString("abc") + self.assertEqual(abc_ir, IndexRange(0,4)) + self.assertEqual(x.string_total_len, 4) + self.assertEqual(x.string_buffer, ["abc"]) + self.assertEqual(x.strings, {"abc": IndexRange(0,4)}) + + abc_ir = x.AddString("abc") + self.assertEqual(abc_ir, IndexRange(0,4)) + self.assertEqual(x.string_total_len, 4) + self.assertEqual(x.string_buffer, ["abc"]) + self.assertEqual(x.strings, {"abc": IndexRange(0,4)}) + +class TestStringList(unittest.TestCase): + def test_AddStringList_empty(self) -> None: + x = Context() + x_ir = x.AddStringList('x', []) + self.assertEqual(x_ir, IndexRange(0,0)) + self.assertEqual(x.string_buffer, []) + self.assertEqual(x.range_buffer, { 'x': [] }) + self.assertEqual(x.ranges, {'x': {StringList([]): IndexRange(0,0)}}) + + def test_AddgStringList_nonempty(self) -> None: + x = Context() + x_ir = x.AddStringList('x', ["abc", "def"]) + + self.assertEqual(x_ir, IndexRange(0,2)) + self.assertEqual(x.range_buffer, {'x': [IndexRange(0,4), IndexRange(4,4)]}) + self.assertEqual(x.ranges, {'x': {StringList(['abc','def']): IndexRange(0,2)}}) + + def test_AddgStringList_nonempty_idempotent(self) -> None: + x = Context() + x_ir = x.AddStringList('x', ["abc", "def"]) + y_ir = x.AddStringList('x', ["abc", "def"]) + + self.assertEqual(x_ir, IndexRange(0,2)) + self.assertEqual(y_ir, IndexRange(0,2)) + self.assertEqual(x.range_buffer, {'x': [IndexRange(0,4), IndexRange(4,4)]}) + self.assertEqual(x.ranges, {'x': {StringList(['abc','def']): IndexRange(0,2)}}) + + def test_AddgStringList_nonempty_does_not_sort(self) -> None: + x = Context() + x_ir = x.AddStringList('x', ["abc", "def"]) + y_ir = x.AddStringList('x', ["def", "abc"]) + + self.assertEqual(x_ir, IndexRange(0,2)) + self.assertEqual(y_ir, IndexRange(2,2)) + self.assertEqual(x.range_buffer, {'x': [IndexRange(0,4), + IndexRange(4,4), + IndexRange(4,4), + IndexRange(0,4)]}) + self.assertEqual(x.ranges, {'x':{StringList(['abc','def']): IndexRange(0,2), + StringList(['def','abc']): IndexRange(2,2)}}) + + def test_AddgStringList_separate_by_kind(self) -> None: + x = Context() + x_ir = x.AddStringList('x', ["abc", "def"]) + y_ir = x.AddStringList('y', ["ghi", "abc"]) + + self.assertEqual(x_ir, IndexRange(0,2)) + self.assertEqual(y_ir, IndexRange(0,2)) + self.assertEqual(x.range_buffer, + {'x': [IndexRange(0,4), IndexRange(4,4)], + 'y': [IndexRange(8,4), IndexRange(0,4)]}) + self.assertEqual(x.ranges, {'x': {StringList(['abc','def']): IndexRange(0,2)}, + 'y': {StringList(['ghi','abc']): IndexRange(0,2)}}) + +if __name__ == "__main__": + unittest.main()
diff --git a/utils/Table/IndexRange.py b/utils/Table/IndexRange.py new file mode 100755 index 0000000..ba29ee2 --- /dev/null +++ b/utils/Table/IndexRange.py
@@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any + +class IndexRange(): + def __init__(self, first: int, count: int) -> None: + self.first = first + self.count = count + if first < 0: + raise Exception("invalid arg: first {} must be non-negative".format(first)) + if count < 0: + raise Exception("invalid arg: count {} must be non-negative".format(count)) + + def __eq__(self, other: Any) -> bool: + return isinstance(other, IndexRange) and self.first == other.first and self.count == other.count + + def __hash__(self) -> int: + return hash("{} {}".format(self.first, self.count)) + + def __str__(self) -> str: + return "IR({}, {})".format(self.first, self.count)
diff --git a/utils/Table/IndexRange_test.py b/utils/Table/IndexRange_test.py new file mode 100755 index 0000000..16f38e4 --- /dev/null +++ b/utils/Table/IndexRange_test.py
@@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from . IndexRange import IndexRange + +class TestIndexRange(unittest.TestCase): + def test_creation(self) -> None: + x: IndexRange = IndexRange(4,5); + self.assertEqual(x.first, 4) + self.assertEqual(x.count, 5) + + def test_creation_bad_first(self) -> None: + self.assertRaises(Exception, IndexRange, -1, 5) + + def test_creation_bad_count(self) -> None: + self.assertRaises(Exception, IndexRange, 1, -5) + + def test_distinct(self) -> None: + x = IndexRange(4, 5); + y = IndexRange(6, 7); + self.assertNotEqual(x.first, y.first) + self.assertNotEqual(x.count, y.count) + + def test_equality(self) -> None: + self.assertEqual(IndexRange(4,5), IndexRange(4,5)) + self.assertNotEqual(IndexRange(4,5), IndexRange(4,7)) + self.assertNotEqual(IndexRange(7,5), IndexRange(4,5)) + + def test_hash_heuristic(self) -> None: + x = hash(IndexRange(4,5)) + y = hash(IndexRange(4,5)) + z = hash(IndexRange(6,7)) + self.assertEqual(x, y) + self.assertNotEqual(x, z) + +if __name__ == "__main__": + unittest.main()
diff --git a/utils/Table/Operand.py b/utils/Table/Operand.py new file mode 100755 index 0000000..8f5fc49 --- /dev/null +++ b/utils/Table/Operand.py
@@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List + +class Operand(): + def __init__(self, json: dict) -> None: + val = json.get('value',None) + + self._obj = json + + @property + def enumerant(self) -> str: + result = self._obj.get('enumerant', None) + if result is None: + raise Exception("operand needs an enumerant string") + return result + + @property + def value(self) -> int: + val: str|int = self._obj['value'] + if isinstance(val, int): + return val + elif isinstance(val,str): + if val.startswith("0x"): + return int(val, 16) + else: + return int(val, 10) + else: + raise Exception("operand needs a value integer or string") + + @property + def capabilities(self) -> List[str]: + return self._obj.get('capabilities',[]) + + @property + def extensions(self) -> List[str]: + return self._obj.get('extensions',[]) + + @property + def aliases(self) -> List[str]: + return self._obj.get('aliases',[]) + + @property + def parameters(self) -> List[dict]: + return self._obj.get('parameters',[]) + + @property + def version(self): # -> str | None # Invald in Python 3.8 + return self._obj.get('version',None) + + @property + def lastVersion(self): # -> str | None # Invalid in Python 3.8 + return self._obj.get('lastVersion',None)
diff --git a/utils/Table/Operand_test.py b/utils/Table/Operand_test.py new file mode 100755 index 0000000..30a197c --- /dev/null +++ b/utils/Table/Operand_test.py
@@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from . Operand import * + +class TestOperand(unittest.TestCase): + def test_enumerant(self) -> None: + x = Operand({'enumerant': 'abc'}); + self.assertEqual(x.enumerant, 'abc'); + + def test_value_decimal(self) -> None: + x = Operand({'value': 123}); + self.assertEqual(x.value, 123); + + def test_value_str_hex(self) -> None: + x = Operand({'value': "0x0101"}); + self.assertEqual(x.value, 257); + + def test_value_str_dec(self) -> None: + x = Operand({'value': "0101"}); + self.assertEqual(x.value, 101); + + def test_value_str_invalid_dec(self) -> None: + x = Operand({'value': "01ab"}); + self.assertRaises(Exception, lambda y: x.value, 0); + + def test_value_str_invalid_hex(self) -> None: + x = Operand({'value': "0x010j"}); + self.assertRaises(Exception, lambda y: x.value, 0); + + def test_capabilities_absent(self) -> None: + x = Operand({}); + self.assertEqual(x.capabilities, []); + + def test_capabilities_present(self) -> None: + x = Operand({'capabilities': ['abc', 'def']}); + self.assertEqual(x.capabilities, ['abc', 'def']); + + def test_extensions_absent(self) -> None: + x = Operand({}); + self.assertEqual(x.extensions, []); + + def test_extensions_present(self) -> None: + x = Operand({'extensions': ['abc', 'def']}); + self.assertEqual(x.extensions, ['abc', 'def']); + + def test_aliases_absent(self) -> None: + x = Operand({}); + self.assertEqual(x.aliases, []); + + def test_aliases_present(self) -> None: + x = Operand({'aliases': ['abc', 'def']}); + self.assertEqual(x.aliases, ['abc', 'def']); + + def test_parameters_absent(self) -> None: + x = Operand({}); + self.assertEqual(x.parameters, []); + + def test_parameters_present(self) -> None: + x = Operand({'parameters': ['abc', 'def']}); + self.assertEqual(x.parameters, ['abc', 'def']); + + def test_version_absent(self) -> None: + x = Operand({}); + self.assertEqual(x.version, None); + + def test_version_present(self) -> None: + x = Operand({'version': '1.0'}); + self.assertEqual(x.version, '1.0'); + + def test_lastVersion_absent(self) -> None: + x = Operand({}); + self.assertEqual(x.lastVersion, None); + + def test_lastVersion_present(self) -> None: + x = Operand({'lastVersion': '1.3'}); + self.assertEqual(x.lastVersion, '1.3'); + + def test_all_propertites(self) -> None: + x = Operand({'enumerant': 'Foobar', + 'value': 12, + 'capabilities': ["yes"], + 'extensions': ["SPV_FOOBAR_baz_bat"], + 'version': "1.0", + 'lastVersion': "1.3", + }); + self.assertEqual(x.enumerant, 'Foobar'); + self.assertEqual(x.value, 12); + self.assertEqual(x.capabilities, ["yes"]); + self.assertEqual(x.extensions, ["SPV_FOOBAR_baz_bat"]); + self.assertEqual(x.version, '1.0'); + self.assertEqual(x.lastVersion, '1.3');
diff --git a/utils/Table/StringList.py b/utils/Table/StringList.py new file mode 100755 index 0000000..0ba166b --- /dev/null +++ b/utils/Table/StringList.py
@@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import functools +from typing import List + +class StringList(list): + """ + A hashable ordered list of strings. + This can be used as the key for a dictionary. + """ + def __init__(self, strs: List[str]) -> None: + super().__init__(strs) + + def __hash__(self) -> int: # type: ignore[override] + return functools.reduce(lambda h, ir: hash((h, hash(ir))), self, 0)
diff --git a/utils/Table/StringList_test.py b/utils/Table/StringList_test.py new file mode 100755 index 0000000..d3cd1ed --- /dev/null +++ b/utils/Table/StringList_test.py
@@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# Copyright 2025 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from . StringList import * + +class TestStringList(unittest.TestCase): + def test_creation_empty(self) -> None: + x = StringList([]) + self.assertEqual(len(x), 0) + self.assertEqual(x, []) + + def test_creation_nonempty(self) -> None: + x = StringList(["abc", "def"]) + self.assertEqual(len(x), 2) + self.assertEqual(x, ["abc", "def"]) + + def test_creation_does_not_sort(self) -> None: + x = StringList(["abc", "def"]) + self.assertEqual(x, ["abc", "def"]) + self.assertNotEqual(x, ["def", "abc"]) + + def test_equality(self) -> None: + x = StringList(["abc", "def"]) + y = StringList(["abc", "def"]) + z = StringList(["abc", "ef"]) + self.assertEqual(x, x) + self.assertEqual(x, y) + self.assertNotEqual(x, z) + + def test_hash_heuristic(self) -> None: + x = StringList(["abc", "def"]) + y = StringList(["abc", "def"]) + z = StringList(["abc", "df"]) + self.assertEqual(hash(x), hash(x)) + self.assertEqual(hash(x), hash(y)) + self.assertNotEqual(hash(x), hash(z)) + +if __name__ == "__main__": + unittest.main()
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/utils/Table/__init__.py similarity index 73% copy from kokoro/check-format/presubmit_check_format.cfg copy to utils/Table/__init__.py index 1993289..2210b4e 100644 --- a/kokoro/check-format/presubmit_check_format.cfg +++ b/utils/Table/__init__.py
@@ -1,16 +1,18 @@ -# Copyright (c) 2018 Google LLC. -# +#!/usr/bin/env python3 +# Copyright 2025 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 +# 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/check-format/build.sh" +__all__ = [ + "Context", + "IndexRange", +]
diff --git a/utils/check_code_format.sh b/utils/check_code_format.sh index da5e019..96db03f 100755 --- a/utils/check_code_format.sh +++ b/utils/check_code_format.sh
@@ -12,13 +12,33 @@ # 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. -# -# Script to determine if source code in Pull Request is properly formatted. -# Exits with non 0 exit code if formatting is needed. -# -# This script assumes to be invoked at the project root directory. -BASE_BRANCH=${1:-main} +# This script determines if the source code in a Pull Request is properly formatted. +# Exits with non 0 exit code if formatting is needed. +# Assumptions: +# - git and python3 are on the path +# - Runs from the project root diretory. +# - 'clang-format' is on the path, or env var CLANG_FORMAT points to it. +# - 'clang-format-diff.py' is in the utils directory, or env var +# points to it.CLANG_FORMAT_DIFF + +BASE_BRANCH=$(git merge-base main HEAD) + +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} +if [ ! -f "$CLANG_FORMAT" ]; then + echo missing clang-format: set CLANG_FORMAT or put clang-format in the PATH + exit 1 +fi + +# Find clang-format-diff.py from an environment variable, or use a default +CLANG_FORMAT_DIFF=${CLANG_FORMAT_DIFF:-./utils/clang-format-diff.py} +if [ ! -f "$CLANG_FORMAT_DIFF" ]; then + echo missing clang-format-diffy.py: set CLANG_FORMAT_DIFF or put it in ./utils/clang-format-diff.py + exit 1 +fi + +echo "Comparing "$(git rev-parse HEAD)" against $BASE_BRANCH" +echo Using $("$CLANG_FORMAT" --version) FILES_TO_CHECK=$(git diff --name-only ${BASE_BRANCH} | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$") @@ -26,8 +46,11 @@ echo "No source code to check for formatting." exit 0 fi +echo "Checking Formatting for the following files" +echo "${FILES_TO_CHECK}" +echo -FORMAT_DIFF=$(git diff -U0 ${BASE_BRANCH} -- ${FILES_TO_CHECK} | python ./utils/clang-format-diff.py -p1 -style=file) +FORMAT_DIFF=$(git diff -U0 ${BASE_BRANCH} -- ${FILES_TO_CHECK} | python3 "${CLANG_FORMAT_DIFF}" -p1 -style=file -binary "$CLANG_FORMAT") if [ -z "${FORMAT_DIFF}" ]; then echo "All source code in PR properly formatted."
diff --git a/utils/check_copyright.py b/utils/check_copyright.py index c477ece..ca4a218 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py
@@ -37,6 +37,7 @@ 'André Perez Maselco', 'Vasyl Teliman', 'Advanced Micro Devices, Inc.', + 'Arm Ltd.', 'Stefano Milizia', 'Alastair F. Donaldson', 'Mostafa Ashraf', @@ -45,7 +46,7 @@ 'Nintendo', 'Epic Games, Inc.', 'NVIDIA Corporation'] -CURRENT_YEAR = 2023 +CURRENT_YEAR = 2025 FIRST_YEAR = 2014 FINAL_YEAR = CURRENT_YEAR + 5 @@ -65,7 +66,7 @@ YEAR_OR_RANGE_REGEX = '(' + YEAR_REGEX + '|' + YEAR_RANGE_REGEX + ')' # The final regular expression to match a valid copyright line. -COPYRIGHT_RE = re.compile('Copyright \(c\) {} ({})'.format( +COPYRIGHT_RE = re.compile('Copyright( \\(c\\))? {} ({})'.format( YEAR_OR_RANGE_REGEX, '|'.join(AUTHORS))) MIT_BEGIN_RE = re.compile('Permission is hereby granted, ' @@ -73,7 +74,7 @@ MIT_END_RE = re.compile('MATERIALS OR THE USE OR OTHER DEALINGS IN ' 'THE MATERIALS.') APACHE2_BEGIN_RE = re.compile('Licensed under the Apache License, ' - 'Version 2.0 \(the "License"\);') + 'Version 2.0 \\(the "License"\\);') APACHE2_END_RE = re.compile('limitations under the License.') LICENSED = """Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py deleted file mode 100755 index 2b695b7..0000000 --- a/utils/generate_grammar_tables.py +++ /dev/null
@@ -1,963 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2016 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. -"""Generates various info tables from SPIR-V JSON grammar.""" - -import errno -import json -import os.path -import re - -# Prefix for all C variables generated by this script. -PYGEN_VARIABLE_PREFIX = 'pygen_variable' - -# Extensions to recognize, but which don't necessarily come from the SPIR-V -# core or KHR grammar files. Get this list from the SPIR-V registry web page. -# NOTE: Only put things on this list if it is not in those grammar files. -EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ -SPV_AMD_gcn_shader -SPV_AMD_gpu_shader_half_float -SPV_AMD_gpu_shader_int16 -SPV_AMD_shader_trinary_minmax -SPV_KHR_non_semantic_info -SPV_EXT_relaxed_printf_string_address_space -""" - -OUTPUT_LANGUAGE = 'c' - -def make_path_to_file(f): - """Makes all ancestor directories to the given file, if they don't yet - exist. - - Arguments: - f: The file whose ancestor directories are to be created. - """ - dir = os.path.dirname(os.path.abspath(f)) - try: - os.makedirs(dir) - except OSError as e: - if e.errno == errno.EEXIST and os.path.isdir(dir): - pass - else: - raise - - -def convert_min_required_version(version): - """Converts the minimal required SPIR-V version encoded in the grammar to - the symbol in SPIRV-Tools.""" - if version is None: - return 'SPV_SPIRV_VERSION_WORD(1, 0)' - if version == 'None': - return '0xffffffffu' - return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) - - -def convert_max_required_version(version): - """Converts the maximum required SPIR-V version encoded in the grammar to - the symbol in SPIRV-Tools.""" - if version is None: - return '0xffffffffu' - return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) - -def get_alias_array_name(aliases): - """Returns the name of the array containing all the given aliases. - - Arguments: - - aliases: a sequence of alias names - """ - if not aliases: - return 'nullptr'; - return '{}_aliases_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(aliases)) - -def compose_alias_list(aliases): - """Returns a string containing a braced list of aliases. - - Arguments: - - aliases: a sequence of alias names - - Returns: - a string containing the braced list of char* named by aliases. - """ - return '{' + ', '.join([('"{}"').format(a) for a in aliases]) + '}' - -def generate_aliases_arrays(aliases): - """Returns the arrays of aliases - - Arguments: - - aliases: a sequence of sequence of alias names - """ - aliases = sorted(set([tuple(a) for a in aliases if a])) - arrays = [ - 'static const char* {}[] = {};'.format( - get_alias_array_name(a), compose_alias_list(a)) - for a in aliases] - return '\n'.join(arrays) - -def compose_capability_list(caps): - """Returns a string containing a braced list of capabilities as enums. - - Arguments: - - caps: a sequence of capability names - - Returns: - a string containing the braced list of SpvCapability* or spv::Capability:: enums named by caps. - """ - base_string = 'SpvCapability' - global OUTPUT_LANGUAGE - if OUTPUT_LANGUAGE == 'c++': - base_string = 'spv::Capability::' - - return '{' + ', '.join([(base_string + '{}').format(c) for c in caps]) + '}' - - -def get_capability_array_name(caps): - """Returns the name of the array containing all the given capabilities. - - Args: - - caps: a sequence of capability names - """ - if not caps: - return 'nullptr' - return '{}_caps_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(caps)) - - -def generate_capability_arrays(caps): - """Returns the arrays of capabilities. - - Arguments: - - caps: a sequence of sequence of capability names - """ - caps = sorted(set([tuple(c) for c in caps if c])) - cap_str = 'SpvCapability' - global OUTPUT_LANGUAGE - if OUTPUT_LANGUAGE == 'c++': - cap_str = 'spv::Capability' - arrays = [ - 'static const ' + cap_str + ' {}[] = {};'.format( - get_capability_array_name(c), compose_capability_list(c)) - for c in caps] - return '\n'.join(arrays) - - -def compose_extension_list(exts): - """Returns a string containing a braced list of extensions as enums. - - Arguments: - - exts: a sequence of extension names - - Returns: - a string containing the braced list of extensions named by exts. - """ - return '{' + ', '.join( - ['spvtools::Extension::k{}'.format(e) for e in exts]) + '}' - - -def get_extension_array_name(extensions): - """Returns the name of the array containing all the given extensions. - - Args: - - extensions: a sequence of extension names - """ - if not extensions: - return 'nullptr' - else: - return '{}_exts_{}'.format( - PYGEN_VARIABLE_PREFIX, ''.join(extensions)) - - -def generate_extension_arrays(extensions): - """Returns the arrays of extensions. - - Arguments: - - caps: a sequence of sequence of extension names - """ - extensions = sorted(set([tuple(e) for e in extensions if e])) - arrays = [ - 'static const spvtools::Extension {}[] = {};'.format( - get_extension_array_name(e), compose_extension_list(e)) - for e in extensions] - return '\n'.join(arrays) - - -def convert_operand_kind(operand_tuple): - """Returns the corresponding operand type used in spirv-tools for the given - operand kind and quantifier used in the JSON grammar. - - Arguments: - - operand_tuple: a tuple of two elements: - - operand kind: used in the JSON grammar - - quantifier: '', '?', or '*' - - Returns: - a string of the enumerant name in spv_operand_type_t - """ - kind, quantifier = operand_tuple - # The following cases are where we differ between the JSON grammar and - # spirv-tools. - if kind == 'IdResultType': - kind = 'TypeId' - elif kind == 'IdResult': - kind = 'ResultId' - elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics': - kind = 'MemorySemanticsId' - elif kind == 'IdScope' or kind == 'Scope': - kind = 'ScopeId' - elif kind == 'IdRef': - kind = 'Id' - - elif kind == 'ImageOperands': - kind = 'Image' - elif kind == 'Dim': - kind = 'Dimensionality' - elif kind == 'ImageFormat': - kind = 'SamplerImageFormat' - elif kind == 'KernelEnqueueFlags': - kind = 'KernelEnqFlags' - - elif kind == 'LiteralExtInstInteger': - kind = 'ExtensionInstructionNumber' - elif kind == 'LiteralSpecConstantOpInteger': - kind = 'SpecConstantOpNumber' - elif kind == 'LiteralContextDependentNumber': - kind = 'TypedLiteralNumber' - - elif kind == 'PairLiteralIntegerIdRef': - kind = 'LiteralIntegerId' - elif kind == 'PairIdRefLiteralInteger': - kind = 'IdLiteralInteger' - elif kind == 'PairIdRefIdRef': # Used by OpPhi in the grammar - kind = 'Id' - - if kind == 'FPRoundingMode': - kind = 'FpRoundingMode' - elif kind == 'FPFastMathMode': - kind = 'FpFastMathMode' - - if quantifier == '?': - kind = 'Optional{}'.format(kind) - elif quantifier == '*': - kind = 'Variable{}'.format(kind) - - return 'SPV_OPERAND_TYPE_{}'.format( - re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper()) - - -class InstInitializer(object): - """Instances holds a SPIR-V instruction suitable for printing as the - initializer for spv_opcode_desc_t.""" - - def __init__(self, opname, aliases, caps, exts, operands, version, lastVersion): - """Initialization. - - Arguments: - - opname: opcode name (with the 'Op' prefix) - - aliases: a sequence of aliases for the name of this opcode - - caps: a sequence of capability names required by this opcode - - exts: a sequence of names of extensions enabling this enumerant - - operands: a sequence of (operand-kind, operand-quantifier) tuples - - version: minimal SPIR-V version required for this opcode - - lastVersion: last version of SPIR-V that includes this opcode - """ - - assert opname.startswith('Op') - self.opname = opname[2:] # Remove the "Op" prefix. - self.num_aliases = len(aliases); - self.aliases_mask = get_alias_array_name(aliases) - self.num_caps = len(caps) - self.caps_mask = get_capability_array_name(caps) - self.num_exts = len(exts) - self.exts = get_extension_array_name(exts) - self.operands = [convert_operand_kind(o) for o in operands] - - self.fix_syntax() - - operands = [o[0] for o in operands] - self.ref_type_id = 'IdResultType' in operands - self.def_result_id = 'IdResult' in operands - - self.version = convert_min_required_version(version) - self.lastVersion = convert_max_required_version(lastVersion) - - def fix_syntax(self): - """Fix an instruction's syntax, adjusting for differences between the - officially released grammar and how SPIRV-Tools uses the grammar. - - Fixes: - - ExtInst should not end with SPV_OPERAND_VARIABLE_ID. - https://github.com/KhronosGroup/SPIRV-Tools/issues/233 - """ - if (self.opname == 'ExtInst' - and self.operands[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID'): - self.operands.pop() - - def __str__(self): - global OUTPUT_LANGUAGE - base_str = 'SpvOp' - if OUTPUT_LANGUAGE == 'c++': - base_str = 'spv::Op::Op' - - template = ['{{"{opname}"', base_str + '{opname}', - '{num_aliases}', '{aliases_mask}', - '{num_caps}', '{caps_mask}', - '{num_operands}', '{{{operands}}}', - '{def_result_id}', '{ref_type_id}', - '{num_exts}', '{exts}', - '{min_version}', '{max_version}}}'] - return ', '.join(template).format( - opname=self.opname, - num_aliases=self.num_aliases, - aliases_mask=self.aliases_mask, - num_caps=self.num_caps, - caps_mask=self.caps_mask, - num_operands=len(self.operands), - operands=', '.join(self.operands), - def_result_id=(1 if self.def_result_id else 0), - ref_type_id=(1 if self.ref_type_id else 0), - num_exts=self.num_exts, - exts=self.exts, - min_version=self.version, - max_version=self.lastVersion) - - -class ExtInstInitializer(object): - """Instances holds a SPIR-V extended instruction suitable for printing as - the initializer for spv_ext_inst_desc_t.""" - - def __init__(self, opname, opcode, caps, operands): - """Initialization. - - Arguments: - - opname: opcode name - - opcode: enumerant value for this opcode - - caps: a sequence of capability names required by this opcode - - operands: a sequence of (operand-kind, operand-quantifier) tuples - """ - self.opname = opname - self.opcode = opcode - self.num_caps = len(caps) - self.caps_mask = get_capability_array_name(caps) - self.operands = [convert_operand_kind(o) for o in operands] - self.operands.append('SPV_OPERAND_TYPE_NONE') - - def __str__(self): - template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}', - '{{{operands}}}}}'] - return ', '.join(template).format( - opname=self.opname, - opcode=self.opcode, - num_caps=self.num_caps, - caps_mask=self.caps_mask, - operands=', '.join(self.operands)) - - -def generate_instruction(inst, is_ext_inst): - """Returns the C initializer for the given SPIR-V instruction. - - Arguments: - - inst: a dict containing information about a SPIR-V instruction - - is_ext_inst: a bool indicating whether |inst| is an extended - instruction. - - Returns: - a string containing the C initializer for spv_opcode_desc_t or - spv_ext_inst_desc_t - """ - opname = inst.get('opname') - opcode = inst.get('opcode') - aliases = inst.get('aliases', []) - caps = inst.get('capabilities', []) - exts = inst.get('extensions', []) - operands = inst.get('operands', {}) - operands = [(o['kind'], o.get('quantifier', '')) for o in operands] - min_version = inst.get('version', None) - max_version = inst.get('lastVersion', None) - - assert opname is not None - - if is_ext_inst: - return str(ExtInstInitializer(opname, opcode, caps, operands)) - else: - return str(InstInitializer(opname, aliases, caps, exts, operands, min_version, max_version)) - - -def generate_instruction_table(inst_table): - """Returns the info table containing all SPIR-V instructions, sorted by - opcode, and prefixed by capability arrays. - - Note: - - the built-in sorted() function is guaranteed to be stable. - https://docs.python.org/3/library/functions.html#sorted - - Arguments: - - inst_table: a list containing all SPIR-V instructions. - """ - inst_table = sorted(inst_table, key=lambda k: (k['opcode'], k['opname'])) - - aliases_arrays = generate_aliases_arrays( - [inst.get('aliases', []) for inst in inst_table]) - caps_arrays = generate_capability_arrays( - [inst.get('capabilities', []) for inst in inst_table]) - exts_arrays = generate_extension_arrays( - [inst.get('extensions', []) for inst in inst_table]) - - insts = [generate_instruction(inst, False) for inst in inst_table] - insts = ['static const spv_opcode_desc_t kOpcodeTableEntries[] = {{\n' - ' {}\n}};'.format(',\n '.join(insts))] - - return '{}\n\n{}\n\n{}\n\n{}'.format(aliases_arrays, caps_arrays, exts_arrays, '\n'.join(insts)) - - -def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""): - """Returns the info table containing all SPIR-V extended instructions, - sorted by opcode, and prefixed by capability arrays. - - Arguments: - - inst_table: a list containing all SPIR-V instructions. - - set_name: the name of the extended instruction set. - - operand_kind_prefix: the prefix, if any, to add to the front - of operand kind names. - """ - if operand_kind_prefix: - prefix_operand_kind_names(operand_kind_prefix, json_grammar) - - inst_table = json_grammar["instructions"] - set_name = set_name.replace(".", "_") - - inst_table = sorted(inst_table, key=lambda k: k['opcode']) - caps = [inst.get('capabilities', []) for inst in inst_table] - caps_arrays = generate_capability_arrays(caps) - insts = [generate_instruction(inst, True) for inst in inst_table] - insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n' - ' {}\n}};'.format(set_name, ',\n '.join(insts))] - - return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) - - -class EnumerantInitializer(object): - """Prints an enumerant as the initializer for spv_operand_desc_t.""" - - def __init__(self, enumerant, value, aliases, caps, exts, parameters, version, lastVersion): - """Initialization. - - Arguments: - - enumerant: enumerant name - - value: enumerant value - - aliases: a sequence of aliased capability names - - caps: a sequence of capability names required by this enumerant - - exts: a sequence of names of extensions enabling this enumerant - - parameters: a sequence of (operand-kind, operand-quantifier) tuples - - version: minimal SPIR-V version required for this opcode - - lastVersion: last SPIR-V version this opode appears - """ - self.enumerant = enumerant - self.value = value - self.num_aliases = len(aliases) - self.aliases = get_alias_array_name(aliases) - self.num_caps = len(caps) - self.caps = get_capability_array_name(caps) - self.num_exts = len(exts) - self.exts = get_extension_array_name(exts) - self.parameters = [convert_operand_kind(p) for p in parameters] - self.version = convert_min_required_version(version) - self.lastVersion = convert_max_required_version(lastVersion) - - def __str__(self): - template = ['{{"{enumerant}"', '{value}', - '{num_aliases}', '{aliases}', - '{num_caps}', '{caps}', - '{num_exts}', '{exts}', - '{{{parameters}}}', '{min_version}', - '{max_version}}}'] - return ', '.join(template).format( - enumerant=self.enumerant, - value=self.value, - num_aliases=self.num_aliases, - aliases=self.aliases, - num_caps=self.num_caps, - caps=self.caps, - num_exts=self.num_exts, - exts=self.exts, - parameters=', '.join(self.parameters), - min_version=self.version, - max_version=self.lastVersion) - - -def generate_enum_operand_kind_entry(entry, extension_map): - """Returns the C initializer for the given operand enum entry. - - Arguments: - - entry: a dict containing information about an enum entry - - extension_map: a dict mapping enum value to list of extensions - - Returns: - a string containing the C initializer for spv_operand_desc_t - """ - enumerant = entry.get('enumerant') - value = entry.get('value') - aliases = entry.get('aliases', []) - caps = entry.get('capabilities', []) - if value in extension_map: - exts = extension_map[value] - else: - exts = [] - params = entry.get('parameters', []) - params = [p.get('kind') for p in params] - params = zip(params, [''] * len(params)) - version = entry.get('version', None) - max_version = entry.get('lastVersion', None) - - assert enumerant is not None - assert value is not None - - return str(EnumerantInitializer( - enumerant, value, aliases, caps, exts, params, version, max_version)) - - -def generate_enum_operand_kind(enum, synthetic_exts_list): - """Returns the C definition for the given operand kind. - It's a static const named array of spv_operand_desc_t. - - Also appends to |synthetic_exts_list| a list of extension lists - used. - """ - kind = enum.get('kind') - assert kind is not None - - # Sort all enumerants according to their values, but otherwise - # preserve their order so the first name listed in the grammar - # as the preferred name for disassembly. - if enum.get('category') == 'ValueEnum': - def functor(k): return (k['value']) - else: - def functor(k): return (int(k['value'], 16)) - entries = sorted(enum.get('enumerants', []), key=functor) - - # SubgroupEqMask and SubgroupEqMaskKHR are the same number with - # same semantics, but one has no extension list while the other - # does. Both should have the extension list. - # So create a mapping from enum value to the union of the extensions - # across all those grammar entries. Preserve order. - extension_map = {} - for e in entries: - value = e.get('value') - extension_map[value] = [] - for e in entries: - value = e.get('value') - exts = e.get('extensions', []) - for ext in exts: - if ext not in extension_map[value]: - extension_map[value].append(ext) - synthetic_exts_list.extend(extension_map.values()) - - name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind) - entries = [' {}'.format(generate_enum_operand_kind_entry(e, extension_map)) - for e in entries] - if len(entries) == 0: - # Insert a dummy entry. Otherwise the array is empty and compilation - # will fail in MSVC. - entries = [' {"place holder", 0, 0, nullptr, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(999,0), 0}'] - - template = ['static const spv_operand_desc_t {name}[] = {{', - '{entries}', '}};'] - entries = '\n'.join(template).format( - name=name, - entries=',\n'.join(entries)) - - return kind, name, entries - - -def generate_operand_kind_table(enums): - """Returns the info table containing all SPIR-V operand kinds.""" - # We only need to output info tables for those operand kinds that are enums. - enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']] - - aliases = [entry.get('aliases', []) - for enum in enums - for entry in enum.get('enumerants', [])] - aliases_arrays = generate_aliases_arrays(aliases) - - caps = [entry.get('capabilities', []) - for enum in enums - for entry in enum.get('enumerants', [])] - caps_arrays = generate_capability_arrays(caps) - - exts = [entry.get('extensions', []) - for enum in enums - for entry in enum.get('enumerants', [])] - enums = [generate_enum_operand_kind(e, exts) for e in enums] - exts_arrays = generate_extension_arrays(exts) - - # We have a few operand kinds that require their optional counterpart to - # exist in the operand info table. - optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat', 'CooperativeMatrixOperands', 'RawAccessChainOperands', 'FPEncoding'] - optional_enums = [e for e in enums if e[0] in optional_enums] - enums.extend(optional_enums) - - enum_kinds, enum_names, enum_entries = zip(*enums) - # Mark the last few as optional ones. - enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums) - # And we don't want redefinition of them. - enum_entries = enum_entries[:-len(optional_enums)] - enum_kinds = [convert_operand_kind(e) - for e in zip(enum_kinds, enum_quantifiers)] - table_entries = zip(enum_kinds, enum_names, enum_names) - table_entries = [' {{{}, ARRAY_SIZE({}), {}}}'.format(*e) - for e in table_entries] - - template = [ - 'static const spv_operand_desc_group_t {p}_OperandInfoTable[] = {{', - '{enums}', '}};'] - table = '\n'.join(template).format( - p=PYGEN_VARIABLE_PREFIX, enums=',\n'.join(table_entries)) - - return '\n\n'.join((aliases_arrays,) + (caps_arrays,) + (exts_arrays,) + enum_entries + (table,)) - - -def get_extension_list(instructions, operand_kinds): - """Returns extensions as an alphabetically sorted list of strings.""" - - things_with_an_extensions_field = [item for item in instructions] - - enumerants = sum([item.get('enumerants', []) - for item in operand_kinds], []) - - things_with_an_extensions_field.extend(enumerants) - - extensions = sum([item.get('extensions', []) - for item in things_with_an_extensions_field - if item.get('extensions')], []) - - for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split(): - # If it's already listed in a grammar, then don't put it in the - # special exceptions list. - assert item not in extensions, 'Extension %s is already in a grammar file' % item - - extensions.extend( - EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split()) - - # Validator would ignore type declaration unique check. Should only be used - # for legacy autogenerated test files containing multiple instances of the - # same type declaration, if fixing the test by other methods is too - # difficult. Shouldn't be used for any other reasons. - extensions.append('SPV_VALIDATOR_ignore_type_decl_unique') - - return sorted(set(extensions)) - - -def get_capabilities(operand_kinds): - """Returns capabilities as a list of JSON objects, in order of - appearance.""" - enumerants = sum([item.get('enumerants', []) for item in operand_kinds - if item.get('kind') in ['Capability']], []) - return enumerants - - -def generate_extension_enum(extensions): - """Returns enumeration containing extensions declared in the grammar.""" - return ',\n'.join(['k' + extension for extension in extensions]) - - -def generate_extension_to_string_mapping(extensions): - """Returns mapping function from extensions to corresponding strings.""" - function = 'const char* ExtensionToString(Extension extension) {\n' - function += ' switch (extension) {\n' - template = ' case Extension::k{extension}:\n' \ - ' return "{extension}";\n' - function += ''.join([template.format(extension=extension) - for extension in extensions]) - function += ' }\n\n return "";\n}' - return function - - -def generate_string_to_extension_mapping(extensions): - """Returns mapping function from strings to corresponding extensions.""" - - function = ''' - bool GetExtensionFromString(const char* str, Extension* extension) {{ - static const char* known_ext_strs[] = {{ {strs} }}; - static const Extension known_ext_ids[] = {{ {ids} }}; - const auto b = std::begin(known_ext_strs); - const auto e = std::end(known_ext_strs); - const auto found = std::equal_range( - b, e, str, [](const char* str1, const char* str2) {{ - return std::strcmp(str1, str2) < 0; - }}); - if (found.first == e || found.first == found.second) return false; - - *extension = known_ext_ids[found.first - b]; - return true; - }} - '''.format(strs=', '.join(['"{}"'.format(e) for e in extensions]), - ids=', '.join(['Extension::k{}'.format(e) for e in extensions])) - - return function - - -def generate_capability_to_string_mapping(operand_kinds): - """Returns mapping function from capabilities to corresponding strings. - - We take care to avoid emitting duplicate values. - """ - cap_str = 'SpvCapability' - cap_join = '' - global OUTPUT_LANGUAGE - if OUTPUT_LANGUAGE == 'c++': - cap_str = 'spv::Capability' - cap_join = '::' - - function = 'const char* CapabilityToString(' + cap_str + ' capability) {\n' - function += ' switch (capability) {\n' - template = ' case ' + cap_str + cap_join + '{capability}:\n' \ - ' return "{capability}";\n' - emitted = set() # The values of capabilities we already have emitted - for capability in get_capabilities(operand_kinds): - value = capability.get('value') - if value not in emitted: - emitted.add(value) - function += template.format(capability=capability.get('enumerant')) - function += ' case ' + cap_str + cap_join + 'Max:\n' \ - ' assert(0 && "Attempting to convert ' + cap_str + cap_join + 'Max to string");\n' \ - ' return "";\n' - function += ' }\n\n return "";\n}' - return function - - -def generate_all_string_enum_mappings(extensions, operand_kinds): - """Returns all string-to-enum / enum-to-string mapping tables.""" - tables = [] - tables.append(generate_extension_to_string_mapping(extensions)) - tables.append(generate_string_to_extension_mapping(extensions)) - tables.append(generate_capability_to_string_mapping(operand_kinds)) - return '\n\n'.join(tables) - - -def precondition_operand_kinds(operand_kinds): - """For operand kinds that have the same number, make sure they all have the - same extension list.""" - - # Map operand kind and value to list of the union of extensions - # for same-valued enumerants. - exts = {} - for kind_entry in operand_kinds: - kind = kind_entry.get('kind') - for enum_entry in kind_entry.get('enumerants', []): - value = enum_entry.get('value') - key = kind + '.' + str(value) - if key in exts: - exts[key].extend(enum_entry.get('extensions', [])) - else: - exts[key] = enum_entry.get('extensions', []) - exts[key] = sorted(set(exts[key])) - - # Now make each entry the same list. - for kind_entry in operand_kinds: - kind = kind_entry.get('kind') - for enum_entry in kind_entry.get('enumerants', []): - value = enum_entry.get('value') - key = kind + '.' + str(value) - if len(exts[key]) > 0: - enum_entry['extensions'] = exts[key] - - return operand_kinds - - -def prefix_operand_kind_names(prefix, json_dict): - """Modifies json_dict, by prefixing all the operand kind names - with the given prefix. Also modifies their uses in the instructions - to match. - """ - - old_to_new = {} - for operand_kind in json_dict["operand_kinds"]: - old_name = operand_kind["kind"] - new_name = prefix + old_name - operand_kind["kind"] = new_name - old_to_new[old_name] = new_name - - for instruction in json_dict["instructions"]: - for operand in instruction.get("operands", []): - replacement = old_to_new.get(operand["kind"]) - if replacement is not None: - operand["kind"] = replacement - - -def main(): - import argparse - parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') - - parser.add_argument('--spirv-core-grammar', metavar='<path>', - type=str, required=False, - help='input JSON grammar file for core SPIR-V ' - 'instructions') - parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>', - type=str, required=False, default=None, - help='input JSON grammar file for DebugInfo extended ' - 'instruction set') - parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>', - type=str, required=False, default=None, - help='input JSON grammar file for OpenCL.DebugInfo.100 ' - 'extended instruction set') - parser.add_argument('--extinst-glsl-grammar', metavar='<path>', - type=str, required=False, default=None, - help='input JSON grammar file for GLSL extended ' - 'instruction set') - parser.add_argument('--extinst-opencl-grammar', metavar='<path>', - type=str, required=False, default=None, - help='input JSON grammar file for OpenCL extended ' - 'instruction set') - parser.add_argument('--output-language', - type=str, required=False, default='c', - choices=['c','c++'], - help='specify output language type') - - parser.add_argument('--core-insts-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for core SPIR-V instructions') - parser.add_argument('--glsl-insts-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for GLSL extended instruction set') - parser.add_argument('--opencl-insts-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for OpenCL extended instruction set') - parser.add_argument('--operand-kinds-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for operand kinds') - parser.add_argument('--extension-enum-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for extension enumeration') - parser.add_argument('--enum-string-mapping-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for enum-string mappings') - parser.add_argument('--extinst-vendor-grammar', metavar='<path>', - type=str, required=False, default=None, - help='input JSON grammar file for vendor extended ' - 'instruction set'), - parser.add_argument('--vendor-insts-output', metavar='<path>', - type=str, required=False, default=None, - help='output file for vendor extended instruction set') - parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>', - type=str, required=False, default=None, - help='prefix for operand kinds (to disambiguate operand type enums)') - args = parser.parse_args() - - global OUTPUT_LANGUAGE - OUTPUT_LANGUAGE = args.output_language - - # The GN build system needs this because it doesn't handle quoting - # empty string arguments well. - if args.vendor_operand_kind_prefix == "...nil...": - args.vendor_operand_kind_prefix = "" - - if (args.core_insts_output is None) != \ - (args.operand_kinds_output is None): - print('error: --core-insts-output and --operand-kinds-output ' - 'should be specified together.') - exit(1) - if args.operand_kinds_output and not (args.spirv_core_grammar and - args.extinst_debuginfo_grammar and - args.extinst_cldebuginfo100_grammar): - print('error: --operand-kinds-output requires --spirv-core-grammar ' - 'and --extinst-debuginfo-grammar ' - 'and --extinst-cldebuginfo100-grammar') - exit(1) - if (args.glsl_insts_output is None) != \ - (args.extinst_glsl_grammar is None): - print('error: --glsl-insts-output and --extinst-glsl-grammar ' - 'should be specified together.') - exit(1) - if (args.opencl_insts_output is None) != \ - (args.extinst_opencl_grammar is None): - print('error: --opencl-insts-output and --extinst-opencl-grammar ' - 'should be specified together.') - exit(1) - if (args.vendor_insts_output is None) != \ - (args.extinst_vendor_grammar is None): - print('error: --vendor-insts-output and ' - '--extinst-vendor-grammar should be specified together.') - exit(1) - if all([args.core_insts_output is None, - args.glsl_insts_output is None, - args.opencl_insts_output is None, - args.vendor_insts_output is None, - args.extension_enum_output is None, - args.enum_string_mapping_output is None]): - print('error: at least one output should be specified.') - exit(1) - - if args.spirv_core_grammar is not None: - with open(args.spirv_core_grammar) as json_file: - core_grammar = json.loads(json_file.read()) - with open(args.extinst_debuginfo_grammar) as debuginfo_json_file: - debuginfo_grammar = json.loads(debuginfo_json_file.read()) - with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file: - cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read()) - prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar) - instructions = [] - instructions.extend(core_grammar['instructions']) - instructions.extend(debuginfo_grammar['instructions']) - instructions.extend(cldebuginfo100_grammar['instructions']) - operand_kinds = [] - operand_kinds.extend(core_grammar['operand_kinds']) - operand_kinds.extend(debuginfo_grammar['operand_kinds']) - operand_kinds.extend(cldebuginfo100_grammar['operand_kinds']) - extensions = get_extension_list(instructions, operand_kinds) - operand_kinds = precondition_operand_kinds(operand_kinds) - if args.core_insts_output is not None: - make_path_to_file(args.core_insts_output) - make_path_to_file(args.operand_kinds_output) - with open(args.core_insts_output, 'w') as f: - f.write(generate_instruction_table( - core_grammar['instructions'])) - with open(args.operand_kinds_output, 'w') as f: - f.write(generate_operand_kind_table(operand_kinds)) - if args.extension_enum_output is not None: - make_path_to_file(args.extension_enum_output) - with open(args.extension_enum_output, 'w') as f: - f.write(generate_extension_enum(extensions)) - if args.enum_string_mapping_output is not None: - make_path_to_file(args.enum_string_mapping_output) - with open(args.enum_string_mapping_output, 'w') as f: - f.write(generate_all_string_enum_mappings( - extensions, operand_kinds)) - - if args.extinst_glsl_grammar is not None: - with open(args.extinst_glsl_grammar) as json_file: - grammar = json.loads(json_file.read()) - make_path_to_file(args.glsl_insts_output) - with open(args.glsl_insts_output, 'w') as f: - f.write(generate_extended_instruction_table( - grammar, 'glsl')) - - if args.extinst_opencl_grammar is not None: - with open(args.extinst_opencl_grammar) as json_file: - grammar = json.loads(json_file.read()) - make_path_to_file(args.opencl_insts_output) - with open(args.opencl_insts_output, 'w') as f: - f.write(generate_extended_instruction_table( - grammar, 'opencl')) - - if args.extinst_vendor_grammar is not None: - with open(args.extinst_vendor_grammar) as json_file: - grammar = json.loads(json_file.read()) - make_path_to_file(args.vendor_insts_output) - name = args.extinst_vendor_grammar - start = name.find('extinst.') + len('extinst.') - name = name[start:-len('.grammar.json')].replace('-', '_') - with open(args.vendor_insts_output, 'w') as f: - f.write(generate_extended_instruction_table( - grammar, name, args.vendor_operand_kind_prefix)) - - -if __name__ == '__main__': - main()
diff --git a/utils/ggt.py b/utils/ggt.py new file mode 100755 index 0000000..cd32f31 --- /dev/null +++ b/utils/ggt.py
@@ -0,0 +1,977 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 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. +"""Generates compressed grammar tables from SPIR-V JSON grammar.""" + +import errno +import json +import os.path +import re +import sys +from typing import Dict, List, Tuple, Any + +# Find modules relative to the directory containing this script. +# This is needed for hermetic Bazel builds, where the Table files are bundled +# together with this script, while keeping their relative locations. +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from Table.Context import Context +from Table.IndexRange import IndexRange +from Table.Operand import Operand + +class GrammarError(Exception): + pass + +# Extensions to recognize, but which don't necessarily come from the SPIR-V +# core or KHR grammar files. Get this list from the SPIR-V registry web page. +# NOTE: Only put things on this list if it is not in those grammar files. +EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ +SPV_AMD_gpu_shader_half_float +SPV_AMD_gpu_shader_int16 +SPV_KHR_non_semantic_info +SPV_EXT_relaxed_printf_string_address_space +""" + +class ExtInst(): + """ + An extended instruction set. + + Properties: + prefix: the string prefix for operand enums. Often an empty string. + file: the location of the JSON grammar file + name: the name, can be used as an identifier + enum_name: the enum name, e.g. SPV_EXT_INST_OPENCL_STD + grammar: the JSON object for the grammar, loaded from the file. + """ + def __init__(self,spec: str): + matches = re.fullmatch('^([^,]*),(.*)',spec) + if matches is None: + raise Exception("Invalid prefix and path: {}".format(spec)) + self.prefix = matches[1] + if self.prefix is None: + self.prefix = "" + self.file = matches[2] + matches = re.match('.*extinst\\.(.*)\\.grammar.json', self.file) + if matches is None: + raise Exception("Invalid grammar file name: {}".format(self.file)) + self.name = matches[1].replace('-','_').replace('.','_') + + self.enum_name = 'SPV_EXT_INST_TYPE_{}'.format(self.name).upper() + if self.enum_name == "SPV_EXT_INST_TYPE_OPENCL_STD_100": + # Live with an old decision, by adjusting this name. + self.enum_name = "SPV_EXT_INST_TYPE_OPENCL_STD" + if self.enum_name == "SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO": + # The version-agnostic grammar file name omits "_100", but the + # public enum in libspirv.h retains the suffix for compatibility. + self.enum_name = "SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100" + + self.load() + + def load(self): + """ + Populates self.grammar from the file. + Applies the self.prefix to operand enums + """ + with open(self.file) as json_file: + self.grammar = json.loads(json_file.read()) + if len(self.prefix) > 0: + prefix_operand_kind_names(self.prefix, self.grammar) + + +def convert_min_required_version(version): # (version: str | None) -> str + """Converts the minimal required SPIR-V version encoded in the grammar to + the symbol in SPIRV-Tools.""" + if version is None: + return 'SPV_SPIRV_VERSION_WORD(1, 0)' + if version == 'None': + return '0xffffffffu' + return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) + + +def convert_max_required_version(version): # (version: str | None) -> str + """Converts the maximum required SPIR-V version encoded in the grammar to + the symbol in SPIRV-Tools.""" + if version is None: + return '0xffffffffu' + return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) + + +def c_bool(b: bool) -> str: + return 'true' if b else 'false' + + +def ctype(kind: str, quantifier: str) -> str: + """Returns the corresponding operand type used in spirv-tools for the given + operand kind and quantifier used in the JSON grammar. + + Arguments: + - kind, e.g. 'IdRef' + - quantifier, e.g. '', '?', '*' + + Returns: + a string of the enumerant name in spv_operand_type_t + """ + if kind == '': + raise Error("operand JSON object missing a 'kind' field") + # The following cases are where we differ between the JSON grammar and + # spirv-tools. + if kind == 'IdResultType': + kind = 'TypeId' + elif kind == 'IdResult': + kind = 'ResultId' + elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics': + kind = 'MemorySemanticsId' + elif kind == 'IdScope' or kind == 'Scope': + kind = 'ScopeId' + elif kind == 'IdRef': + kind = 'Id' + + elif kind == 'ImageOperands': + kind = 'Image' + elif kind == 'Dim': + kind = 'Dimensionality' + elif kind == 'ImageFormat': + kind = 'SamplerImageFormat' + elif kind == 'KernelEnqueueFlags': + kind = 'KernelEnqFlags' + + elif kind == 'LiteralExtInstInteger': + kind = 'ExtensionInstructionNumber' + elif kind == 'LiteralSpecConstantOpInteger': + kind = 'SpecConstantOpNumber' + elif kind == 'LiteralContextDependentNumber': + kind = 'TypedLiteralNumber' + + elif kind == 'PairLiteralIntegerIdRef': + kind = 'LiteralIntegerId' + elif kind == 'PairIdRefLiteralInteger': + kind = 'IdLiteralInteger' + elif kind == 'PairIdRefIdRef': # Used by OpPhi in the grammar + kind = 'Id' + + if kind == 'FPRoundingMode': + kind = 'FpRoundingMode' + elif kind == 'FPFastMathMode': + kind = 'FpFastMathMode' + + if quantifier == '?': + kind = 'Optional{}'.format(kind) + elif quantifier == '*': + kind = 'Variable{}'.format(kind) + + return 'SPV_OPERAND_TYPE_{}'.format( + re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper()) + + +def convert_operand_kind(obj: Dict[str, str]) -> str: + """Returns the corresponding operand type used in spirv-tools for the given + operand kind and quantifier used in the JSON grammar. + + Arguments: + - obj: an instruction operand, having keys: + - 'kind', e.g. 'IdRef' + - optionally, a quantifier: '?' or '*' + + Returns: + a string of the enumerant name in spv_operand_type_t + """ + kind = obj.get('kind', '') + quantifier = obj.get('quantifier', '') + return ctype(kind, quantifier) + + +def to_safe_identifier(s: str) -> str: + """ + Returns a new string with all non-letters converted to underscores, + and prepending 'k'. + The result should be safe to use as a C identifier. + """ + return 'k' + re.sub(r'[^a-zA-Z0-9]', '_', s) + + +class Grammar(): + """ + Accumulates string and enum tables. + The extensions and operand kinds lists are fixed at creation time. + Prints tables for instructions, operand kinds, and underlying string + and enum tables. + Assumes an index range is emitted by printing an IndexRange object. + """ + def __init__(self, extensions: List[str], operand_kinds:List[dict], printing_classes: List[str]) -> None: + self.context = Context() + self.extensions = sorted(extensions) + self.operand_kinds = sorted(operand_kinds, key = lambda ok: convert_operand_kind(ok)) + self.printing_classes = sorted([to_safe_identifier(x) for x in printing_classes]) + + # The self.header_ignore_decls are only used to debug the flow. + # They are copied into the C++ source code where they are more likely + # to be seen by humans. + self.header_ignore_decls: List[str] = [self.IndexRangeDecls()] + + # The self.header_decls content goes into core_tables_header.inc to be + # included in a .h file. + self.header_decls: List[str] = [] + # The self.body_decls content goes into core_tables_body.inc to be included + # in a .cpp file. It includes definitions of static variables and + # hidden functions. + self.body_decls: List[str] = [] + + if len(self.operand_kinds) == 0: + raise Exception("operand_kinds should be a non-empty list") + if len(self.extensions) == 0: + raise Exception("extensions should be a non-empty list") + + self.ComputePrintingClassDecls() + self.ComputeExtensionDecls() + + # These operand kinds need to have their optional counterpart to also + # be represented in the lookup tables, with the same content. + self.operand_kinds_needing_optional_variant = [ + 'ImageOperands', + 'AccessQualifier', + 'MemoryAccess', + 'PackedVectorFormat', + 'CooperativeMatrixOperands', + 'MatrixMultiplyAccumulateOperands', + 'RawAccessChainOperands', + 'FPEncoding', + 'TensorOperands', + 'Capability'] + + def dump(self) -> None: + self.context.dump() + + def IndexRangeDecls(self) -> str: + return """ +struct IndexRange { + uint32_t first = 0; // index of the first element in the range + uint32_t count = 0; // number of elements in the range +}; +constexpr inline IndexRange IR(uint32_t first, uint32_t count) { + return {first, count}; +} +""" + + def ComputePrintingClassDecls(self) -> str: + parts: List[str] = [] + parts.append("enum class PrintingClass : uint32_t {"); + parts.extend([" {},".format(x) for x in self.printing_classes]) + parts.append("};\n") + self.header_decls.extend(parts) + + def ComputeExtensionDecls(self) -> None: + parts: List[str] = [] + parts.append("enum Extension : uint32_t {"); + parts.extend([" {},".format(to_safe_identifier(x)) for x in self.extensions]) + parts.append("};\n") + self.header_decls.extend(parts) + + parts = [] + parts.append("// Returns the name of an extension, as an index into kStrings") + parts.append("IndexRange ExtensionToIndexRange(Extension extension) {\n switch(extension) {") + for e in self.extensions: + parts.append(' case Extension::k{}: return {};'.format(e,self.context.AddString(e))) + parts.append(" default: break;"); + parts.append(' }\n return {};\n}\n'); + self.body_decls.extend(parts) + + parts = [] + parts.append("""// Extension names and values, ordered by name +// The fields in order are: +// name, indexing into kStrings +// enum value""") + parts.append("static const std::array<NameValue,{}> kExtensionNames{{{{".format(len(self.extensions))) + for e in self.extensions: + parts.append(' {{{}, static_cast<uint32_t>({})}},'.format(self.context.AddString(e), to_safe_identifier(e))) + parts.append("}};\n") + self.body_decls.extend(parts) + + def ComputeOperandTables(self) -> None: + """ + Returns the string for the C definitions of the operand kind tables. + + An operand kind such as ImageOperands also has an associated + operand kind that is an 'optional' variant. + These are represented as two distinct operand kinds in spv_operand_type_t. + For example, ImageOperands maps to both SPV_OPERAND_TYPE_IMAGE, and also + to SPV_OPERAND_TYPE_OPTIONAL_IMAGE. + + The definitions are: + - kOperandsByValue: a 1-dimensional array of all operand descriptions + sorted first by operand kind, then by operand value. + Only non-optional operand kinds are represented here. + + - kOperandsByValueRangeByKind: a function mapping from operand kind to + the index range into kOperandByValue. + This has mappings for both concrete and corresponding optional operand kinds. + + - kOperandNames: a 1-dimensional array of all operand NameIndex + entries, sorted first by operand kinds, then by operand name. + The name part is represented by an index range into the string table. + The index part is the index of this name's entry into the by-value array. + This can have more entries than the by-value array, because names + can have string aliases. For example,the MemorySemantics value 0 + is named both "Relaxed" and "None". + Only non-optional operand kinds are represented here. + + - kOperandNamesRangeByKind: a mapping from operand kind to the index + range into kOperandNames. + This has mappings for both concrete and corresponding optional operand kinds. + """ + + self.header_ignore_decls.append( +""" +struct NameIndex { + // Location of the null-terminated name in the global string table. + IndexRange name; + // Index of this name's entry in in the associated by-value table. + uint32_t index; +}; +struct NameValue { + // Location of the null-terminated name in the global string table. + IndexRange name; + // Enum value in the binary format. + uint32_t value; +}; +// Describes a SPIR-V operand. +struct OperandDesc { + uint32_t value; + IndexRange operands_range; // Indexes kOperandSpans + IndexRange name_range; // Indexes kStrings + IndexRange aliases_range; // Indexes kAliasSpans + IndexRange capabilities_range; // Indexes kCapabilitySpans + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + IndexRange extensions_range; // Indexes kExtensionSpans + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty + // extension lists means only available in extensions. + uint32_t minVersion; + uint32_t lastVersion; + utils::Span<spv_operand_type_t> operands() const; + utils::Span<char> name() const; + utils::Span<IndexRange> aliases() const; + utils::Span<spv::Capability> capabilities() const; + utils::Span<spvtools::Extension> extensions() const; + OperandDesc(const OperandDesc&) = delete; + OperandDesc(OperandDesc&&) = delete; +}; +""") + + def ShouldEmit(operand_kind_json: Dict[str,any]): + """ Returns true if we should emit a table for the given + operand kind. + """ + category = operand_kind_json.get('category') + return category in ['ValueEnum', 'BitEnum'] + + # Populate kOperandsByValue + operands_by_value: List[str] = [] + operands_by_value_by_kind: Dict[str,IndexRange] = {} + # Maps the operand kind and value to the index into kOperandsByValue + index_by_kind_and_value: Dict[Tuple(str,int),int] = {} + index = 0 + for operand_kind_json in self.operand_kinds: + kind_key: str = convert_operand_kind(operand_kind_json) + if ShouldEmit(operand_kind_json): + operands = [Operand(o) for o in operand_kind_json['enumerants']] + operand_descs: List[str] = [] + for o in sorted(operands, key = lambda o: o.value): + suboperands = [convert_operand_kind(p) for p in o.parameters] + desc = [ + o.value, + self.context.AddStringList('operand', suboperands), + str(self.context.AddString(o.enumerant)) + '/* {} */'.format(o.enumerant), + self.context.AddStringList('alias', o.aliases), + self.context.AddStringList('capability', o.capabilities), + self.context.AddStringList('extension', o.extensions), + convert_min_required_version(o.version), + convert_max_required_version(o.lastVersion), + ] + operand_descs.append('{' + ','.join([str(d) for d in desc]) + '}}, // {}'.format(kind_key)) + index_by_kind_and_value[(kind_key,o.value)] = index + index += 1 + operands_by_value_by_kind[kind_key] = IndexRange(len(operands_by_value), len(operand_descs)) + operands_by_value.extend(operand_descs) + else: + pass + + parts = [] + parts.append("""// Operand descriptions, ordered by (operand kind, operand enum value). +// The fields in order are: +// enum value +// operands, an IndexRange into kOperandSpans +// name, a character-counting IndexRange into kStrings +// aliases, an IndexRange into kAliasSpans +// capabilities, an IndexRange into kCapabilitySpans +// extensions, as an IndexRange into kExtensionSpans +// version, first version of SPIR-V that has it +// lastVersion, last version of SPIR-V that has it""") + parts.append("static const std::array<OperandDesc, {}> kOperandsByValue{{{{".format(len(operands_by_value))) + parts.extend([' ' + str(x) for x in operands_by_value]) + parts.append("}};\n") + self.body_decls.extend(parts) + + parts = [] + parts.append("""// Maps an operand kind to possible operands for that kind. +// The result is an IndexRange into kOperandsByValue, and the operands +// are sorted by value within that span. +// An optional variant of a kind maps to the details for the corresponding +// concrete operand kind.""") + parts.append("IndexRange OperandByValueRangeForKind(spv_operand_type_t type) {\n switch(type) {") + for kind_key, ir in operands_by_value_by_kind.items(): + parts.append(" case {}: return {};".format( + kind_key, + str(operands_by_value_by_kind[kind_key]))) + for kind in self.operand_kinds_needing_optional_variant: + non_optional_kind = ctype(kind,'') + if non_optional_kind in operands_by_value_by_kind: + parts.append(" case {}: return {};".format( + ctype(kind, '?'), + str(operands_by_value_by_kind[ctype(kind,'')]))) + else: + raise GrammarError( + "error: unknown operand type {}, from JSON grammar operand '{}':".format(non_optional_kind, kind) + + " consider updating spv_operand_type_t in spirv-tools/libspirv.h") + + parts.append(" default: break;"); + parts.append(" }\n return IR(0,0);\n}\n") + self.body_decls.extend(parts) + + # Populate kOperandNames + operand_names: List[Tuple[IndexRange,int]] = [] + name_range_for_kind: Dict[str,IndexRange] = {} + for operand_kind_json in self.operand_kinds: + kind_key: str = convert_operand_kind(operand_kind_json) + if ShouldEmit(operand_kind_json): + operands = [Operand(o) for o in operand_kind_json['enumerants']] + tuples: List[Tuple[str,int,str]] = [] + for o in operands: + tuples.append((o.enumerant, o.value, kind_key)) + for a in o.aliases: + tuples.append((a, o.value, kind_key)) + tuples = sorted(tuples, key = lambda t: t[0]) + ir_tuples = [(self.context.AddString(t[0]),t[1],t[2]) for t in tuples] + name_range_for_kind[kind_key] = IndexRange(len(operand_names), len(ir_tuples)) + operand_names.extend(ir_tuples) + else: + pass + operand_name_strings: List[str] = [] + for i in range(0, len(operand_names)): + ir, value, kind_key = operand_names[i] + index = index_by_kind_and_value[(kind_key,value)] + operand_name_strings.append('{{{}, {}}}, // {} {} in {}'.format( + str(ir),index,i,self.context.GetString(ir),kind_key)) + + parts: List[str] = [] + parts.append("""// Operand names and index into kOperandsByValue, ordered by (operand kind, name) +// The fields in order are: +// name, either the primary name or an alias, indexing into kStrings +// index into the kOperandsByValue array""") + parts.append("static const std::array<NameIndex, {}> kOperandNames{{{{".format(len(operand_name_strings))) + parts.extend([' ' + str(x) for x in operand_name_strings]) + parts.append("}};\n") + self.body_decls.extend(parts) + + parts.append("""// Maps an operand kind to possible names for operands of that kind. +// The result is an IndexRange into kOperandNames, and the names +// are sorted by name within that span. +// An optional variant of a kind maps to the details for the corresponding +// concrete operand kind.""") + parts = ["IndexRange OperandNameRangeForKind(spv_operand_type_t type) {\n switch(type) {"] + for kind_key, ir in name_range_for_kind.items(): + parts.append(" case {}: return {};".format( + kind_key, + str(name_range_for_kind[kind_key]))) + for kind in self.operand_kinds_needing_optional_variant: + parts.append(" case {}: return {};".format( + ctype(kind, '?'), + str(name_range_for_kind[ctype(kind,'')]))) + parts.append(" default: break;"); + parts.append(" }\n return IR(0,0);\n}\n") + self.body_decls.extend(parts) + + + def ComputeInstructionTables(self, insts) -> None: + """ + Creates declarations for instruction tables. + Populates self.header_ignore_decls, self.body_decls. + + Params: + insts: an array of instructions objects using the JSON schema + """ + self.header_ignore_decls.append(""" +// Describes an Instruction +struct InstructionDesc { + const spv::Op value; + const bool hasResult; + const bool hasType; + const IndexRange operands_range; // Indexes kOperandSpans + const IndexRange name_range; // Indexes kStrings + const IndexRange aliases_range; // Indexes kAliasSpans + const IndexRange capabilities_range; // Indexes kCapbilitySpans + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const IndexRange extensions_range; // Indexes kExtensionSpans + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty + // extension lists means only available in extensions. + uint32_t minVersion; + uint32_t lastVersion; + PrintingClass printingClass; // Section of SPIR-V spec. e.g. kComposite, kImage + utils::Span<spv_operand_type_t> operands() const; + utils::Span<char> name() const; + utils::Span<IndexRange> aliases() const; + utils::Span<spv::Capability> capabilities() const; + utils::Span<spvtools::Extension> extensions() const; + OperandDesc(const OperandDesc&) = delete; + OperandDesc(OperandDesc&&) = delete; +}; +""") + + # Create the array of InstructionDesc + lines: List[str] = [] + # Maps the opcode name (without "Op" prefix) to its index in the table. + index_by_opcode: Dict[int,int] = {} + # Sort by opcode, so lookup can use binary search + for inst in sorted(insts, key = lambda inst: int(inst['opcode'])): + parts: List[str] = [] + + opname: str = inst['opname'] + + operand_kinds = [convert_operand_kind(o) for o in inst.get('operands',[])] + if opname == 'OpExtInst' and operand_kinds[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID': + # The published grammar uses 'sequence of ID' at the + # end of the ExtInst operands. But SPIRV-Tools uses + # a specific pattern based on the particular opcode. + # Drop it here. + # See https://github.com/KhronosGroup/SPIRV-Tools/issues/233 + operand_kinds.pop() + + hasResult = 'SPV_OPERAND_TYPE_RESULT_ID' in operand_kinds + hasType = 'SPV_OPERAND_TYPE_TYPE_ID' in operand_kinds + + # Remove the "Op" prefix from opcode alias names + aliases = [name[2:] for name in inst.get('aliases',[])] + + parts.extend([ + 'spv::Op::' + opname, + c_bool(hasResult), + c_bool(hasType), + self.context.AddStringList('operand', operand_kinds), + self.context.AddString(opname[2:]), + self.context.AddStringList('alias', aliases), + self.context.AddStringList('capability', inst.get('capabilities',[])), + self.context.AddStringList('extension', inst.get('extensions',[])), + convert_min_required_version(inst.get('version', None)), + convert_max_required_version(inst.get('lastVersion', None)), + 'PrintingClass::' + to_safe_identifier(inst.get('class','@exclude')) + ]) + + index_by_opcode[int(inst['opcode'])] = len(lines) + lines.append('{{{}}},'.format(', '.join([str(x) for x in parts]))) + parts = [] + parts.append("""// Instruction descriptions, ordered by opcode. +// The fields in order are: +// opcode +// a boolean indicating if the instruction produces a result ID +// a boolean indicating if the instruction result ID has a type +// operands, an IndexRange into kOperandSpans +// opcode name (without the 'Op' prefix), a character-counting IndexRange into kStrings +// aliases, an IndexRange into kAliasSpans +// capabilities, an IndexRange into kCapabilitySpans +// extensions, as an IndexRange into kExtensionSpans +// version, first version of SPIR-V that has it +// lastVersion, last version of SPIR-V that has it""") + parts.append("static const std::array<InstructionDesc, {}> kInstructionDesc{{{{".format(len(lines))); + parts.extend([' ' + l for l in lines]) + parts.append("}};\n"); + self.body_decls.extend(parts) + + # Create kInstructionNames. + opcode_name_entries: List[str] = [] + name_value_pairs: List[Tuple[str,int]] = [] + for i in insts: + name_value_pairs.append((i['opname'][2:], i['opcode'])) + for a in i.get('aliases',[]): + name_value_pairs.append((a[2:], i['opcode'])) + name_value_pairs = sorted(name_value_pairs) + inst_name_strings: List[str] = [] + for i in range(0, len(name_value_pairs)): + name, value = name_value_pairs[i] + ir = self.context.AddString(name) + index = index_by_opcode[value] + inst_name_strings.append('{{{}, {}}}, // {} {}'.format(str(ir),index,i,name)) + parts: List[str] = [] + parts.append("""// Opcode strings (without the 'Op' prefix) and opcode values, ordered by name. +// The fields in order are: +// name, either the primary name or an alias, indexing into kStrings +// index into kInstructionDesc""") + parts.append("static const std::array<NameIndex, {}> kInstructionNames{{{{".format(len(inst_name_strings))) + parts.extend([' ' + str(x) for x in inst_name_strings]) + parts.append("}};\n") + self.body_decls.extend(parts) + + + def ComputeExtendedInstructions(self, extinsts) -> None: + """ + Generates tables for extended instructions + + Args: + self + extinsts: a list of extinst objects + """ + + """ + ExtInstDesc { + value: uint32_t + name: IndexRange + operands: IndexRange + capabilities: IndexRange + } + + The definitions are: + - kExtInstByValue: a 1-dimensional array of all operand descriptions + sorted first by extended instruction enum, then by operand value. + + - ExtInstByValueRangeForKind: a function mapping from extinst enum to + the index range into kExtInstByValue. + + - kExtInstNames: a 1-dimensional array of all extinst name-index pairs, + sorted first by extinst enum, then by operand name. + The name part is represented by an index range into the string table. + The index part is the index of this name's entry in the kExtInstByValue + array. + + - kExtInstNamesRangeByKind: a mapping from operand kind to the index + range into kOperandNames. + This has mappings for both concrete and corresponding optional operand kinds. + """ + + # Create kExtInstByValue + by_value: List[List[Any]] = [] + by_value_by_kind: Dict[str,IndexRange] = {} + index_by_kind_and_opcode: Dict[Tuple[str,int],int] = {} + index = 0 + for e in extinsts: + insts_in_set = [] + for inst in sorted(e.grammar['instructions'], key = lambda inst: inst['opcode']): + operands = [convert_operand_kind(o) for o in inst.get('operands',[])] + inst_parts = [ + inst['opcode'], + self.context.AddStringList('operand', operands), + self.context.AddString(inst['opname']), + self.context.AddStringList('capability', inst.get('capabilities',[])), + ] + inst_parts = [str(x) for x in inst_parts] + insts_in_set.append(' {{{}}}, // {} in {}'.format( + ','.join(inst_parts), inst['opname'], e.name)) + index_by_kind_and_opcode[(e.enum_name,int(inst['opcode']))] = index + index += 1 + by_value_by_kind[e.enum_name] = IndexRange(len(by_value), len(insts_in_set)) + by_value.extend(insts_in_set) + + parts: List[str] = [] + parts.append("""// Extended instruction descriptions, ordered by (extinst enum, opcode value). +// The fields in order are: +// enum value +// operands, an IndexRange into kOperandSpans +// name, a character-counting IndexRange into kStrings +// capabilities, an IndexRange into kCapabilitySpans""") + parts.append("static const std::array<ExtInstDesc, {}> kExtInstByValue{{{{".format(len(by_value))) + parts.extend(by_value) + parts.append('}};\n') + self.body_decls.extend(parts) + + # Create kExtInstByValueRangeForKind + parts = [] + parts.append("""// Maps an extended instruction enum to possible names for operands of that kind. +// The result is an IndexRange into kOperandNames, and the names +// are sorted by name within that span. +// An optional variant of a kind maps to the details for the corresponding +// concrete operand kind.""") + parts = ["IndexRange ExtInstByValueRangeForKind(spv_ext_inst_type_t type) {\n switch(type) {"] + for name, ir in by_value_by_kind.items(): + parts.append(" case {}: return {};".format(name, ir)) + parts.append(" default: break;"); + parts.append(" }\n return IR(0,0);\n}\n") + self.body_decls.extend(parts) + + # Create kExtInstNames + parts = [] + by_name: List[List[Any]] = [] + by_name_by_kind: Dict[str,IndexRange] = {} + for e in extinsts: + # Sort by name within a set + insts_by_name = sorted(e.grammar['instructions'], key = lambda i: i['opname']) + insts_in_set = [] + for inst in insts_by_name: + index = index_by_kind_and_opcode[(e.enum_name,int(inst['opcode']))] + insts_in_set.append( + ' {{{}, {}}}, // {} in {}'.format( + str(self.context.AddString(inst['opname'])), + index, + inst['opname'], + e.name)) + by_name_by_kind[e.enum_name] = IndexRange(len(by_name), len(insts_in_set)) + by_name.extend(insts_in_set) + parts.append("""// Extended instruction opcode names sorted by extended instruction kind, then opcode name. +// The fields in order are: +// name +// index into kExtInstByValue""") + parts.append("static const std::array<NameIndex, {}> kExtInstNames{{{{".format(len(by_name))) + parts.extend(by_name) + parts.append('}};\n') + self.body_decls.extend(parts) + + # Create kExtInstNameRangeByKind + parts = [] + parts.append("""// Maps an extended instruction kind to possible names for instructions of that kind. +// The result is an IndexRange into kExtInstNames, and the names +// are sorted by name within that span.""") + parts = ["IndexRange ExtInstNameRangeForKind(spv_ext_inst_type_t type) {\n switch(type) {"] + for name, ir in by_name_by_kind.items(): + parts.append(" case {}: return {};".format(name, str(ir))) + parts.append(" default: break;"); + parts.append(" }\n return IR(0,0);\n}\n") + self.body_decls.extend(parts) + + def ComputeLeafTables(self) -> None: + """ + Generates the tables that the instruction and operand tables point to. + The tables are: + - the string table + - the table of sequences of: + - capabilities + - extensions + - operands + + This method must be called after computing instruction and operand tables. + """ + + def c_str(s: str): + """ + Returns the source for a C string literal or the given string, including + the explicit null at the end + """ + return '"{}\\0"'.format(json.dumps(s).strip('"')) + + parts: List[str] = [] + parts.append("// Array of characters, referenced by IndexRanges elsewhere.") + parts.append("// Each IndexRange denotes a string.") + parts.append('static const char kStrings[] ='); + parts.extend([' {} // {}'.format(c_str(s), str(self.context.strings[s])) for s in self.context.string_buffer]) + parts.append(';\n'); + self.body_decls.extend(parts); + + parts: List[str] = [] + parts.append("""// Array of IndexRanges, where each represents a string by referencing +// the kStrings table. +// This array contains all sequences of alias strings used in the grammar. +// This table is referenced by an IndexRange elsewhere, i.e. by the 'aliases' +// field of an instruction or operand description.""") + parts.append('static const IndexRange kAliasSpans[] = {'); + ranges = self.context.range_buffer['alias'] + for i in range(0, len(ranges)): + ir = ranges[i] + parts.append(' {}, // {} {}'.format(str(ir), i, self.context.GetString(ir))) + parts.append('};\n'); + self.body_decls.extend(parts); + + parts = [] + parts.append("// Array of capabilities, referenced by IndexRanges elsewhere.") + parts.append("// Contains all sequences of capabilities used in the grammar.") + parts.append('static const spv::Capability kCapabilitySpans[] = {'); + capability_ranges = self.context.range_buffer['capability'] + for i in range(0, len(capability_ranges)): + ir = capability_ranges[i] + cap = self.context.GetString(ir) + parts.append(' spv::Capability::{}, // {}'.format(cap, i)) + parts.append('};\n'); + self.body_decls.extend(parts); + + parts = [] + parts.append("// Array of extensions, referenced by IndexRanges elsewhere.") + parts.append("// Contains all sequences of extensions used in the grammar.") + parts.append('static const spvtools::Extension kExtensionSpans[] = {'); + ranges = self.context.range_buffer['extension'] + for i in range(0, len(ranges)): + ir = ranges[i] + name = self.context.GetString(ir) + parts.append(' spvtools::Extension::k{}, // {}'.format(name, i)) + parts.append('};\n'); + self.body_decls.extend(parts); + + parts = [] + parts.append("// Array of operand types, referenced by IndexRanges elsewhere.") + parts.append("// Contains all sequences of operand types used in the grammar.") + parts.append('static const spv_operand_type_t kOperandSpans[] = {'); + ranges = self.context.range_buffer['operand'] + for i in range(0, len(ranges)): + ir = ranges[i] + name = self.context.GetString(ir) + parts.append(' {}, // {}'.format(name, i)) + parts.append('};\n'); + self.body_decls.extend(parts) + + +def make_path_to_file(f: str) -> None: + """Makes all ancestor directories to the given file, if they don't yet + exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + + +def get_extension_list(instructions, operand_kinds): + """Returns extensions as an alphabetically sorted list of strings. + + Args: + instructions: list of instruction objects, using the JSON grammar file schema + operand_kinds: list of operand_kind objects, using the JSON grammar file schema + """ + + things_with_an_extensions_field = [item for item in instructions] + + enumerants = sum([item.get('enumerants', []) + for item in operand_kinds], []) + + things_with_an_extensions_field.extend(enumerants) + + extensions = sum([item.get('extensions', []) + for item in things_with_an_extensions_field + if item.get('extensions')], []) + + for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split(): + # If it's already listed in a grammar, then don't put it in the + # special exceptions list. + assert item not in extensions, 'Extension %s is already in a grammar file' % item + + extensions.extend( + EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split()) + + # Validator would ignore type declaration unique check. Should only be used + # for legacy autogenerated test files containing multiple instances of the + # same type declaration, if fixing the test by other methods is too + # difficult. Shouldn't be used for any other reasons. + extensions.append('SPV_VALIDATOR_ignore_type_decl_unique') + + return sorted(set(extensions)) + + +def prefix_operand_kind_names(prefix, json_dict): + """Modifies json_dict, by prefixing all the operand kind names + with the given prefix. Also modifies their uses in the instructions + to match. + """ + + old_to_new = {} + for operand_kind in json_dict["operand_kinds"]: + old_name = operand_kind["kind"] + new_name = prefix + old_name + operand_kind["kind"] = new_name + old_to_new[old_name] = new_name + + for instruction in json_dict["instructions"]: + for operand in instruction.get("operands", []): + replacement = old_to_new.get(operand["kind"]) + if replacement is not None: + operand["kind"] = replacement + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') + + parser.add_argument('--spirv-core-grammar', metavar='<path>', + type=str, required=False, + help='input JSON grammar file for core SPIR-V ' + 'instructions') + parser.add_argument('--extinst', metavar='<path>', + type=str, action='append', required=False, default=None, + help='extended instruction info: an enum prefix, then a comma, then' + ' the file location of the JSON grammar') + + parser.add_argument('--core-tables-body-output', metavar='<path>', + type=str, required=False, default=None, + help='output file for core SPIR-V grammar tables to be included in .cpp') + parser.add_argument('--core-tables-header-output', metavar='<path>', + type=str, required=False, default=None, + help='output file for core SPIR-V grammar tables to be included in .h') + + args = parser.parse_args() + + if args.spirv_core_grammar is None: + print('error: missing --spirv-core-grammar ') + sys.exit(1) + if (args.core_tables_body_output is None) and (args.core_tables_header_output is None): + print('error: need at least one of --core-tables-body-output --core-tables-header-output ') + sys.exit(1) + if len(args.extinst) < 1: + print('error: missing --extinst ') + sys.exit(1) + + # Load the JSON grammar files. + extinsts = sorted([ExtInst(e) for e in args.extinst], key = lambda e: e.name) + with open(args.spirv_core_grammar) as json_file: + core_grammar = json.loads(json_file.read()) + printing_class: List[str] = [e['tag'] for e in core_grammar['instruction_printing_class']] + + # Collect all operand kinds and instructions, so we can generate + # extension lists, capability lists, and alias lists. + # Make a copy to avoid polluting the instruction list. + instructions = [x for x in core_grammar['instructions']] + operand_kinds = [x for x in core_grammar['operand_kinds']] + for e in extinsts: + instructions.extend(e.grammar.get('instructions',[])) + operand_kinds.extend(e.grammar.get('operand_kinds',[])) + + extensions = get_extension_list(instructions, operand_kinds) + + g = Grammar(extensions, operand_kinds, printing_class) + g.ComputeOperandTables() + g.ComputeInstructionTables(core_grammar['instructions']) + g.ComputeExtendedInstructions(extinsts) + g.ComputeLeafTables() + + if args.core_tables_body_output is not None: + make_path_to_file(args.core_tables_body_output) + with open(args.core_tables_body_output, 'w') as f: + f.write('\n'.join(g.body_decls)) + if args.core_tables_header_output is not None: + make_path_to_file(args.core_tables_header_output) + with open(args.core_tables_header_output, 'w') as f: + f.write('\n'.join(g.header_decls)) + sys.exit(0) + + +if __name__ == '__main__': + try: + main() + except GrammarError as ge: + print(ge) + sys.exit(1)
diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh index a62ebe9..d8c1da3 100755 --- a/utils/roll_deps.sh +++ b/utils/roll_deps.sh
@@ -35,6 +35,7 @@ dependency_to_branch_map["external/googletest/"]="origin/main" dependency_to_branch_map["external/re2/"]="origin/main" dependency_to_branch_map["external/spirv-headers/"]="origin/main" +dependency_to_branch_map["external/mimalloc/"]="origin/main" # This script assumes it's parent directory is the repo root. repo_path=$(dirname "$0")/..
diff --git a/utils/vim/README.md b/utils/vim/README.md new file mode 100644 index 0000000..a73977e --- /dev/null +++ b/utils/vim/README.md
@@ -0,0 +1,87 @@ +# Neovim configuration guide for SPIR-V disassembly files + +This directory holds instructions to configure Neovim for SPIR-V assembly files (`.spvasm`) + +At the end, Neovim should support: +* Syntax highlighting +* Jump to definition +* Find all references +* Symbol renaming +* Operand hover information +* Formatting +* Completion suggestions for all Opcodes and Ids + +While the instructions here are specifically for Neovim, they should translate easily to vim. + +## Dependencies + +In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies: +* [`golang 1.16+`](https://golang.org/) + +## File type detection + +Neovim's default config location is typically `~/.config/nvim` so the rest of the instructions assume that but it will need to be changed if your system is different. + +Tell neovim that `*.spvasm` files should be treated as `spvasm` filetype +```bash +echo "au BufRead,BufNewFile *.spvasm set filetype=spvasm" > ~/.config/nvim/ftdetect/spvasm.vim +``` + +## Syntax Highlighting + +### Generate the syntax highlighting file +```bash +cd <spirv-tools dir> +mkdir -p build && cd build +# Any platform is fine, ninja is used an as example +cmake -G Ninja .. +ninja spirv-tools-vimsyntax +``` + +### Copy the syntax file +```bash +cp spvasm.vim ~/.config/nvim/syntax/spvasm.vim +``` + +## Language Server + +### Building the LSP (masOS / Linux) + +Run `build_lsp.sh` +Copy `spirvls` and `spirv.json` to a location in `$PATH` + +```bash +cd <spirv-tools dir>/utils/vscode +./build_lsp.sh +sudo cp spirvls/* /usr/local/bin/ +``` + +### Building the LSP (Windows) + +TODO + +### Configuring Neovim + +Configuration will depend a lot on your installed plugins but assuming you are using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) the following should be sufficient. + +```lua +local lspconfig = require 'lspconfig' +local configs = require 'lspconfig.configs' + +if not configs.spvasm then + configs.spvasm = { + default_config = { + cmd = { 'spirvls' }, + filetypes = { 'spvasm' }, + root_dir = function(fname) + return '.' + end, + settings = {}, + }, + } +end + +lspconfig.spvasm.setup { + capabilities = require('cmp_nvim_lsp').default_capabilities(vim.lsp.protocol.make_client_capabilities()), +} +```
diff --git a/utils/generate_vim_syntax.py b/utils/vim/generate_syntax.py similarity index 100% rename from utils/generate_vim_syntax.py rename to utils/vim/generate_syntax.py
diff --git a/utils/vscode/README.md b/utils/vscode/README.md index d7aa2b4..9cf82b1 100644 --- a/utils/vscode/README.md +++ b/utils/vscode/README.md
@@ -9,6 +9,7 @@ * Symbol renaming * Operand hover information * Formatting +* Completion suggestions for all Opcodes and Ids ## Dependencies @@ -18,8 +19,8 @@ ## Installing (macOS / Linux) -Run `install.sh` +Run `install_vscode.sh` ## Installing (Windows) -Run `install.bat` +Run `install_vscode.bat`
diff --git a/utils/vscode/build_lsp.sh b/utils/vscode/build_lsp.sh new file mode 100755 index 0000000..dc92cc8 --- /dev/null +++ b/utils/vscode/build_lsp.sh
@@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright (c) 2019 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. + +set -e # Fail on any error. + +ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +pushd ${ROOT_PATH} + go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json + go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go + + mkdir -p ./spirvls + cp ./spirv.json ./spirvls + + go build -o ./spirvls/spirvls ./src/langsvr.go +popd
diff --git a/utils/vscode/install.bat b/utils/vscode/install_vscode.bat similarity index 100% rename from utils/vscode/install.bat rename to utils/vscode/install_vscode.bat
diff --git a/utils/vscode/install.sh b/utils/vscode/install_vscode.sh similarity index 100% rename from utils/vscode/install.sh rename to utils/vscode/install_vscode.sh
diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json index 2e88296..4ab2529 100644 --- a/utils/vscode/spirv.json +++ b/utils/vscode/spirv.json
@@ -12,6 +12,8 @@ { "include": "#BitEnum_MemoryAccess" }, { "include": "#BitEnum_KernelProfilingInfo" }, { "include": "#BitEnum_RayFlags" }, + { "include": "#BitEnum_FragmentShadingRate" }, + { "include": "#BitEnum_RawAccessChainOperands" }, { "include": "#ValueEnum_SourceLanguage" }, { "include": "#ValueEnum_ExecutionModel" }, { "include": "#ValueEnum_AddressingModel" }, @@ -25,8 +27,13 @@ { "include": "#ValueEnum_ImageChannelOrder" }, { "include": "#ValueEnum_ImageChannelDataType" }, { "include": "#ValueEnum_FPRoundingMode" }, + { "include": "#ValueEnum_FPDenormMode" }, + { "include": "#ValueEnum_QuantizationModes" }, + { "include": "#ValueEnum_FPOperationMode" }, + { "include": "#ValueEnum_OverflowModes" }, { "include": "#ValueEnum_LinkageType" }, { "include": "#ValueEnum_AccessQualifier" }, + { "include": "#ValueEnum_HostAccessQualifier" }, { "include": "#ValueEnum_FunctionParameterAttribute" }, { "include": "#ValueEnum_Decoration" }, { "include": "#ValueEnum_BuiltIn" }, @@ -37,6 +44,17 @@ { "include": "#ValueEnum_RayQueryIntersection" }, { "include": "#ValueEnum_RayQueryCommittedIntersectionType" }, { "include": "#ValueEnum_RayQueryCandidateIntersectionType" }, + { "include": "#ValueEnum_PackedVectorFormat" }, + { "include": "#BitEnum_CooperativeMatrixOperands" }, + { "include": "#ValueEnum_CooperativeMatrixLayout" }, + { "include": "#ValueEnum_CooperativeMatrixUse" }, + { "include": "#BitEnum_CooperativeMatrixReduce" }, + { "include": "#ValueEnum_TensorClampMode" }, + { "include": "#BitEnum_TensorAddressingOperands" }, + { "include": "#ValueEnum_InitializationModeQualifier" }, + { "include": "#ValueEnum_LoadCacheControl" }, + { "include": "#ValueEnum_StoreCacheControl" }, + { "include": "#ValueEnum_NamedMaximumNumberOfRegisters" }, { "include": "#BitEnum_DebugInfoFlags" }, { "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" }, { "include": "#ValueEnum_DebugCompositeType" }, @@ -53,11 +71,11 @@ ], "repository": { "BitEnum_ImageOperands": { - "match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelAvailableKHR|MakeTexelVisible|MakeTexelVisibleKHR|NonPrivateTexel|NonPrivateTexelKHR|VolatileTexel|VolatileTexelKHR|SignExtend|ZeroExtend)\\b", + "match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelVisible|NonPrivateTexel|VolatileTexel|SignExtend|ZeroExtend|Nontemporal|Offsets)\\b", "name": "keyword.spirv" }, "BitEnum_FPFastMathMode": { - "match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast)\\b", + "match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast|AllowContract|AllowReassoc|AllowTransform)\\b", "name": "keyword.spirv" }, "BitEnum_SelectionControl": { @@ -65,19 +83,19 @@ "name": "keyword.spirv" }, "BitEnum_LoopControl": { - "match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount)\\b", + "match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount|InitiationIntervalINTEL|MaxConcurrencyINTEL|DependencyArrayINTEL|PipelineEnableINTEL|LoopCoalesceINTEL|MaxInterleavingINTEL|SpeculatedIterationsINTEL|NoFusionINTEL|LoopCountINTEL|MaxReinvocationDelayINTEL)\\b", "name": "keyword.spirv" }, "BitEnum_FunctionControl": { - "match": "\\b(None|Inline|DontInline|Pure|Const)\\b", + "match": "\\b(None|Inline|DontInline|Pure|Const|OptNoneEXT)\\b", "name": "keyword.spirv" }, "BitEnum_MemorySemantics": { - "match": "\\b(Relaxed|None|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|OutputMemoryKHR|MakeAvailable|MakeAvailableKHR|MakeVisible|MakeVisibleKHR|Volatile)\\b", + "match": "\\b(Relaxed|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|MakeAvailable|MakeVisible|Volatile)\\b", "name": "keyword.spirv" }, "BitEnum_MemoryAccess": { - "match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerAvailableKHR|MakePointerVisible|MakePointerVisibleKHR|NonPrivatePointer|NonPrivatePointerKHR)\\b", + "match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerVisible|NonPrivatePointer|AliasScopeINTELMask|NoAliasINTELMask)\\b", "name": "keyword.spirv" }, "BitEnum_KernelProfilingInfo": { @@ -85,35 +103,43 @@ "name": "keyword.spirv" }, "BitEnum_RayFlags": { - "match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR)\\b", + "match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR|ForceOpacityMicromap2StateEXT)\\b", + "name": "keyword.spirv" + }, + "BitEnum_FragmentShadingRate": { + "match": "\\b(Vertical2Pixels|Vertical4Pixels|Horizontal2Pixels|Horizontal4Pixels)\\b", + "name": "keyword.spirv" + }, + "BitEnum_RawAccessChainOperands": { + "match": "\\b(None|RobustnessPerComponentNV|RobustnessPerElementNV)\\b", "name": "keyword.spirv" }, "ValueEnum_SourceLanguage": { - "match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b", + "match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL|CPP_for_OpenCL|SYCL|HERO_C|NZSL|WGSL|Slang|Zig)\\b", "name": "keyword.spirv" }, "ValueEnum_ExecutionModel": { - "match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|RayGenerationKHR|IntersectionNV|IntersectionKHR|AnyHitNV|AnyHitKHR|ClosestHitNV|ClosestHitKHR|MissNV|MissKHR|CallableNV|CallableKHR)\\b", + "match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationKHR|IntersectionKHR|AnyHitKHR|ClosestHitKHR|MissKHR|CallableKHR|TaskEXT|MeshEXT)\\b", "name": "keyword.spirv" }, "ValueEnum_AddressingModel": { - "match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64|PhysicalStorageBuffer64EXT)\\b", + "match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64)\\b", "name": "keyword.spirv" }, "ValueEnum_MemoryModel": { - "match": "\\b(Simple|GLSL450|OpenCL|Vulkan|VulkanKHR)\\b", + "match": "\\b(Simple|GLSL450|OpenCL|Vulkan)\\b", "name": "keyword.spirv" }, "ValueEnum_ExecutionMode": { - "match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT)\\b", + "match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|NonCoherentColorAttachmentReadEXT|NonCoherentDepthAttachmentReadEXT|NonCoherentStencilAttachmentReadEXT|SubgroupUniformControlFlowKHR|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|EarlyAndLateFragmentTestsAMD|StencilRefReplacingEXT|CoalescingAMDX|IsApiEntryAMDX|MaxNodeRecursionAMDX|StaticNumWorkgroupsAMDX|ShaderIndexAMDX|MaxNumWorkgroupsAMDX|StencilRefUnchangedFrontAMD|StencilRefGreaterFrontAMD|StencilRefLessFrontAMD|StencilRefUnchangedBackAMD|StencilRefGreaterBackAMD|StencilRefLessBackAMD|QuadDerivativesKHR|RequireFullQuadsKHR|SharesInputWithAMDX|OutputLinesEXT|OutputPrimitivesEXT|DerivativeGroupQuadsKHR|DerivativeGroupLinearKHR|OutputTrianglesEXT|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT|SharedLocalMemorySizeINTEL|RoundingModeRTPINTEL|RoundingModeRTNINTEL|FloatingPointModeALTINTEL|FloatingPointModeIEEEINTEL|MaxWorkgroupSizeINTEL|MaxWorkDimINTEL|NoGlobalOffsetINTEL|NumSIMDWorkitemsINTEL|SchedulerTargetFmaxMhzINTEL|MaximallyReconvergesKHR|FPFastMathDefault|StreamingInterfaceINTEL|RegisterMapInterfaceINTEL|NamedBarrierCountINTEL|MaximumRegistersINTEL|MaximumRegistersIdINTEL|NamedMaximumRegistersINTEL)\\b", "name": "keyword.spirv" }, "ValueEnum_StorageClass": { - "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|CallableDataKHR|IncomingCallableDataNV|IncomingCallableDataKHR|RayPayloadNV|RayPayloadKHR|HitAttributeNV|HitAttributeKHR|IncomingRayPayloadNV|IncomingRayPayloadKHR|ShaderRecordBufferNV|ShaderRecordBufferKHR|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b", + "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|TileImageEXT|NodePayloadAMDX|CallableDataKHR|IncomingCallableDataKHR|RayPayloadKHR|HitAttributeKHR|IncomingRayPayloadKHR|ShaderRecordBufferKHR|PhysicalStorageBuffer|HitObjectAttributeNV|TaskPayloadWorkgroupEXT|CodeSectionINTEL|DeviceOnlyINTEL|HostOnlyINTEL)\\b", "name": "keyword.spirv" }, "ValueEnum_Dim": { - "match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData)\\b", + "match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData|TileImageDataEXT)\\b", "name": "keyword.spirv" }, "ValueEnum_SamplerAddressingMode": { @@ -125,7 +151,7 @@ "name": "keyword.spirv" }, "ValueEnum_ImageFormat": { - "match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui)\\b", + "match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui|R64ui|R64i)\\b", "name": "keyword.spirv" }, "ValueEnum_ImageChannelOrder": { @@ -133,35 +159,55 @@ "name": "keyword.spirv" }, "ValueEnum_ImageChannelDataType": { - "match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2)\\b", + "match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2|UnsignedIntRaw10EXT|UnsignedIntRaw12EXT|UnormInt2_101010EXT)\\b", "name": "keyword.spirv" }, "ValueEnum_FPRoundingMode": { "match": "\\b(RTE|RTZ|RTP|RTN)\\b", "name": "keyword.spirv" }, + "ValueEnum_FPDenormMode": { + "match": "\\b(Preserve|FlushToZero)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_QuantizationModes": { + "match": "\\b(TRN|TRN_ZERO|RND|RND_ZERO|RND_INF|RND_MIN_INF|RND_CONV|RND_CONV_ODD)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FPOperationMode": { + "match": "\\b(IEEE|ALT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_OverflowModes": { + "match": "\\b(WRAP|SAT|SAT_ZERO|SAT_SYM)\\b", + "name": "keyword.spirv" + }, "ValueEnum_LinkageType": { - "match": "\\b(Export|Import)\\b", + "match": "\\b(Export|Import|LinkOnceODR)\\b", "name": "keyword.spirv" }, "ValueEnum_AccessQualifier": { "match": "\\b(ReadOnly|WriteOnly|ReadWrite)\\b", "name": "keyword.spirv" }, + "ValueEnum_HostAccessQualifier": { + "match": "\\b(NoneINTEL|ReadINTEL|WriteINTEL|ReadWriteINTEL)\\b", + "name": "keyword.spirv" + }, "ValueEnum_FunctionParameterAttribute": { - "match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite)\\b", + "match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite|RuntimeAlignedINTEL)\\b", "name": "keyword.spirv" }, "ValueEnum_Decoration": { - "match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE)\\b", + "match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|WeightTextureQCOM|BlockMatchTextureQCOM|BlockMatchSamplerQCOM|ExplicitInterpAMD|NodeSharesPayloadLimitsWithAMDX|NodeMaxPayloadsAMDX|TrackFinishWritingAMDX|PayloadNodeNameAMDX|PayloadNodeBaseIndexAMDX|PayloadNodeSparseArrayAMDX|PayloadNodeArraySizeAMDX|PayloadDispatchIndirectAMDX|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveEXT|PerViewNV|PerTaskNV|PerVertexKHR|NonUniform|RestrictPointer|AliasedPointer|HitObjectShaderRecordBufferNV|BindlessSamplerNV|BindlessImageNV|BoundSamplerNV|BoundImageNV|SIMTCallINTEL|ReferencedIndirectlyINTEL|ClobberINTEL|SideEffectsINTEL|VectorComputeVariableINTEL|FuncParamIOKindINTEL|VectorComputeFunctionINTEL|StackCallINTEL|GlobalVariableOffsetINTEL|CounterBuffer|UserSemantic|UserTypeGOOGLE|FunctionRoundingModeINTEL|FunctionDenormModeINTEL|RegisterINTEL|MemoryINTEL|NumbanksINTEL|BankwidthINTEL|MaxPrivateCopiesINTEL|SinglepumpINTEL|DoublepumpINTEL|MaxReplicatesINTEL|SimpleDualPortINTEL|MergeINTEL|BankBitsINTEL|ForcePow2DepthINTEL|StridesizeINTEL|WordsizeINTEL|TrueDualPortINTEL|BurstCoalesceINTEL|CacheSizeINTEL|DontStaticallyCoalesceINTEL|PrefetchINTEL|StallEnableINTEL|FuseLoopsInFunctionINTEL|MathOpDSPModeINTEL|AliasScopeINTEL|NoAliasINTEL|InitiationIntervalINTEL|MaxConcurrencyINTEL|PipelineEnableINTEL|BufferLocationINTEL|IOPipeStorageINTEL|FunctionFloatingPointModeINTEL|SingleElementVectorINTEL|VectorComputeCallableFunctionINTEL|MediaBlockIOINTEL|StallFreeINTEL|FPMaxErrorDecorationINTEL|LatencyControlLabelINTEL|LatencyControlConstraintINTEL|ConduitKernelArgumentINTEL|RegisterMapKernelArgumentINTEL|MMHostInterfaceAddressWidthINTEL|MMHostInterfaceDataWidthINTEL|MMHostInterfaceLatencyINTEL|MMHostInterfaceReadWriteModeINTEL|MMHostInterfaceMaxBurstINTEL|MMHostInterfaceWaitRequestINTEL|StableKernelArgumentINTEL|HostAccessINTEL|InitModeINTEL|ImplementInRegisterMapINTEL|CacheControlLoadINTEL|CacheControlStoreINTEL)\\b", "name": "keyword.spirv" }, "ValueEnum_BuiltIn": { - "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchIdKHR|LaunchSizeNV|LaunchSizeKHR|WorldRayOriginNV|WorldRayOriginKHR|WorldRayDirectionNV|WorldRayDirectionKHR|ObjectRayOriginNV|ObjectRayOriginKHR|ObjectRayDirectionNV|ObjectRayDirectionKHR|RayTminNV|RayTminKHR|RayTmaxNV|RayTmaxKHR|InstanceCustomIndexNV|InstanceCustomIndexKHR|ObjectToWorldNV|ObjectToWorldKHR|WorldToObjectNV|WorldToObjectKHR|HitTNV|HitTKHR|HitKindNV|HitKindKHR|IncomingRayFlagsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b", + "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|CoreIDARM|CoreCountARM|CoreMaxIDARM|WarpIDARM|WarpMaxIDARM|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|BaseVertex|BaseInstance|DrawIndex|PrimitiveShadingRateKHR|DeviceIndex|ViewIndex|ShadingRateKHR|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|RemainingRecursionLevelsAMDX|ShaderIndexAMDX|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordKHR|BaryCoordNoPerspKHR|FragSizeEXT|FragInvocationCountEXT|PrimitivePointIndicesEXT|PrimitiveLineIndicesEXT|PrimitiveTriangleIndicesEXT|CullPrimitiveEXT|LaunchIdKHR|LaunchSizeKHR|WorldRayOriginKHR|WorldRayDirectionKHR|ObjectRayOriginKHR|ObjectRayDirectionKHR|RayTminKHR|RayTmaxKHR|InstanceCustomIndexKHR|ObjectToWorldKHR|WorldToObjectKHR|HitTNV|HitKindKHR|CurrentRayTimeNV|HitTriangleVertexPositionsKHR|HitMicroTriangleVertexPositionsNV|HitMicroTriangleVertexBarycentricsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV|HitKindFrontFacingMicroTriangleNV|HitKindBackFacingMicroTriangleNV|CullMaskKHR)\\b", "name": "keyword.spirv" }, "ValueEnum_Scope": { - "match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR|ShaderCallKHR)\\b", + "match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|ShaderCallKHR)\\b", "name": "keyword.spirv" }, "ValueEnum_GroupOperation": { @@ -173,7 +219,7 @@ "name": "keyword.spirv" }, "ValueEnum_Capability": { - "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayTraversalPrimitiveCullingProvisionalKHR|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b", + "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|UniformDecoration|CoreBuiltinsARM|TileImageColorReadAccessEXT|TileImageDepthReadAccessEXT|TileImageStencilReadAccessEXT|CooperativeMatrixLayoutsARM|FragmentShadingRateKHR|SubgroupBallotKHR|DrawParameters|WorkgroupMemoryExplicitLayoutKHR|WorkgroupMemoryExplicitLayout8BitAccessKHR|WorkgroupMemoryExplicitLayout16BitAccessKHR|SubgroupVoteKHR|StorageBuffer16BitAccess|UniformAndStorageBuffer16BitAccess|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayQueryKHR|UntypedPointersKHR|RayTraversalPrimitiveCullingKHR|RayTracingKHR|TextureSampleWeightedQCOM|TextureBoxFilterQCOM|TextureBlockMatchQCOM|TextureBlockMatch2QCOM|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|Int64ImageEXT|ShaderClockKHR|ShaderEnqueueAMDX|QuadControlKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|MeshShadingEXT|FragmentBarycentricKHR|ComputeDerivativeGroupQuadsKHR|FragmentDensityEXT|GroupNonUniformPartitionedNV|ShaderNonUniform|RuntimeDescriptorArray|InputAttachmentArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexing|UniformBufferArrayNonUniformIndexing|SampledImageArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexing|StorageImageArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexing|RayTracingPositionFetchKHR|RayTracingNV|RayTracingMotionBlurNV|VulkanMemoryModel|VulkanMemoryModelDeviceScope|PhysicalStorageBufferAddresses|ComputeDerivativeGroupLinearKHR|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocation|DisplacementMicromapNV|RayTracingOpacityMicromapEXT|ShaderInvocationReorderNV|BindlessTextureNV|RayQueryPositionFetchKHR|AtomicFloat16VectorNV|RayTracingDisplacementMicromapNV|RawAccessChainsNV|CooperativeMatrixReductionsNV|CooperativeMatrixConversionsNV|CooperativeMatrixPerElementOperationsNV|CooperativeMatrixTensorAddressingNV|CooperativeMatrixBlockLoadsNV|TensorAddressingNV|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|RoundToInfinityINTEL|FloatingPointModeINTEL|IntegerFunctions2INTEL|FunctionPointersINTEL|IndirectReferencesINTEL|AsmINTEL|AtomicFloat32MinMaxEXT|AtomicFloat64MinMaxEXT|AtomicFloat16MinMaxEXT|VectorComputeINTEL|VectorAnyINTEL|ExpectAssumeKHR|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL|VariableLengthArrayINTEL|FunctionFloatControlINTEL|FPGAMemoryAttributesINTEL|FPFastMathModeINTEL|ArbitraryPrecisionIntegersINTEL|ArbitraryPrecisionFloatingPointINTEL|UnstructuredLoopControlsINTEL|FPGALoopControlsINTEL|KernelAttributesINTEL|FPGAKernelAttributesINTEL|FPGAMemoryAccessesINTEL|FPGAClusterAttributesINTEL|LoopFuseINTEL|FPGADSPControlINTEL|MemoryAccessAliasingINTEL|FPGAInvocationPipeliningAttributesINTEL|FPGABufferLocationINTEL|ArbitraryPrecisionFixedPointINTEL|USMStorageClassesINTEL|RuntimeAlignedAttributeINTEL|IOPipesINTEL|BlockingPipesINTEL|FPGARegINTEL|DotProductInputAll|DotProductInput4x8Bit|DotProductInput4x8BitPacked|DotProduct|RayCullMaskKHR|CooperativeMatrixKHR|ReplicatedCompositesEXT|BitInstructions|GroupNonUniformRotateKHR|FloatControls2|AtomicFloat32AddEXT|AtomicFloat64AddEXT|LongCompositesINTEL|OptNoneEXT|AtomicFloat16AddEXT|DebugInfoModuleINTEL|BFloat16ConversionINTEL|SplitBarrierINTEL|ArithmeticFenceEXT|FPGAClusterAttributesV2INTEL|FPGAKernelAttributesv2INTEL|FPMaxErrorINTEL|FPGALatencyControlINTEL|FPGAArgumentInterfacesINTEL|GlobalVariableHostAccessINTEL|GlobalVariableFPGADecorationsINTEL|SubgroupBufferPrefetchINTEL|GroupUniformArithmeticKHR|MaskedGatherScatterINTEL|CacheControlsINTEL|RegisterLimitsINTEL)\\b", "name": "keyword.spirv" }, "ValueEnum_RayQueryIntersection": { @@ -188,8 +234,52 @@ "match": "\\b(RayQueryCandidateIntersectionTriangleKHR|RayQueryCandidateIntersectionAABBKHR)\\b", "name": "keyword.spirv" }, + "ValueEnum_PackedVectorFormat": { + "match": "\\b(PackedVectorFormat4x8Bit)\\b", + "name": "keyword.spirv" + }, + "BitEnum_CooperativeMatrixOperands": { + "match": "\\b(NoneKHR|MatrixASignedComponentsKHR|MatrixBSignedComponentsKHR|MatrixCSignedComponentsKHR|MatrixResultSignedComponentsKHR|SaturatingAccumulationKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_CooperativeMatrixLayout": { + "match": "\\b(RowMajorKHR|ColumnMajorKHR|RowBlockedInterleavedARM|ColumnBlockedInterleavedARM)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_CooperativeMatrixUse": { + "match": "\\b(MatrixAKHR|MatrixBKHR|MatrixAccumulatorKHR)\\b", + "name": "keyword.spirv" + }, + "BitEnum_CooperativeMatrixReduce": { + "match": "\\b(Row|Column|2x2)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_TensorClampMode": { + "match": "\\b(Undefined|Constant|ClampToEdge|Repeat|RepeatMirrored)\\b", + "name": "keyword.spirv" + }, + "BitEnum_TensorAddressingOperands": { + "match": "\\b(None|TensorView|DecodeFunc)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_InitializationModeQualifier": { + "match": "\\b(InitOnDeviceReprogramINTEL|InitOnDeviceResetINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_LoadCacheControl": { + "match": "\\b(UncachedINTEL|CachedINTEL|StreamingINTEL|InvalidateAfterReadINTEL|ConstCachedINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_StoreCacheControl": { + "match": "\\b(UncachedINTEL|WriteThroughINTEL|WriteBackINTEL|StreamingINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_NamedMaximumNumberOfRegisters": { + "match": "\\b(AutoINTEL)\\b", + "name": "keyword.spirv" + }, "BitEnum_DebugInfoFlags": { - "match": "\\b(FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", + "match": "\\b(None|FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", "name": "keyword.spirv" }, "ValueEnum_DebugBaseTypeAttributeEncoding": { @@ -217,7 +307,7 @@ "name": "entity.name.function.spirv" }, "extopcode": { - "match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource)", + "match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource|DebugModuleINTEL)", "name": "entity.name.function.ext" }, "identifier": {
diff --git a/utils/vscode/src/langsvr.go b/utils/vscode/src/langsvr.go index b76e35f..56b11b2 100644 --- a/utils/vscode/src/langsvr.go +++ b/utils/vscode/src/langsvr.go
@@ -19,7 +19,6 @@ "context" "fmt" "io" - "io/ioutil" "log" "os" "path" @@ -67,7 +66,7 @@ // main entry point. func main() { - log.SetOutput(ioutil.Discard) + log.SetOutput(io.Discard) if enableDebugLogging { // create a log file in the executable's directory. if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil { @@ -213,6 +212,10 @@ ReferencesProvider: true, RenameProvider: true, DocumentFormattingProvider: true, + CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{"%"}, + ResolveProvider: false, + }, }, } return &res, nil @@ -225,9 +228,56 @@ log.Println("server.WillSaveWaitUntil()") return nil, nil } + +func markdownOpcode(op *schema.Opcode) string { + sb := strings.Builder{} + sb.WriteString(fmt.Sprintf("**%s** (%s)\n\n", op.Opname, op.Class)) + + for idx, operand := range op.Operands { + sb.WriteString(fmt.Sprintf("Operand %d%s: ", idx, operand.Quantifier)) + sb.WriteString(fmt.Sprintf("%s (%s)\n", operand.Name, operand.Kind.Kind)) + } + + return sb.String() +} + func (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) { log.Println("server.Completion()") - return nil, nil + f := s.getFile(p.TextDocument.URI) + if f == nil { + return nil, fmt.Errorf("Unknown file") + } + + if p.Context.TriggerCharacter == "%" { + idents := []lsp.CompletionItem{} + for name, ident := range f.res.Identifiers { + idents = append(idents, lsp.CompletionItem{ + Label: name, + Kind: 6, + Documentation: ident.Definition.Range.Text(f.res.Lines), + }) + } + res := &lsp.CompletionList{ + IsIncomplete: false, + Items: idents, + } + + return res, nil + } + + opcodes := []lsp.CompletionItem{} + for name, opcode := range schema.Opcodes { + opcodes = append(opcodes, lsp.CompletionItem{ + Label: name, + Kind: 3, + Documentation: markdownOpcode(opcode), + }) + } + res := &lsp.CompletionList{ + IsIncomplete: false, + Items: opcodes, + } + return res, nil } func (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) { log.Println("server.Resolve()") @@ -246,7 +296,7 @@ default: sb.WriteString(fmt.Sprintf("<Unhandled type '%T'>", v)) case *parser.Instruction: - sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Opcode.Opname)) + sb.WriteString(markdownOpcode(v.Opcode)) case *parser.Identifier: sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines))) case *parser.Operand:
diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go index ed02de4..b7931ae 100755 --- a/utils/vscode/src/schema/schema.go +++ b/utils/vscode/src/schema/schema.go
@@ -447,12 +447,60 @@ "OpPtrEqual": OpPtrEqual, "OpPtrNotEqual": OpPtrNotEqual, "OpPtrDiff": OpPtrDiff, + "OpColorAttachmentReadEXT": OpColorAttachmentReadEXT, + "OpDepthAttachmentReadEXT": OpDepthAttachmentReadEXT, + "OpStencilAttachmentReadEXT": OpStencilAttachmentReadEXT, + "OpTerminateInvocation": OpTerminateInvocation, + "OpTypeUntypedPointerKHR": OpTypeUntypedPointerKHR, + "OpUntypedVariableKHR": OpUntypedVariableKHR, + "OpUntypedAccessChainKHR": OpUntypedAccessChainKHR, + "OpUntypedInBoundsAccessChainKHR": OpUntypedInBoundsAccessChainKHR, "OpSubgroupBallotKHR": OpSubgroupBallotKHR, "OpSubgroupFirstInvocationKHR": OpSubgroupFirstInvocationKHR, + "OpUntypedPtrAccessChainKHR": OpUntypedPtrAccessChainKHR, + "OpUntypedInBoundsPtrAccessChainKHR": OpUntypedInBoundsPtrAccessChainKHR, + "OpUntypedArrayLengthKHR": OpUntypedArrayLengthKHR, + "OpUntypedPrefetchKHR": OpUntypedPrefetchKHR, "OpSubgroupAllKHR": OpSubgroupAllKHR, "OpSubgroupAnyKHR": OpSubgroupAnyKHR, "OpSubgroupAllEqualKHR": OpSubgroupAllEqualKHR, + "OpGroupNonUniformRotateKHR": OpGroupNonUniformRotateKHR, "OpSubgroupReadInvocationKHR": OpSubgroupReadInvocationKHR, + "OpExtInstWithForwardRefsKHR": OpExtInstWithForwardRefsKHR, + "OpTraceRayKHR": OpTraceRayKHR, + "OpExecuteCallableKHR": OpExecuteCallableKHR, + "OpConvertUToAccelerationStructureKHR": OpConvertUToAccelerationStructureKHR, + "OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR, + "OpTerminateRayKHR": OpTerminateRayKHR, + "OpSDot": OpSDot, + "OpUDot": OpUDot, + "OpSUDot": OpSUDot, + "OpSDotAccSat": OpSDotAccSat, + "OpUDotAccSat": OpUDotAccSat, + "OpSUDotAccSat": OpSUDotAccSat, + "OpTypeCooperativeMatrixKHR": OpTypeCooperativeMatrixKHR, + "OpCooperativeMatrixLoadKHR": OpCooperativeMatrixLoadKHR, + "OpCooperativeMatrixStoreKHR": OpCooperativeMatrixStoreKHR, + "OpCooperativeMatrixMulAddKHR": OpCooperativeMatrixMulAddKHR, + "OpCooperativeMatrixLengthKHR": OpCooperativeMatrixLengthKHR, + "OpConstantCompositeReplicateEXT": OpConstantCompositeReplicateEXT, + "OpSpecConstantCompositeReplicateEXT": OpSpecConstantCompositeReplicateEXT, + "OpCompositeConstructReplicateEXT": OpCompositeConstructReplicateEXT, + "OpTypeRayQueryKHR": OpTypeRayQueryKHR, + "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, + "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, + "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, + "OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR, + "OpRayQueryProceedKHR": OpRayQueryProceedKHR, + "OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR, + "OpImageSampleWeightedQCOM": OpImageSampleWeightedQCOM, + "OpImageBoxFilterQCOM": OpImageBoxFilterQCOM, + "OpImageBlockMatchSSDQCOM": OpImageBlockMatchSSDQCOM, + "OpImageBlockMatchSADQCOM": OpImageBlockMatchSADQCOM, + "OpImageBlockMatchWindowSSDQCOM": OpImageBlockMatchWindowSSDQCOM, + "OpImageBlockMatchWindowSADQCOM": OpImageBlockMatchWindowSADQCOM, + "OpImageBlockMatchGatherSSDQCOM": OpImageBlockMatchGatherSSDQCOM, + "OpImageBlockMatchGatherSADQCOM": OpImageBlockMatchGatherSADQCOM, "OpGroupIAddNonUniformAMD": OpGroupIAddNonUniformAMD, "OpGroupFAddNonUniformAMD": OpGroupFAddNonUniformAMD, "OpGroupFMinNonUniformAMD": OpGroupFMinNonUniformAMD, @@ -464,45 +512,66 @@ "OpFragmentMaskFetchAMD": OpFragmentMaskFetchAMD, "OpFragmentFetchAMD": OpFragmentFetchAMD, "OpReadClockKHR": OpReadClockKHR, + "OpAllocateNodePayloadsAMDX": OpAllocateNodePayloadsAMDX, + "OpEnqueueNodePayloadsAMDX": OpEnqueueNodePayloadsAMDX, + "OpTypeNodePayloadArrayAMDX": OpTypeNodePayloadArrayAMDX, + "OpFinishWritingNodePayloadAMDX": OpFinishWritingNodePayloadAMDX, + "OpNodePayloadArrayLengthAMDX": OpNodePayloadArrayLengthAMDX, + "OpIsNodePayloadValidAMDX": OpIsNodePayloadValidAMDX, + "OpConstantStringAMDX": OpConstantStringAMDX, + "OpSpecConstantStringAMDX": OpSpecConstantStringAMDX, + "OpGroupNonUniformQuadAllKHR": OpGroupNonUniformQuadAllKHR, + "OpGroupNonUniformQuadAnyKHR": OpGroupNonUniformQuadAnyKHR, + "OpHitObjectRecordHitMotionNV": OpHitObjectRecordHitMotionNV, + "OpHitObjectRecordHitWithIndexMotionNV": OpHitObjectRecordHitWithIndexMotionNV, + "OpHitObjectRecordMissMotionNV": OpHitObjectRecordMissMotionNV, + "OpHitObjectGetWorldToObjectNV": OpHitObjectGetWorldToObjectNV, + "OpHitObjectGetObjectToWorldNV": OpHitObjectGetObjectToWorldNV, + "OpHitObjectGetObjectRayDirectionNV": OpHitObjectGetObjectRayDirectionNV, + "OpHitObjectGetObjectRayOriginNV": OpHitObjectGetObjectRayOriginNV, + "OpHitObjectTraceRayMotionNV": OpHitObjectTraceRayMotionNV, + "OpHitObjectGetShaderRecordBufferHandleNV": OpHitObjectGetShaderRecordBufferHandleNV, + "OpHitObjectGetShaderBindingTableRecordIndexNV": OpHitObjectGetShaderBindingTableRecordIndexNV, + "OpHitObjectRecordEmptyNV": OpHitObjectRecordEmptyNV, + "OpHitObjectTraceRayNV": OpHitObjectTraceRayNV, + "OpHitObjectRecordHitNV": OpHitObjectRecordHitNV, + "OpHitObjectRecordHitWithIndexNV": OpHitObjectRecordHitWithIndexNV, + "OpHitObjectRecordMissNV": OpHitObjectRecordMissNV, + "OpHitObjectExecuteShaderNV": OpHitObjectExecuteShaderNV, + "OpHitObjectGetCurrentTimeNV": OpHitObjectGetCurrentTimeNV, + "OpHitObjectGetAttributesNV": OpHitObjectGetAttributesNV, + "OpHitObjectGetHitKindNV": OpHitObjectGetHitKindNV, + "OpHitObjectGetPrimitiveIndexNV": OpHitObjectGetPrimitiveIndexNV, + "OpHitObjectGetGeometryIndexNV": OpHitObjectGetGeometryIndexNV, + "OpHitObjectGetInstanceIdNV": OpHitObjectGetInstanceIdNV, + "OpHitObjectGetInstanceCustomIndexNV": OpHitObjectGetInstanceCustomIndexNV, + "OpHitObjectGetWorldRayDirectionNV": OpHitObjectGetWorldRayDirectionNV, + "OpHitObjectGetWorldRayOriginNV": OpHitObjectGetWorldRayOriginNV, + "OpHitObjectGetRayTMaxNV": OpHitObjectGetRayTMaxNV, + "OpHitObjectGetRayTMinNV": OpHitObjectGetRayTMinNV, + "OpHitObjectIsEmptyNV": OpHitObjectIsEmptyNV, + "OpHitObjectIsHitNV": OpHitObjectIsHitNV, + "OpHitObjectIsMissNV": OpHitObjectIsMissNV, + "OpReorderThreadWithHitObjectNV": OpReorderThreadWithHitObjectNV, + "OpReorderThreadWithHintNV": OpReorderThreadWithHintNV, + "OpTypeHitObjectNV": OpTypeHitObjectNV, "OpImageSampleFootprintNV": OpImageSampleFootprintNV, + "OpCooperativeMatrixConvertNV": OpCooperativeMatrixConvertNV, + "OpEmitMeshTasksEXT": OpEmitMeshTasksEXT, + "OpSetMeshOutputsEXT": OpSetMeshOutputsEXT, "OpGroupNonUniformPartitionNV": OpGroupNonUniformPartitionNV, "OpWritePackedPrimitiveIndices4x8NV": OpWritePackedPrimitiveIndices4x8NV, - "OpReportIntersectionNV": OpReportIntersectionNV, + "OpFetchMicroTriangleVertexPositionNV": OpFetchMicroTriangleVertexPositionNV, + "OpFetchMicroTriangleVertexBarycentricNV": OpFetchMicroTriangleVertexBarycentricNV, "OpReportIntersectionKHR": OpReportIntersectionKHR, "OpIgnoreIntersectionNV": OpIgnoreIntersectionNV, - "OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR, "OpTerminateRayNV": OpTerminateRayNV, - "OpTerminateRayKHR": OpTerminateRayKHR, "OpTraceNV": OpTraceNV, - "OpTraceRayKHR": OpTraceRayKHR, - "OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV, + "OpTraceMotionNV": OpTraceMotionNV, + "OpTraceRayMotionNV": OpTraceRayMotionNV, + "OpRayQueryGetIntersectionTriangleVertexPositionsKHR": OpRayQueryGetIntersectionTriangleVertexPositionsKHR, "OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR, - "OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR, - "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, - "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, - "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, - "OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR, - "OpRayQueryProceedKHR": OpRayQueryProceedKHR, - "OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR, - "OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR, - "OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR, - "OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR, - "OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR, - "OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR, - "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR, - "OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR, - "OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR, - "OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR, - "OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR, - "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR, - "OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR, - "OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR, - "OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR, - "OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR, - "OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR, - "OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR, "OpExecuteCallableNV": OpExecuteCallableNV, - "OpExecuteCallableKHR": OpExecuteCallableKHR, "OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV, "OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV, "OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV, @@ -510,8 +579,33 @@ "OpCooperativeMatrixLengthNV": OpCooperativeMatrixLengthNV, "OpBeginInvocationInterlockEXT": OpBeginInvocationInterlockEXT, "OpEndInvocationInterlockEXT": OpEndInvocationInterlockEXT, - "OpDemoteToHelperInvocationEXT": OpDemoteToHelperInvocationEXT, + "OpCooperativeMatrixReduceNV": OpCooperativeMatrixReduceNV, + "OpCooperativeMatrixLoadTensorNV": OpCooperativeMatrixLoadTensorNV, + "OpCooperativeMatrixStoreTensorNV": OpCooperativeMatrixStoreTensorNV, + "OpCooperativeMatrixPerElementOpNV": OpCooperativeMatrixPerElementOpNV, + "OpTypeTensorLayoutNV": OpTypeTensorLayoutNV, + "OpTypeTensorViewNV": OpTypeTensorViewNV, + "OpCreateTensorLayoutNV": OpCreateTensorLayoutNV, + "OpTensorLayoutSetDimensionNV": OpTensorLayoutSetDimensionNV, + "OpTensorLayoutSetStrideNV": OpTensorLayoutSetStrideNV, + "OpTensorLayoutSliceNV": OpTensorLayoutSliceNV, + "OpTensorLayoutSetClampValueNV": OpTensorLayoutSetClampValueNV, + "OpCreateTensorViewNV": OpCreateTensorViewNV, + "OpTensorViewSetDimensionNV": OpTensorViewSetDimensionNV, + "OpTensorViewSetStrideNV": OpTensorViewSetStrideNV, + "OpDemoteToHelperInvocation": OpDemoteToHelperInvocation, "OpIsHelperInvocationEXT": OpIsHelperInvocationEXT, + "OpTensorViewSetClipNV": OpTensorViewSetClipNV, + "OpTensorLayoutSetBlockSizeNV": OpTensorLayoutSetBlockSizeNV, + "OpCooperativeMatrixTransposeNV": OpCooperativeMatrixTransposeNV, + "OpConvertUToImageNV": OpConvertUToImageNV, + "OpConvertUToSamplerNV": OpConvertUToSamplerNV, + "OpConvertImageToUNV": OpConvertImageToUNV, + "OpConvertSamplerToUNV": OpConvertSamplerToUNV, + "OpConvertUToSampledImageNV": OpConvertUToSampledImageNV, + "OpConvertSampledImageToUNV": OpConvertSampledImageToUNV, + "OpSamplerImageAddressingModeNV": OpSamplerImageAddressingModeNV, + "OpRawAccessChainNV": OpRawAccessChainNV, "OpSubgroupShuffleINTEL": OpSubgroupShuffleINTEL, "OpSubgroupShuffleDownINTEL": OpSubgroupShuffleDownINTEL, "OpSubgroupShuffleUpINTEL": OpSubgroupShuffleUpINTEL, @@ -536,10 +630,17 @@ "OpUSubSatINTEL": OpUSubSatINTEL, "OpIMul32x16INTEL": OpIMul32x16INTEL, "OpUMul32x16INTEL": OpUMul32x16INTEL, + "OpConstantFunctionPointerINTEL": OpConstantFunctionPointerINTEL, + "OpFunctionPointerCallINTEL": OpFunctionPointerCallINTEL, + "OpAsmTargetINTEL": OpAsmTargetINTEL, + "OpAsmINTEL": OpAsmINTEL, + "OpAsmCallINTEL": OpAsmCallINTEL, + "OpAtomicFMinEXT": OpAtomicFMinEXT, + "OpAtomicFMaxEXT": OpAtomicFMaxEXT, + "OpAssumeTrueKHR": OpAssumeTrueKHR, + "OpExpectKHR": OpExpectKHR, "OpDecorateString": OpDecorateString, - "OpDecorateStringGOOGLE": OpDecorateStringGOOGLE, "OpMemberDecorateString": OpMemberDecorateString, - "OpMemberDecorateStringGOOGLE": OpMemberDecorateStringGOOGLE, "OpVmeImageINTEL": OpVmeImageINTEL, "OpTypeVmeImageINTEL": OpTypeVmeImageINTEL, "OpTypeAvcImePayloadINTEL": OpTypeAvcImePayloadINTEL, @@ -658,6 +759,109 @@ "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL, "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL, "OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL, + "OpVariableLengthArrayINTEL": OpVariableLengthArrayINTEL, + "OpSaveMemoryINTEL": OpSaveMemoryINTEL, + "OpRestoreMemoryINTEL": OpRestoreMemoryINTEL, + "OpArbitraryFloatSinCosPiINTEL": OpArbitraryFloatSinCosPiINTEL, + "OpArbitraryFloatCastINTEL": OpArbitraryFloatCastINTEL, + "OpArbitraryFloatCastFromIntINTEL": OpArbitraryFloatCastFromIntINTEL, + "OpArbitraryFloatCastToIntINTEL": OpArbitraryFloatCastToIntINTEL, + "OpArbitraryFloatAddINTEL": OpArbitraryFloatAddINTEL, + "OpArbitraryFloatSubINTEL": OpArbitraryFloatSubINTEL, + "OpArbitraryFloatMulINTEL": OpArbitraryFloatMulINTEL, + "OpArbitraryFloatDivINTEL": OpArbitraryFloatDivINTEL, + "OpArbitraryFloatGTINTEL": OpArbitraryFloatGTINTEL, + "OpArbitraryFloatGEINTEL": OpArbitraryFloatGEINTEL, + "OpArbitraryFloatLTINTEL": OpArbitraryFloatLTINTEL, + "OpArbitraryFloatLEINTEL": OpArbitraryFloatLEINTEL, + "OpArbitraryFloatEQINTEL": OpArbitraryFloatEQINTEL, + "OpArbitraryFloatRecipINTEL": OpArbitraryFloatRecipINTEL, + "OpArbitraryFloatRSqrtINTEL": OpArbitraryFloatRSqrtINTEL, + "OpArbitraryFloatCbrtINTEL": OpArbitraryFloatCbrtINTEL, + "OpArbitraryFloatHypotINTEL": OpArbitraryFloatHypotINTEL, + "OpArbitraryFloatSqrtINTEL": OpArbitraryFloatSqrtINTEL, + "OpArbitraryFloatLogINTEL": OpArbitraryFloatLogINTEL, + "OpArbitraryFloatLog2INTEL": OpArbitraryFloatLog2INTEL, + "OpArbitraryFloatLog10INTEL": OpArbitraryFloatLog10INTEL, + "OpArbitraryFloatLog1pINTEL": OpArbitraryFloatLog1pINTEL, + "OpArbitraryFloatExpINTEL": OpArbitraryFloatExpINTEL, + "OpArbitraryFloatExp2INTEL": OpArbitraryFloatExp2INTEL, + "OpArbitraryFloatExp10INTEL": OpArbitraryFloatExp10INTEL, + "OpArbitraryFloatExpm1INTEL": OpArbitraryFloatExpm1INTEL, + "OpArbitraryFloatSinINTEL": OpArbitraryFloatSinINTEL, + "OpArbitraryFloatCosINTEL": OpArbitraryFloatCosINTEL, + "OpArbitraryFloatSinCosINTEL": OpArbitraryFloatSinCosINTEL, + "OpArbitraryFloatSinPiINTEL": OpArbitraryFloatSinPiINTEL, + "OpArbitraryFloatCosPiINTEL": OpArbitraryFloatCosPiINTEL, + "OpArbitraryFloatASinINTEL": OpArbitraryFloatASinINTEL, + "OpArbitraryFloatASinPiINTEL": OpArbitraryFloatASinPiINTEL, + "OpArbitraryFloatACosINTEL": OpArbitraryFloatACosINTEL, + "OpArbitraryFloatACosPiINTEL": OpArbitraryFloatACosPiINTEL, + "OpArbitraryFloatATanINTEL": OpArbitraryFloatATanINTEL, + "OpArbitraryFloatATanPiINTEL": OpArbitraryFloatATanPiINTEL, + "OpArbitraryFloatATan2INTEL": OpArbitraryFloatATan2INTEL, + "OpArbitraryFloatPowINTEL": OpArbitraryFloatPowINTEL, + "OpArbitraryFloatPowRINTEL": OpArbitraryFloatPowRINTEL, + "OpArbitraryFloatPowNINTEL": OpArbitraryFloatPowNINTEL, + "OpLoopControlINTEL": OpLoopControlINTEL, + "OpAliasDomainDeclINTEL": OpAliasDomainDeclINTEL, + "OpAliasScopeDeclINTEL": OpAliasScopeDeclINTEL, + "OpAliasScopeListDeclINTEL": OpAliasScopeListDeclINTEL, + "OpFixedSqrtINTEL": OpFixedSqrtINTEL, + "OpFixedRecipINTEL": OpFixedRecipINTEL, + "OpFixedRsqrtINTEL": OpFixedRsqrtINTEL, + "OpFixedSinINTEL": OpFixedSinINTEL, + "OpFixedCosINTEL": OpFixedCosINTEL, + "OpFixedSinCosINTEL": OpFixedSinCosINTEL, + "OpFixedSinPiINTEL": OpFixedSinPiINTEL, + "OpFixedCosPiINTEL": OpFixedCosPiINTEL, + "OpFixedSinCosPiINTEL": OpFixedSinCosPiINTEL, + "OpFixedLogINTEL": OpFixedLogINTEL, + "OpFixedExpINTEL": OpFixedExpINTEL, + "OpPtrCastToCrossWorkgroupINTEL": OpPtrCastToCrossWorkgroupINTEL, + "OpCrossWorkgroupCastToPtrINTEL": OpCrossWorkgroupCastToPtrINTEL, + "OpReadPipeBlockingINTEL": OpReadPipeBlockingINTEL, + "OpWritePipeBlockingINTEL": OpWritePipeBlockingINTEL, + "OpFPGARegINTEL": OpFPGARegINTEL, + "OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR, + "OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR, + "OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR, + "OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR, + "OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR, + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR, + "OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR, + "OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR, + "OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR, + "OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR, + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR, + "OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR, + "OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR, + "OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR, + "OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR, + "OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR, + "OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR, + "OpAtomicFAddEXT": OpAtomicFAddEXT, + "OpTypeBufferSurfaceINTEL": OpTypeBufferSurfaceINTEL, + "OpTypeStructContinuedINTEL": OpTypeStructContinuedINTEL, + "OpConstantCompositeContinuedINTEL": OpConstantCompositeContinuedINTEL, + "OpSpecConstantCompositeContinuedINTEL": OpSpecConstantCompositeContinuedINTEL, + "OpCompositeConstructContinuedINTEL": OpCompositeConstructContinuedINTEL, + "OpConvertFToBF16INTEL": OpConvertFToBF16INTEL, + "OpConvertBF16ToFINTEL": OpConvertBF16ToFINTEL, + "OpControlBarrierArriveINTEL": OpControlBarrierArriveINTEL, + "OpControlBarrierWaitINTEL": OpControlBarrierWaitINTEL, + "OpArithmeticFenceEXT": OpArithmeticFenceEXT, + "OpSubgroupBlockPrefetchINTEL": OpSubgroupBlockPrefetchINTEL, + "OpGroupIMulKHR": OpGroupIMulKHR, + "OpGroupFMulKHR": OpGroupFMulKHR, + "OpGroupBitwiseAndKHR": OpGroupBitwiseAndKHR, + "OpGroupBitwiseOrKHR": OpGroupBitwiseOrKHR, + "OpGroupBitwiseXorKHR": OpGroupBitwiseXorKHR, + "OpGroupLogicalAndKHR": OpGroupLogicalAndKHR, + "OpGroupLogicalOrKHR": OpGroupLogicalOrKHR, + "OpGroupLogicalXorKHR": OpGroupLogicalXorKHR, + "OpMaskedGatherINTEL": OpMaskedGatherINTEL, + "OpMaskedScatterINTEL": OpMaskedScatterINTEL, } // ExtOpcodes is a map of extension name to Opcode description list. @@ -946,6 +1150,7 @@ "DebugMacroUndef": OpenCLDebugInfo100_DebugMacroUndef, "DebugImportedEntity": OpenCLDebugInfo100_DebugImportedEntity, "DebugSource": OpenCLDebugInfo100_DebugSource, + "DebugModuleINTEL": OpenCLDebugInfo100_DebugModuleINTEL, }, } @@ -1297,6 +1502,11 @@ Name: "'Width'", Quantifier: "", }, + Operand { + Kind: OperandKindFPEncoding, + Name: "'Floating Point Encoding'", + Quantifier: "?", + }, }, } OpTypeVector = &Opcode { @@ -10022,6 +10232,197 @@ }, }, } + OpColorAttachmentReadEXT = &Opcode { + Opname: "OpColorAttachmentReadEXT", + Class: "Image", + Opcode: 4160, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Attachment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "?", + }, + }, + } + OpDepthAttachmentReadEXT = &Opcode { + Opname: "OpDepthAttachmentReadEXT", + Class: "Image", + Opcode: 4161, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "?", + }, + }, + } + OpStencilAttachmentReadEXT = &Opcode { + Opname: "OpStencilAttachmentReadEXT", + Class: "Image", + Opcode: 4162, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "?", + }, + }, + } + OpTerminateInvocation = &Opcode { + Opname: "OpTerminateInvocation", + Class: "Control-Flow", + Opcode: 4416, + Operands: []Operand { + }, + } + OpTypeUntypedPointerKHR = &Opcode { + Opname: "OpTypeUntypedPointerKHR", + Class: "Type-Declaration", + Opcode: 4417, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + }, + } + OpUntypedVariableKHR = &Opcode { + Opname: "OpUntypedVariableKHR", + Class: "Memory", + Opcode: 4418, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data Type'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Initializer'", + Quantifier: "?", + }, + }, + } + OpUntypedAccessChainKHR = &Opcode { + Opname: "OpUntypedAccessChainKHR", + Class: "Memory", + Opcode: 4419, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpUntypedInBoundsAccessChainKHR = &Opcode { + Opname: "OpUntypedInBoundsAccessChainKHR", + Class: "Memory", + Opcode: 4420, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } OpSubgroupBallotKHR = &Opcode { Opname: "OpSubgroupBallotKHR", Class: "Group", @@ -10066,6 +10467,144 @@ }, }, } + OpUntypedPtrAccessChainKHR = &Opcode { + Opname: "OpUntypedPtrAccessChainKHR", + Class: "Memory", + Opcode: 4423, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpUntypedInBoundsPtrAccessChainKHR = &Opcode { + Opname: "OpUntypedInBoundsPtrAccessChainKHR", + Class: "Memory", + Opcode: 4424, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpUntypedArrayLengthKHR = &Opcode { + Opname: "OpUntypedArrayLengthKHR", + Class: "Memory", + Opcode: 4425, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Array member'", + Quantifier: "", + }, + }, + } + OpUntypedPrefetchKHR = &Opcode { + Opname: "OpUntypedPrefetchKHR", + Class: "Memory", + Opcode: 4426, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Bytes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RW'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Locality'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cache Type'", + Quantifier: "?", + }, + }, + } OpSubgroupAllKHR = &Opcode { Opname: "OpSubgroupAllKHR", Class: "Group", @@ -10132,6 +10671,43 @@ }, }, } + OpGroupNonUniformRotateKHR = &Opcode { + Opname: "OpGroupNonUniformRotateKHR", + Class: "Group", + Opcode: 4431, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } OpSubgroupReadInvocationKHR = &Opcode { Opname: "OpSubgroupReadInvocationKHR", Class: "Group", @@ -10159,6 +10735,1056 @@ }, }, } + OpExtInstWithForwardRefsKHR = &Opcode { + Opname: "OpExtInstWithForwardRefsKHR", + Class: "Extension", + Opcode: 4433, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Set'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralExtInstInteger, + Name: "'Instruction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1', + 'Operand 2', + ...", + Quantifier: "*", + }, + }, + } + OpTraceRayKHR = &Opcode { + Opname: "OpTraceRayKHR", + Class: "Reserved", + Opcode: 4445, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpExecuteCallableKHR = &Opcode { + Opname: "OpExecuteCallableKHR", + Class: "Reserved", + Opcode: 4446, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Callable Data'", + Quantifier: "", + }, + }, + } + OpConvertUToAccelerationStructureKHR = &Opcode { + Opname: "OpConvertUToAccelerationStructureKHR", + Class: "Reserved", + Opcode: 4447, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + }, + } + OpIgnoreIntersectionKHR = &Opcode { + Opname: "OpIgnoreIntersectionKHR", + Class: "Reserved", + Opcode: 4448, + Operands: []Operand { + }, + } + OpTerminateRayKHR = &Opcode { + Opname: "OpTerminateRayKHR", + Class: "Reserved", + Opcode: 4449, + Operands: []Operand { + }, + } + OpSDot = &Opcode { + Opname: "OpSDot", + Class: "Arithmetic", + Opcode: 4450, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpUDot = &Opcode { + Opname: "OpUDot", + Class: "Arithmetic", + Opcode: 4451, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpSUDot = &Opcode { + Opname: "OpSUDot", + Class: "Arithmetic", + Opcode: 4452, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpSDotAccSat = &Opcode { + Opname: "OpSDotAccSat", + Class: "Arithmetic", + Opcode: 4453, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accumulator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpUDotAccSat = &Opcode { + Opname: "OpUDotAccSat", + Class: "Arithmetic", + Opcode: 4454, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accumulator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpSUDotAccSat = &Opcode { + Opname: "OpSUDotAccSat", + Class: "Arithmetic", + Opcode: 4455, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accumulator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpTypeCooperativeMatrixKHR = &Opcode { + Opname: "OpTypeCooperativeMatrixKHR", + Class: "Type-Declaration", + Opcode: 4456, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Rows'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Columns'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Use'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLoadKHR = &Opcode { + Opname: "OpCooperativeMatrixLoadKHR", + Class: "Memory", + Opcode: 4457, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'MemoryLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixStoreKHR = &Opcode { + Opname: "OpCooperativeMatrixStoreKHR", + Class: "Memory", + Opcode: 4458, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'MemoryLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixMulAddKHR = &Opcode { + Opname: "OpCooperativeMatrixMulAddKHR", + Class: "Arithmetic", + Opcode: 4459, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'C'", + Quantifier: "", + }, + Operand { + Kind: OperandKindCooperativeMatrixOperands, + Name: "'Cooperative Matrix Operands'", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixLengthKHR = &Opcode { + Opname: "OpCooperativeMatrixLengthKHR", + Class: "Miscellaneous", + Opcode: 4460, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + }, + } + OpConstantCompositeReplicateEXT = &Opcode { + Opname: "OpConstantCompositeReplicateEXT", + Class: "Constant-Creation", + Opcode: 4461, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSpecConstantCompositeReplicateEXT = &Opcode { + Opname: "OpSpecConstantCompositeReplicateEXT", + Class: "Constant-Creation", + Opcode: 4462, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpCompositeConstructReplicateEXT = &Opcode { + Opname: "OpCompositeConstructReplicateEXT", + Class: "Composite", + Opcode: 4463, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpTypeRayQueryKHR = &Opcode { + Opname: "OpTypeRayQueryKHR", + Class: "Type-Declaration", + Opcode: 4472, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpRayQueryInitializeKHR = &Opcode { + Opname: "OpRayQueryInitializeKHR", + Class: "Reserved", + Opcode: 4473, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'CullMask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayOrigin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayDirection'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMax'", + Quantifier: "", + }, + }, + } + OpRayQueryTerminateKHR = &Opcode { + Opname: "OpRayQueryTerminateKHR", + Class: "Reserved", + Opcode: 4474, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGenerateIntersectionKHR = &Opcode { + Opname: "OpRayQueryGenerateIntersectionKHR", + Class: "Reserved", + Opcode: 4475, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitT'", + Quantifier: "", + }, + }, + } + OpRayQueryConfirmIntersectionKHR = &Opcode { + Opname: "OpRayQueryConfirmIntersectionKHR", + Class: "Reserved", + Opcode: 4476, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryProceedKHR = &Opcode { + Opname: "OpRayQueryProceedKHR", + Class: "Reserved", + Opcode: 4477, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTypeKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTypeKHR", + Class: "Reserved", + Opcode: 4479, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpImageSampleWeightedQCOM = &Opcode { + Opname: "OpImageSampleWeightedQCOM", + Class: "Image", + Opcode: 4480, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Texture'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Weights'", + Quantifier: "", + }, + }, + } + OpImageBoxFilterQCOM = &Opcode { + Opname: "OpImageBoxFilterQCOM", + Class: "Image", + Opcode: 4481, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Texture'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Box Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchSSDQCOM = &Opcode { + Opname: "OpImageBlockMatchSSDQCOM", + Class: "Image", + Opcode: 4482, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchSADQCOM = &Opcode { + Opname: "OpImageBlockMatchSADQCOM", + Class: "Image", + Opcode: 4483, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchWindowSSDQCOM = &Opcode { + Opname: "OpImageBlockMatchWindowSSDQCOM", + Class: "Image", + Opcode: 4500, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchWindowSADQCOM = &Opcode { + Opname: "OpImageBlockMatchWindowSADQCOM", + Class: "Image", + Opcode: 4501, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchGatherSSDQCOM = &Opcode { + Opname: "OpImageBlockMatchGatherSSDQCOM", + Class: "Image", + Opcode: 4502, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchGatherSADQCOM = &Opcode { + Opname: "OpImageBlockMatchGatherSADQCOM", + Class: "Image", + Opcode: 4503, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } OpGroupIAddNonUniformAMD = &Opcode { Opname: "OpGroupIAddNonUniformAMD", Class: "Group", @@ -10491,7 +12117,1238 @@ }, Operand { Kind: OperandKindIdScope, - Name: "'Execution'", + Name: "'Scope'", + Quantifier: "", + }, + }, + } + OpAllocateNodePayloadsAMDX = &Opcode { + Opname: "OpAllocateNodePayloadsAMDX", + Class: "Reserved", + Opcode: 5074, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Visibility'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Node Index'", + Quantifier: "", + }, + }, + } + OpEnqueueNodePayloadsAMDX = &Opcode { + Opname: "OpEnqueueNodePayloadsAMDX", + Class: "Reserved", + Opcode: 5075, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Array'", + Quantifier: "", + }, + }, + } + OpTypeNodePayloadArrayAMDX = &Opcode { + Opname: "OpTypeNodePayloadArrayAMDX", + Class: "Reserved", + Opcode: 5076, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Type'", + Quantifier: "", + }, + }, + } + OpFinishWritingNodePayloadAMDX = &Opcode { + Opname: "OpFinishWritingNodePayloadAMDX", + Class: "Reserved", + Opcode: 5078, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpNodePayloadArrayLengthAMDX = &Opcode { + Opname: "OpNodePayloadArrayLengthAMDX", + Class: "Reserved", + Opcode: 5090, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Array'", + Quantifier: "", + }, + }, + } + OpIsNodePayloadValidAMDX = &Opcode { + Opname: "OpIsNodePayloadValidAMDX", + Class: "Reserved", + Opcode: 5101, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Node Index'", + Quantifier: "", + }, + }, + } + OpConstantStringAMDX = &Opcode { + Opname: "OpConstantStringAMDX", + Class: "Reserved", + Opcode: 5103, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Literal String'", + Quantifier: "", + }, + }, + } + OpSpecConstantStringAMDX = &Opcode { + Opname: "OpSpecConstantStringAMDX", + Class: "Reserved", + Opcode: 5104, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Literal String'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformQuadAllKHR = &Opcode { + Opname: "OpGroupNonUniformQuadAllKHR", + Class: "Non-Uniform", + Opcode: 5110, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformQuadAnyKHR = &Opcode { + Opname: "OpGroupNonUniformQuadAnyKHR", + Class: "Non-Uniform", + Opcode: 5111, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitMotionNV = &Opcode { + Opname: "OpHitObjectRecordHitMotionNV", + Class: "Reserved", + Opcode: 5249, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitWithIndexMotionNV = &Opcode { + Opname: "OpHitObjectRecordHitWithIndexMotionNV", + Class: "Reserved", + Opcode: 5250, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordMissMotionNV = &Opcode { + Opname: "OpHitObjectRecordMissMotionNV", + Class: "Reserved", + Opcode: 5251, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current Time'", + Quantifier: "", + }, + }, + } + OpHitObjectGetWorldToObjectNV = &Opcode { + Opname: "OpHitObjectGetWorldToObjectNV", + Class: "Reserved", + Opcode: 5252, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetObjectToWorldNV = &Opcode { + Opname: "OpHitObjectGetObjectToWorldNV", + Class: "Reserved", + Opcode: 5253, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetObjectRayDirectionNV = &Opcode { + Opname: "OpHitObjectGetObjectRayDirectionNV", + Class: "Reserved", + Opcode: 5254, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetObjectRayOriginNV = &Opcode { + Opname: "OpHitObjectGetObjectRayOriginNV", + Class: "Reserved", + Opcode: 5255, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectTraceRayMotionNV = &Opcode { + Opname: "OpHitObjectTraceRayMotionNV", + Class: "Reserved", + Opcode: 5256, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cullmask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpHitObjectGetShaderRecordBufferHandleNV = &Opcode { + Opname: "OpHitObjectGetShaderRecordBufferHandleNV", + Class: "Reserved", + Opcode: 5257, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetShaderBindingTableRecordIndexNV = &Opcode { + Opname: "OpHitObjectGetShaderBindingTableRecordIndexNV", + Class: "Reserved", + Opcode: 5258, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordEmptyNV = &Opcode { + Opname: "OpHitObjectRecordEmptyNV", + Class: "Reserved", + Opcode: 5259, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectTraceRayNV = &Opcode { + Opname: "OpHitObjectTraceRayNV", + Class: "Reserved", + Opcode: 5260, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cullmask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitNV = &Opcode { + Opname: "OpHitObjectRecordHitNV", + Class: "Reserved", + Opcode: 5261, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitWithIndexNV = &Opcode { + Opname: "OpHitObjectRecordHitWithIndexNV", + Class: "Reserved", + Opcode: 5262, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordMissNV = &Opcode { + Opname: "OpHitObjectRecordMissNV", + Class: "Reserved", + Opcode: 5263, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + }, + } + OpHitObjectExecuteShaderNV = &Opcode { + Opname: "OpHitObjectExecuteShaderNV", + Class: "Reserved", + Opcode: 5264, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpHitObjectGetCurrentTimeNV = &Opcode { + Opname: "OpHitObjectGetCurrentTimeNV", + Class: "Reserved", + Opcode: 5265, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetAttributesNV = &Opcode { + Opname: "OpHitObjectGetAttributesNV", + Class: "Reserved", + Opcode: 5266, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object Attribute'", + Quantifier: "", + }, + }, + } + OpHitObjectGetHitKindNV = &Opcode { + Opname: "OpHitObjectGetHitKindNV", + Class: "Reserved", + Opcode: 5267, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetPrimitiveIndexNV = &Opcode { + Opname: "OpHitObjectGetPrimitiveIndexNV", + Class: "Reserved", + Opcode: 5268, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetGeometryIndexNV = &Opcode { + Opname: "OpHitObjectGetGeometryIndexNV", + Class: "Reserved", + Opcode: 5269, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetInstanceIdNV = &Opcode { + Opname: "OpHitObjectGetInstanceIdNV", + Class: "Reserved", + Opcode: 5270, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetInstanceCustomIndexNV = &Opcode { + Opname: "OpHitObjectGetInstanceCustomIndexNV", + Class: "Reserved", + Opcode: 5271, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetWorldRayDirectionNV = &Opcode { + Opname: "OpHitObjectGetWorldRayDirectionNV", + Class: "Reserved", + Opcode: 5272, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetWorldRayOriginNV = &Opcode { + Opname: "OpHitObjectGetWorldRayOriginNV", + Class: "Reserved", + Opcode: 5273, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetRayTMaxNV = &Opcode { + Opname: "OpHitObjectGetRayTMaxNV", + Class: "Reserved", + Opcode: 5274, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetRayTMinNV = &Opcode { + Opname: "OpHitObjectGetRayTMinNV", + Class: "Reserved", + Opcode: 5275, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectIsEmptyNV = &Opcode { + Opname: "OpHitObjectIsEmptyNV", + Class: "Reserved", + Opcode: 5276, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectIsHitNV = &Opcode { + Opname: "OpHitObjectIsHitNV", + Class: "Reserved", + Opcode: 5277, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectIsMissNV = &Opcode { + Opname: "OpHitObjectIsMissNV", + Class: "Reserved", + Opcode: 5278, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpReorderThreadWithHitObjectNV = &Opcode { + Opname: "OpReorderThreadWithHitObjectNV", + Class: "Reserved", + Opcode: 5279, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hint'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bits'", + Quantifier: "?", + }, + }, + } + OpReorderThreadWithHintNV = &Opcode { + Opname: "OpReorderThreadWithHintNV", + Class: "Reserved", + Opcode: 5280, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hint'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bits'", + Quantifier: "", + }, + }, + } + OpTypeHitObjectNV = &Opcode { + Opname: "OpTypeHitObjectNV", + Class: "Type-Declaration", + Opcode: 5281, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", Quantifier: "", }, }, @@ -10538,6 +13395,72 @@ }, }, } + OpCooperativeMatrixConvertNV = &Opcode { + Opname: "OpCooperativeMatrixConvertNV", + Class: "Conversion", + Opcode: 5293, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpEmitMeshTasksEXT = &Opcode { + Opname: "OpEmitMeshTasksEXT", + Class: "Reserved", + Opcode: 5294, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Group Count X'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Group Count Y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Group Count Z'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "?", + }, + }, + } + OpSetMeshOutputsEXT = &Opcode { + Opname: "OpSetMeshOutputsEXT", + Class: "Reserved", + Opcode: 5295, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Vertex Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Primitive Count'", + Quantifier: "", + }, + }, + } OpGroupNonUniformPartitionNV = &Opcode { Opname: "OpGroupNonUniformPartitionNV", Class: "Non-Uniform", @@ -10577,10 +13500,10 @@ }, }, } - OpReportIntersectionNV = &Opcode { - Opname: "OpReportIntersectionNV", + OpFetchMicroTriangleVertexPositionNV = &Opcode { + Opname: "OpFetchMicroTriangleVertexPositionNV", Class: "Reserved", - Opcode: 5334, + Opcode: 5300, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10594,12 +13517,69 @@ }, Operand { Kind: OperandKindIdRef, - Name: "'Hit'", + Name: "'Accel'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'HitKind'", + Name: "'Instance Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Geometry Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Primitive Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Barycentric'", + Quantifier: "", + }, + }, + } + OpFetchMicroTriangleVertexBarycentricNV = &Opcode { + Opname: "OpFetchMicroTriangleVertexBarycentricNV", + Class: "Reserved", + Opcode: 5301, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Instance Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Geometry Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Primitive Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Barycentric'", Quantifier: "", }, }, @@ -10638,13 +13618,6 @@ Operands: []Operand { }, } - OpIgnoreIntersectionKHR = &Opcode { - Opname: "OpIgnoreIntersectionKHR", - Class: "Reserved", - Opcode: 5335, - Operands: []Operand { - }, - } OpTerminateRayNV = &Opcode { Opname: "OpTerminateRayNV", Class: "Reserved", @@ -10652,13 +13625,6 @@ Operands: []Operand { }, } - OpTerminateRayKHR = &Opcode { - Opname: "OpTerminateRayKHR", - Class: "Reserved", - Opcode: 5336, - Operands: []Operand { - }, - } OpTraceNV = &Opcode { Opname: "OpTraceNV", Class: "Reserved", @@ -10721,10 +13687,10 @@ }, }, } - OpTraceRayKHR = &Opcode { - Opname: "OpTraceRayKHR", + OpTraceMotionNV = &Opcode { + Opname: "OpTraceMotionNV", Class: "Reserved", - Opcode: 5337, + Opcode: 5338, Operands: []Operand { Operand { Kind: OperandKindIdRef, @@ -10778,161 +13744,87 @@ }, Operand { Kind: OperandKindIdRef, + Name: "'Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, Name: "'PayloadId'", Quantifier: "", }, }, } - OpTypeAccelerationStructureNV = &Opcode { - Opname: "OpTypeAccelerationStructureNV", + OpTraceRayMotionNV = &Opcode { + Opname: "OpTraceRayMotionNV", Class: "Reserved", - Opcode: 5341, + Opcode: 5339, Operands: []Operand { Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - }, - } - OpTypeAccelerationStructureKHR = &Opcode { - Opname: "OpTypeAccelerationStructureKHR", - Class: "Reserved", - Opcode: 5341, - Operands: []Operand { - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - }, - } - OpTypeRayQueryProvisionalKHR = &Opcode { - Opname: "OpTypeRayQueryProvisionalKHR", - Class: "Reserved", - Opcode: 4472, - Operands: []Operand { - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - }, - } - OpRayQueryInitializeKHR = &Opcode { - Opname: "OpRayQueryInitializeKHR", - Class: "Reserved", - Opcode: 4473, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { Kind: OperandKindIdRef, Name: "'Accel'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'RayFlags'", + Name: "'Ray Flags'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'CullMask'", + Name: "'Cull Mask'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'RayOrigin'", + Name: "'SBT Offset'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'RayTMin'", + Name: "'SBT Stride'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'RayDirection'", + Name: "'Miss Index'", Quantifier: "", }, Operand { Kind: OperandKindIdRef, - Name: "'RayTMax'", + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", Quantifier: "", }, }, } - OpRayQueryTerminateKHR = &Opcode { - Opname: "OpRayQueryTerminateKHR", + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTriangleVertexPositionsKHR", Class: "Reserved", - Opcode: 4474, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGenerateIntersectionKHR = &Opcode { - Opname: "OpRayQueryGenerateIntersectionKHR", - Class: "Reserved", - Opcode: 4475, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'HitT'", - Quantifier: "", - }, - }, - } - OpRayQueryConfirmIntersectionKHR = &Opcode { - Opname: "OpRayQueryConfirmIntersectionKHR", - Class: "Reserved", - Opcode: 4476, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryProceedKHR = &Opcode { - Opname: "OpRayQueryProceedKHR", - Class: "Reserved", - Opcode: 4477, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionTypeKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionTypeKHR", - Class: "Reserved", - Opcode: 4479, + Opcode: 5340, Operands: []Operand { Operand { Kind: OperandKindIdResultType, @@ -10956,438 +13848,16 @@ }, }, } - OpRayQueryGetRayTMinKHR = &Opcode { - Opname: "OpRayQueryGetRayTMinKHR", - Class: "Reserved", - Opcode: 6016, + OpTypeAccelerationStructureKHR = &Opcode { + Opname: "OpTypeAccelerationStructureKHR", + Class: "Type-Declaration", + Opcode: 5341, Operands: []Operand { Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { Kind: OperandKindIdResult, Name: "", Quantifier: "", }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetRayFlagsKHR = &Opcode { - Opname: "OpRayQueryGetRayFlagsKHR", - Class: "Reserved", - Opcode: 6017, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionTKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionTKHR", - Class: "Reserved", - Opcode: 6018, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionInstanceCustomIndexKHR", - Class: "Reserved", - Opcode: 6019, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionInstanceIdKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionInstanceIdKHR", - Class: "Reserved", - Opcode: 6020, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", - Class: "Reserved", - Opcode: 6021, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionGeometryIndexKHR", - Class: "Reserved", - Opcode: 6022, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionPrimitiveIndexKHR", - Class: "Reserved", - Opcode: 6023, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionBarycentricsKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionBarycentricsKHR", - Class: "Reserved", - Opcode: 6024, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionFrontFaceKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionFrontFaceKHR", - Class: "Reserved", - Opcode: 6025, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", - Class: "Reserved", - Opcode: 6026, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionObjectRayDirectionKHR", - Class: "Reserved", - Opcode: 6027, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionObjectRayOriginKHR", - Class: "Reserved", - Opcode: 6028, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetWorldRayDirectionKHR = &Opcode { - Opname: "OpRayQueryGetWorldRayDirectionKHR", - Class: "Reserved", - Opcode: 6029, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetWorldRayOriginKHR = &Opcode { - Opname: "OpRayQueryGetWorldRayOriginKHR", - Class: "Reserved", - Opcode: 6030, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionObjectToWorldKHR", - Class: "Reserved", - Opcode: 6031, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, - }, - } - OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode { - Opname: "OpRayQueryGetIntersectionWorldToObjectKHR", - Class: "Reserved", - Opcode: 6032, - Operands: []Operand { - Operand { - Kind: OperandKindIdResultType, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdResult, - Name: "", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'RayQuery'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Intersection'", - Quantifier: "", - }, }, } OpExecuteCallableNV = &Opcode { @@ -11407,26 +13877,9 @@ }, }, } - OpExecuteCallableKHR = &Opcode { - Opname: "OpExecuteCallableKHR", - Class: "Reserved", - Opcode: 5344, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'SBT Index'", - Quantifier: "", - }, - Operand { - Kind: OperandKindIdRef, - Name: "'Callable DataId'", - Quantifier: "", - }, - }, - } OpTypeCooperativeMatrixNV = &Opcode { Opname: "OpTypeCooperativeMatrixNV", - Class: "Reserved", + Class: "Type-Declaration", Opcode: 5358, Operands: []Operand { Operand { @@ -11593,9 +14046,392 @@ Operands: []Operand { }, } - OpDemoteToHelperInvocationEXT = &Opcode { - Opname: "OpDemoteToHelperInvocationEXT", + OpCooperativeMatrixReduceNV = &Opcode { + Opname: "OpCooperativeMatrixReduceNV", + Class: "Arithmetic", + Opcode: 5366, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindCooperativeMatrixReduce, + Name: "'Reduce'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'CombineFunc'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLoadTensorNV = &Opcode { + Opname: "OpCooperativeMatrixLoadTensorNV", + Class: "Memory", + Opcode: 5367, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "", + }, + Operand { + Kind: OperandKindTensorAddressingOperands, + Name: "'Tensor Addressing Operands'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixStoreTensorNV = &Opcode { + Opname: "OpCooperativeMatrixStoreTensorNV", + Class: "Memory", + Opcode: 5368, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "", + }, + Operand { + Kind: OperandKindTensorAddressingOperands, + Name: "'Tensor Addressing Operands'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixPerElementOpNV = &Opcode { + Opname: "OpCooperativeMatrixPerElementOpNV", + Class: "Function", + Opcode: 5369, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Func'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operands'", + Quantifier: "*", + }, + }, + } + OpTypeTensorLayoutNV = &Opcode { + Opname: "OpTypeTensorLayoutNV", + Class: "Type-Declaration", + Opcode: 5370, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClampMode'", + Quantifier: "", + }, + }, + } + OpTypeTensorViewNV = &Opcode { + Opname: "OpTypeTensorViewNV", + Class: "Type-Declaration", + Opcode: 5371, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HasDimensions'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "*", + }, + }, + } + OpCreateTensorLayoutNV = &Opcode { + Opname: "OpCreateTensorLayoutNV", Class: "Reserved", + Opcode: 5372, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTensorLayoutSetDimensionNV = &Opcode { + Opname: "OpTensorLayoutSetDimensionNV", + Class: "Reserved", + Opcode: 5373, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "*", + }, + }, + } + OpTensorLayoutSetStrideNV = &Opcode { + Opname: "OpTensorLayoutSetStrideNV", + Class: "Reserved", + Opcode: 5374, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "*", + }, + }, + } + OpTensorLayoutSliceNV = &Opcode { + Opname: "OpTensorLayoutSliceNV", + Class: "Reserved", + Opcode: 5375, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operands'", + Quantifier: "*", + }, + }, + } + OpTensorLayoutSetClampValueNV = &Opcode { + Opname: "OpTensorLayoutSetClampValueNV", + Class: "Reserved", + Opcode: 5376, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpCreateTensorViewNV = &Opcode { + Opname: "OpCreateTensorViewNV", + Class: "Reserved", + Opcode: 5377, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTensorViewSetDimensionNV = &Opcode { + Opname: "OpTensorViewSetDimensionNV", + Class: "Reserved", + Opcode: 5378, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorView'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "*", + }, + }, + } + OpTensorViewSetStrideNV = &Opcode { + Opname: "OpTensorViewSetStrideNV", + Class: "Reserved", + Opcode: 5379, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorView'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "*", + }, + }, + } + OpDemoteToHelperInvocation = &Opcode { + Opname: "OpDemoteToHelperInvocation", + Class: "Control-Flow", Opcode: 5380, Operands: []Operand { }, @@ -11617,6 +14453,283 @@ }, }, } + OpTensorViewSetClipNV = &Opcode { + Opname: "OpTensorViewSetClipNV", + Class: "Reserved", + Opcode: 5382, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorView'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipRowOffset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipRowSpan'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipColOffset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipColSpan'", + Quantifier: "", + }, + }, + } + OpTensorLayoutSetBlockSizeNV = &Opcode { + Opname: "OpTensorLayoutSetBlockSizeNV", + Class: "Reserved", + Opcode: 5384, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'BlockSize'", + Quantifier: "*", + }, + }, + } + OpCooperativeMatrixTransposeNV = &Opcode { + Opname: "OpCooperativeMatrixTransposeNV", + Class: "Conversion", + Opcode: 5390, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpConvertUToImageNV = &Opcode { + Opname: "OpConvertUToImageNV", + Class: "Reserved", + Opcode: 5391, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertUToSamplerNV = &Opcode { + Opname: "OpConvertUToSamplerNV", + Class: "Reserved", + Opcode: 5392, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertImageToUNV = &Opcode { + Opname: "OpConvertImageToUNV", + Class: "Reserved", + Opcode: 5393, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertSamplerToUNV = &Opcode { + Opname: "OpConvertSamplerToUNV", + Class: "Reserved", + Opcode: 5394, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertUToSampledImageNV = &Opcode { + Opname: "OpConvertUToSampledImageNV", + Class: "Reserved", + Opcode: 5395, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertSampledImageToUNV = &Opcode { + Opname: "OpConvertSampledImageToUNV", + Class: "Reserved", + Opcode: 5396, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpSamplerImageAddressingModeNV = &Opcode { + Opname: "OpSamplerImageAddressingModeNV", + Class: "Reserved", + Opcode: 5397, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Bit Width'", + Quantifier: "", + }, + }, + } + OpRawAccessChainNV = &Opcode { + Opname: "OpRawAccessChainNV", + Class: "Memory", + Opcode: 5398, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Byte stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Byte offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindRawAccessChainOperands, + Name: "", + Quantifier: "?", + }, + }, + } OpSubgroupShuffleINTEL = &Opcode { Opname: "OpSubgroupShuffleINTEL", Class: "Group", @@ -12260,6 +15373,249 @@ }, }, } + OpConstantFunctionPointerINTEL = &Opcode { + Opname: "OpConstantFunctionPointerINTEL", + Class: "@exclude", + Opcode: 5600, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + }, + } + OpFunctionPointerCallINTEL = &Opcode { + Opname: "OpFunctionPointerCallINTEL", + Class: "@exclude", + Opcode: 5601, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "*", + }, + }, + } + OpAsmTargetINTEL = &Opcode { + Opname: "OpAsmTargetINTEL", + Class: "@exclude", + Opcode: 5609, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Asm target'", + Quantifier: "", + }, + }, + } + OpAsmINTEL = &Opcode { + Opname: "OpAsmINTEL", + Class: "@exclude", + Opcode: 5610, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Asm type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Asm instructions'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Constraints'", + Quantifier: "", + }, + }, + } + OpAsmCallINTEL = &Opcode { + Opname: "OpAsmCallINTEL", + Class: "@exclude", + Opcode: 5611, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Asm'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Argument 0'", + Quantifier: "*", + }, + }, + } + OpAtomicFMinEXT = &Opcode { + Opname: "OpAtomicFMinEXT", + Class: "Atomic", + Opcode: 5614, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicFMaxEXT = &Opcode { + Opname: "OpAtomicFMaxEXT", + Class: "Atomic", + Opcode: 5615, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAssumeTrueKHR = &Opcode { + Opname: "OpAssumeTrueKHR", + Class: "Miscellaneous", + Opcode: 5630, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Condition'", + Quantifier: "", + }, + }, + } + OpExpectKHR = &Opcode { + Opname: "OpExpectKHR", + Class: "Miscellaneous", + Opcode: 5631, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ExpectedValue'", + Quantifier: "", + }, + }, + } OpDecorateString = &Opcode { Opname: "OpDecorateString", Class: "Annotation", @@ -12277,23 +15633,6 @@ }, }, } - OpDecorateStringGOOGLE = &Opcode { - Opname: "OpDecorateStringGOOGLE", - Class: "Annotation", - Opcode: 5632, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Target'", - Quantifier: "", - }, - Operand { - Kind: OperandKindDecoration, - Name: "", - Quantifier: "", - }, - }, - } OpMemberDecorateString = &Opcode { Opname: "OpMemberDecorateString", Class: "Annotation", @@ -12316,28 +15655,6 @@ }, }, } - OpMemberDecorateStringGOOGLE = &Opcode { - Opname: "OpMemberDecorateStringGOOGLE", - Class: "Annotation", - Opcode: 5633, - Operands: []Operand { - Operand { - Kind: OperandKindIdRef, - Name: "'Struct Type'", - Quantifier: "", - }, - Operand { - Kind: OperandKindLiteralInteger, - Name: "'Member'", - Quantifier: "", - }, - Operand { - Kind: OperandKindDecoration, - Name: "", - Quantifier: "", - }, - }, - } OpVmeImageINTEL = &Opcode { Opname: "OpVmeImageINTEL", Class: "@exclude", @@ -15439,6 +18756,3782 @@ }, }, } + OpVariableLengthArrayINTEL = &Opcode { + Opname: "OpVariableLengthArrayINTEL", + Class: "@exclude", + Opcode: 5818, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Lenght'", + Quantifier: "", + }, + }, + } + OpSaveMemoryINTEL = &Opcode { + Opname: "OpSaveMemoryINTEL", + Class: "@exclude", + Opcode: 5819, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpRestoreMemoryINTEL = &Opcode { + Opname: "OpRestoreMemoryINTEL", + Class: "@exclude", + Opcode: 5820, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinCosPiINTEL = &Opcode { + Opname: "OpArbitraryFloatSinCosPiINTEL", + Class: "@exclude", + Opcode: 5840, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'FromSign'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCastINTEL = &Opcode { + Opname: "OpArbitraryFloatCastINTEL", + Class: "@exclude", + Opcode: 5841, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCastFromIntINTEL = &Opcode { + Opname: "OpArbitraryFloatCastFromIntINTEL", + Class: "@exclude", + Opcode: 5842, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'FromSign'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCastToIntINTEL = &Opcode { + Opname: "OpArbitraryFloatCastToIntINTEL", + Class: "@exclude", + Opcode: 5843, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatAddINTEL = &Opcode { + Opname: "OpArbitraryFloatAddINTEL", + Class: "@exclude", + Opcode: 5846, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSubINTEL = &Opcode { + Opname: "OpArbitraryFloatSubINTEL", + Class: "@exclude", + Opcode: 5847, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatMulINTEL = &Opcode { + Opname: "OpArbitraryFloatMulINTEL", + Class: "@exclude", + Opcode: 5848, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatDivINTEL = &Opcode { + Opname: "OpArbitraryFloatDivINTEL", + Class: "@exclude", + Opcode: 5849, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatGTINTEL = &Opcode { + Opname: "OpArbitraryFloatGTINTEL", + Class: "@exclude", + Opcode: 5850, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatGEINTEL = &Opcode { + Opname: "OpArbitraryFloatGEINTEL", + Class: "@exclude", + Opcode: 5851, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLTINTEL = &Opcode { + Opname: "OpArbitraryFloatLTINTEL", + Class: "@exclude", + Opcode: 5852, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLEINTEL = &Opcode { + Opname: "OpArbitraryFloatLEINTEL", + Class: "@exclude", + Opcode: 5853, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatEQINTEL = &Opcode { + Opname: "OpArbitraryFloatEQINTEL", + Class: "@exclude", + Opcode: 5854, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatRecipINTEL = &Opcode { + Opname: "OpArbitraryFloatRecipINTEL", + Class: "@exclude", + Opcode: 5855, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatRSqrtINTEL = &Opcode { + Opname: "OpArbitraryFloatRSqrtINTEL", + Class: "@exclude", + Opcode: 5856, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCbrtINTEL = &Opcode { + Opname: "OpArbitraryFloatCbrtINTEL", + Class: "@exclude", + Opcode: 5857, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatHypotINTEL = &Opcode { + Opname: "OpArbitraryFloatHypotINTEL", + Class: "@exclude", + Opcode: 5858, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSqrtINTEL = &Opcode { + Opname: "OpArbitraryFloatSqrtINTEL", + Class: "@exclude", + Opcode: 5859, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLogINTEL = &Opcode { + Opname: "OpArbitraryFloatLogINTEL", + Class: "@exclude", + Opcode: 5860, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLog2INTEL = &Opcode { + Opname: "OpArbitraryFloatLog2INTEL", + Class: "@exclude", + Opcode: 5861, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLog10INTEL = &Opcode { + Opname: "OpArbitraryFloatLog10INTEL", + Class: "@exclude", + Opcode: 5862, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLog1pINTEL = &Opcode { + Opname: "OpArbitraryFloatLog1pINTEL", + Class: "@exclude", + Opcode: 5863, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExpINTEL = &Opcode { + Opname: "OpArbitraryFloatExpINTEL", + Class: "@exclude", + Opcode: 5864, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExp2INTEL = &Opcode { + Opname: "OpArbitraryFloatExp2INTEL", + Class: "@exclude", + Opcode: 5865, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExp10INTEL = &Opcode { + Opname: "OpArbitraryFloatExp10INTEL", + Class: "@exclude", + Opcode: 5866, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExpm1INTEL = &Opcode { + Opname: "OpArbitraryFloatExpm1INTEL", + Class: "@exclude", + Opcode: 5867, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinINTEL = &Opcode { + Opname: "OpArbitraryFloatSinINTEL", + Class: "@exclude", + Opcode: 5868, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCosINTEL = &Opcode { + Opname: "OpArbitraryFloatCosINTEL", + Class: "@exclude", + Opcode: 5869, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinCosINTEL = &Opcode { + Opname: "OpArbitraryFloatSinCosINTEL", + Class: "@exclude", + Opcode: 5870, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinPiINTEL = &Opcode { + Opname: "OpArbitraryFloatSinPiINTEL", + Class: "@exclude", + Opcode: 5871, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCosPiINTEL = &Opcode { + Opname: "OpArbitraryFloatCosPiINTEL", + Class: "@exclude", + Opcode: 5872, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatASinINTEL = &Opcode { + Opname: "OpArbitraryFloatASinINTEL", + Class: "@exclude", + Opcode: 5873, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatASinPiINTEL = &Opcode { + Opname: "OpArbitraryFloatASinPiINTEL", + Class: "@exclude", + Opcode: 5874, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatACosINTEL = &Opcode { + Opname: "OpArbitraryFloatACosINTEL", + Class: "@exclude", + Opcode: 5875, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatACosPiINTEL = &Opcode { + Opname: "OpArbitraryFloatACosPiINTEL", + Class: "@exclude", + Opcode: 5876, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatATanINTEL = &Opcode { + Opname: "OpArbitraryFloatATanINTEL", + Class: "@exclude", + Opcode: 5877, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatATanPiINTEL = &Opcode { + Opname: "OpArbitraryFloatATanPiINTEL", + Class: "@exclude", + Opcode: 5878, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatATan2INTEL = &Opcode { + Opname: "OpArbitraryFloatATan2INTEL", + Class: "@exclude", + Opcode: 5879, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatPowINTEL = &Opcode { + Opname: "OpArbitraryFloatPowINTEL", + Class: "@exclude", + Opcode: 5880, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatPowRINTEL = &Opcode { + Opname: "OpArbitraryFloatPowRINTEL", + Class: "@exclude", + Opcode: 5881, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatPowNINTEL = &Opcode { + Opname: "OpArbitraryFloatPowNINTEL", + Class: "@exclude", + Opcode: 5882, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpLoopControlINTEL = &Opcode { + Opname: "OpLoopControlINTEL", + Class: "Reserved", + Opcode: 5887, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Loop Control Parameters'", + Quantifier: "*", + }, + }, + } + OpAliasDomainDeclINTEL = &Opcode { + Opname: "OpAliasDomainDeclINTEL", + Class: "@exclude", + Opcode: 5911, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpAliasScopeDeclINTEL = &Opcode { + Opname: "OpAliasScopeDeclINTEL", + Class: "@exclude", + Opcode: 5912, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Alias Domain'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpAliasScopeListDeclINTEL = &Opcode { + Opname: "OpAliasScopeListDeclINTEL", + Class: "@exclude", + Opcode: 5913, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'AliasScope1, AliasScope2, ...'", + Quantifier: "*", + }, + }, + } + OpFixedSqrtINTEL = &Opcode { + Opname: "OpFixedSqrtINTEL", + Class: "@exclude", + Opcode: 5923, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedRecipINTEL = &Opcode { + Opname: "OpFixedRecipINTEL", + Class: "@exclude", + Opcode: 5924, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedRsqrtINTEL = &Opcode { + Opname: "OpFixedRsqrtINTEL", + Class: "@exclude", + Opcode: 5925, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinINTEL = &Opcode { + Opname: "OpFixedSinINTEL", + Class: "@exclude", + Opcode: 5926, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedCosINTEL = &Opcode { + Opname: "OpFixedCosINTEL", + Class: "@exclude", + Opcode: 5927, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinCosINTEL = &Opcode { + Opname: "OpFixedSinCosINTEL", + Class: "@exclude", + Opcode: 5928, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinPiINTEL = &Opcode { + Opname: "OpFixedSinPiINTEL", + Class: "@exclude", + Opcode: 5929, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedCosPiINTEL = &Opcode { + Opname: "OpFixedCosPiINTEL", + Class: "@exclude", + Opcode: 5930, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinCosPiINTEL = &Opcode { + Opname: "OpFixedSinCosPiINTEL", + Class: "@exclude", + Opcode: 5931, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedLogINTEL = &Opcode { + Opname: "OpFixedLogINTEL", + Class: "@exclude", + Opcode: 5932, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedExpINTEL = &Opcode { + Opname: "OpFixedExpINTEL", + Class: "@exclude", + Opcode: 5933, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpPtrCastToCrossWorkgroupINTEL = &Opcode { + Opname: "OpPtrCastToCrossWorkgroupINTEL", + Class: "@exclude", + Opcode: 5934, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpCrossWorkgroupCastToPtrINTEL = &Opcode { + Opname: "OpCrossWorkgroupCastToPtrINTEL", + Class: "@exclude", + Opcode: 5938, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpReadPipeBlockingINTEL = &Opcode { + Opname: "OpReadPipeBlockingINTEL", + Class: "Pipe", + Opcode: 5946, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpWritePipeBlockingINTEL = &Opcode { + Opname: "OpWritePipeBlockingINTEL", + Class: "Pipe", + Opcode: 5947, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpFPGARegINTEL = &Opcode { + Opname: "OpFPGARegINTEL", + Class: "Reserved", + Opcode: 5949, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Result'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayTMinKHR = &Opcode { + Opname: "OpRayQueryGetRayTMinKHR", + Class: "Reserved", + Opcode: 6016, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayFlagsKHR = &Opcode { + Opname: "OpRayQueryGetRayFlagsKHR", + Class: "Reserved", + Opcode: 6017, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTKHR", + Class: "Reserved", + Opcode: 6018, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + Class: "Reserved", + Opcode: 6019, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceIdKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceIdKHR", + Class: "Reserved", + Opcode: 6020, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + Class: "Reserved", + Opcode: 6021, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionGeometryIndexKHR", + Class: "Reserved", + Opcode: 6022, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionPrimitiveIndexKHR", + Class: "Reserved", + Opcode: 6023, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionBarycentricsKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionBarycentricsKHR", + Class: "Reserved", + Opcode: 6024, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionFrontFaceKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionFrontFaceKHR", + Class: "Reserved", + Opcode: 6025, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + Class: "Reserved", + Opcode: 6026, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayDirectionKHR", + Class: "Reserved", + Opcode: 6027, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayOriginKHR", + Class: "Reserved", + Opcode: 6028, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayDirectionKHR", + Class: "Reserved", + Opcode: 6029, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayOriginKHR", + Class: "Reserved", + Opcode: 6030, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectToWorldKHR", + Class: "Reserved", + Opcode: 6031, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionWorldToObjectKHR", + Class: "Reserved", + Opcode: 6032, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpAtomicFAddEXT = &Opcode { + Opname: "OpAtomicFAddEXT", + Class: "Atomic", + Opcode: 6035, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpTypeBufferSurfaceINTEL = &Opcode { + Opname: "OpTypeBufferSurfaceINTEL", + Class: "Type-Declaration", + Opcode: 6086, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindAccessQualifier, + Name: "'AccessQualifier'", + Quantifier: "", + }, + }, + } + OpTypeStructContinuedINTEL = &Opcode { + Opname: "OpTypeStructContinuedINTEL", + Class: "Type-Declaration", + Opcode: 6090, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Member 0 type', + 'member 1 type', + ...", + Quantifier: "*", + }, + }, + } + OpConstantCompositeContinuedINTEL = &Opcode { + Opname: "OpConstantCompositeContinuedINTEL", + Class: "Constant-Creation", + Opcode: 6091, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpSpecConstantCompositeContinuedINTEL = &Opcode { + Opname: "OpSpecConstantCompositeContinuedINTEL", + Class: "Constant-Creation", + Opcode: 6092, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpCompositeConstructContinuedINTEL = &Opcode { + Opname: "OpCompositeConstructContinuedINTEL", + Class: "Composite", + Opcode: 6096, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpConvertFToBF16INTEL = &Opcode { + Opname: "OpConvertFToBF16INTEL", + Class: "Conversion", + Opcode: 6116, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpConvertBF16ToFINTEL = &Opcode { + Opname: "OpConvertBF16ToFINTEL", + Class: "Conversion", + Opcode: 6117, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'BFloat16 Value'", + Quantifier: "", + }, + }, + } + OpControlBarrierArriveINTEL = &Opcode { + Opname: "OpControlBarrierArriveINTEL", + Class: "Barrier", + Opcode: 6142, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpControlBarrierWaitINTEL = &Opcode { + Opname: "OpControlBarrierWaitINTEL", + Class: "Barrier", + Opcode: 6143, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpArithmeticFenceEXT = &Opcode { + Opname: "OpArithmeticFenceEXT", + Class: "Miscellaneous", + Opcode: 6145, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target '", + Quantifier: "", + }, + }, + } + OpSubgroupBlockPrefetchINTEL = &Opcode { + Opname: "OpSubgroupBlockPrefetchINTEL", + Class: "Group", + Opcode: 6221, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'NumBytes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpGroupIMulKHR = &Opcode { + Opname: "OpGroupIMulKHR", + Class: "Group", + Opcode: 6401, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMulKHR = &Opcode { + Opname: "OpGroupFMulKHR", + Class: "Group", + Opcode: 6402, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupBitwiseAndKHR = &Opcode { + Opname: "OpGroupBitwiseAndKHR", + Class: "Group", + Opcode: 6403, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupBitwiseOrKHR = &Opcode { + Opname: "OpGroupBitwiseOrKHR", + Class: "Group", + Opcode: 6404, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupBitwiseXorKHR = &Opcode { + Opname: "OpGroupBitwiseXorKHR", + Class: "Group", + Opcode: 6405, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupLogicalAndKHR = &Opcode { + Opname: "OpGroupLogicalAndKHR", + Class: "Group", + Opcode: 6406, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupLogicalOrKHR = &Opcode { + Opname: "OpGroupLogicalOrKHR", + Class: "Group", + Opcode: 6407, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupLogicalXorKHR = &Opcode { + Opname: "OpGroupLogicalXorKHR", + Class: "Group", + Opcode: 6408, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpMaskedGatherINTEL = &Opcode { + Opname: "OpMaskedGatherINTEL", + Class: "Memory", + Opcode: 6428, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PtrVector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Alignment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'FillEmpty'", + Quantifier: "", + }, + }, + } + OpMaskedScatterINTEL = &Opcode { + Opname: "OpMaskedScatterINTEL", + Class: "Memory", + Opcode: 6429, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'InputVector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PtrVector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Alignment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Mask'", + Quantifier: "", + }, + }, + } GLSLStd450_Round = &Opcode { Opname: "Round", @@ -19600,6 +26693,51 @@ }, }, } + OpenCLDebugInfo100_DebugModuleINTEL = &Opcode { + Opname: "DebugModuleINTEL", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ConfigurationMacros'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'IncludePath'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'APINotesFile'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'IsDeclaration'", + Quantifier: "", + }, + }, + } OperandKindImageOperands = &OperandKind { @@ -19618,56 +26756,56 @@ Value: 0x0001, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Lod", Value: 0x0002, Capabilities: []string{}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Grad", Value: 0x0004, Capabilities: []string{}, Parameters: []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ConstOffset", Value: 0x0008, Capabilities: []string{}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Offset", Value: 0x0010, Capabilities: []string{"ImageGatherExtended",}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ConstOffsets", Value: 0x0020, Capabilities: []string{"ImageGatherExtended",}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Sample", Value: 0x0040, Capabilities: []string{}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MinLod", Value: 0x0080, Capabilities: []string{"MinLod",}, Parameters: []Parameter{{OperandKindIdRef, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MakeTexelAvailable", @@ -19677,13 +26815,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "MakeTexelAvailableKHR", - Value: 0x0100, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ Enumerant: "MakeTexelVisible", Value: 0x0200, Capabilities: []string{"VulkanMemoryModel",}, @@ -19691,13 +26822,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "MakeTexelVisibleKHR", - Value: 0x0200, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ Enumerant: "NonPrivateTexel", Value: 0x0400, Capabilities: []string{"VulkanMemoryModel",}, @@ -19705,13 +26829,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "NonPrivateTexelKHR", - Value: 0x0400, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "VolatileTexel", Value: 0x0800, Capabilities: []string{"VulkanMemoryModel",}, @@ -19719,13 +26836,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "VolatileTexelKHR", - Value: 0x0800, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "SignExtend", Value: 0x1000, Capabilities: []string{}, @@ -19739,6 +26849,20 @@ Parameters: []Parameter{}, Version: "1.4", }, + Enumerant{ + Enumerant: "Nontemporal", + Value: 0x4000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "Offsets", + Value: 0x10000, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, }, Bases: []*OperandKind {}, } @@ -19751,42 +26875,63 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NotNaN", Value: 0x0001, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NotInf", Value: 0x0002, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NSZ", Value: 0x0004, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "AllowRecip", Value: 0x0008, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Fast", Value: 0x0010, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "AllowContract", + Value: 0x10000, + Capabilities: []string{"FloatControls2","FPFastMathModeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AllowReassoc", + Value: 0x20000, + Capabilities: []string{"FloatControls2","FPFastMathModeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AllowTransform", + Value: 0x40000, + Capabilities: []string{"FloatControls2",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -19800,21 +26945,21 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Flatten", Value: 0x0001, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DontFlatten", Value: 0x0002, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -19828,21 +26973,21 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Unroll", Value: 0x0001, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DontUnroll", Value: 0x0002, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DependencyInfinite", @@ -19893,6 +27038,76 @@ Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, Version: "1.4", }, + Enumerant{ + Enumerant: "InitiationIntervalINTEL", + Value: 0x10000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxConcurrencyINTEL", + Value: 0x20000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DependencyArrayINTEL", + Value: 0x40000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PipelineEnableINTEL", + Value: 0x80000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "LoopCoalesceINTEL", + Value: 0x100000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxInterleavingINTEL", + Value: 0x200000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SpeculatedIterationsINTEL", + Value: 0x400000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoFusionINTEL", + Value: 0x800000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LoopCountINTEL", + Value: 0x1000000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxReinvocationDelayINTEL", + Value: 0x2000000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, }, Bases: []*OperandKind {}, } @@ -19905,35 +27120,42 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Inline", Value: 0x0001, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DontInline", Value: 0x0002, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Pure", Value: 0x0004, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Const", Value: 0x0008, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "OptNoneEXT", + Value: 0x10000, + Capabilities: []string{"OptNoneEXT",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -19947,84 +27169,77 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", - }, - Enumerant{ - Enumerant: "None", - Value: 0x0000, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Acquire", Value: 0x0002, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Release", Value: 0x0004, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "AcquireRelease", Value: 0x0008, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SequentiallyConsistent", Value: 0x0010, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UniformMemory", Value: 0x0040, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupMemory", Value: 0x0080, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WorkgroupMemory", Value: 0x0100, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "CrossWorkgroupMemory", Value: 0x0200, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "AtomicCounterMemory", Value: 0x0400, Capabilities: []string{"AtomicStorage",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageMemory", Value: 0x0800, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OutputMemory", @@ -20034,13 +27249,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "OutputMemoryKHR", - Value: 0x1000, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "MakeAvailable", Value: 0x2000, Capabilities: []string{"VulkanMemoryModel",}, @@ -20048,13 +27256,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "MakeAvailableKHR", - Value: 0x2000, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "MakeVisible", Value: 0x4000, Capabilities: []string{"VulkanMemoryModel",}, @@ -20062,13 +27263,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "MakeVisibleKHR", - Value: 0x4000, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "Volatile", Value: 0x8000, Capabilities: []string{"VulkanMemoryModel",}, @@ -20087,28 +27281,28 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Volatile", Value: 0x0001, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Aligned", Value: 0x0002, Capabilities: []string{}, Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Nontemporal", Value: 0x0004, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MakePointerAvailable", @@ -20118,13 +27312,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "MakePointerAvailableKHR", - Value: 0x0008, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ Enumerant: "MakePointerVisible", Value: 0x0010, Capabilities: []string{"VulkanMemoryModel",}, @@ -20132,13 +27319,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "MakePointerVisibleKHR", - Value: 0x0010, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{{OperandKindIdScope, ""},}, - Version: "1.5", - }, - Enumerant{ Enumerant: "NonPrivatePointer", Value: 0x0020, Capabilities: []string{"VulkanMemoryModel",}, @@ -20146,11 +27326,18 @@ Version: "1.5", }, Enumerant{ - Enumerant: "NonPrivatePointerKHR", - Value: 0x0020, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", + Enumerant: "AliasScopeINTELMask", + Value: 0x10000, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoAliasINTELMask", + Value: 0x20000, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -20164,14 +27351,14 @@ Value: 0x0000, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "CmdExecTime", Value: 0x0001, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -20183,80 +27370,150 @@ Enumerant{ Enumerant: "NoneKHR", Value: 0x0000, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "OpaqueKHR", Value: 0x0001, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "NoOpaqueKHR", Value: 0x0002, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "TerminateOnFirstHitKHR", Value: 0x0004, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "SkipClosestHitShaderKHR", Value: 0x0008, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "CullBackFacingTrianglesKHR", Value: 0x0010, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "CullFrontFacingTrianglesKHR", Value: 0x0020, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "CullOpaqueKHR", Value: 0x0040, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "CullNoOpaqueKHR", Value: 0x0080, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "SkipTrianglesKHR", Value: 0x0100, - Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, + Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "SkipAABBsKHR", Value: 0x0200, - Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, + Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ForceOpacityMicromap2StateEXT", + Value: 0x0400, + Capabilities: []string{"RayTracingOpacityMicromapEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFragmentShadingRate = &OperandKind { + Kind: "FragmentShadingRate", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Vertical2Pixels", + Value: 0x0001, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Vertical4Pixels", + Value: 0x0002, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Horizontal2Pixels", + Value: 0x0004, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Horizontal4Pixels", + Value: 0x0008, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRawAccessChainOperands = &OperandKind { + Kind: "RawAccessChainOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "", }, + Enumerant{ + Enumerant: "RobustnessPerComponentNV", + Value: 0x0001, + Capabilities: []string{"RawAccessChainsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RobustnessPerElementNV", + Value: 0x0002, + Capabilities: []string{"RawAccessChainsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, }, Bases: []*OperandKind {}, } @@ -20269,42 +27526,91 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ESSL", Value: 1, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GLSL", Value: 2, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OpenCL_C", Value: 3, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OpenCL_CPP", Value: 4, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "HLSL", Value: 5, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "CPP_for_OpenCL", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SYCL", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "HERO_C", + Value: 8, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NZSL", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WGSL", + Value: 10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Slang", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Zig", + Value: 12, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -20318,49 +27624,49 @@ Value: 0, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TessellationControl", Value: 1, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TessellationEvaluation", Value: 2, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Geometry", Value: 3, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Fragment", Value: 4, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GLCompute", Value: 5, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Kernel", Value: 6, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TaskNV", @@ -20377,86 +27683,58 @@ Version: "None", }, Enumerant{ - Enumerant: "RayGenerationNV", - Value: 5313, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ Enumerant: "RayGenerationKHR", Value: 5313, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "IntersectionNV", - Value: 5314, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IntersectionKHR", Value: 5314, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "AnyHitNV", - Value: 5315, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "AnyHitKHR", Value: 5315, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ClosestHitNV", - Value: 5316, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ClosestHitKHR", Value: 5316, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "MissNV", - Value: 5317, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "MissKHR", Value: 5317, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "CallableNV", - Value: 5318, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableKHR", Value: 5318, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TaskEXT", + Value: 5364, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshEXT", + Value: 5365, + Capabilities: []string{"MeshShadingEXT",}, Parameters: []Parameter{}, Version: "None", }, @@ -20472,21 +27750,21 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Physical32", Value: 1, Capabilities: []string{"Addresses",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Physical64", Value: 2, Capabilities: []string{"Addresses",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PhysicalStorageBuffer64", @@ -20495,13 +27773,6 @@ Parameters: []Parameter{}, Version: "1.5", }, - Enumerant{ - Enumerant: "PhysicalStorageBuffer64EXT", - Value: 5348, - Capabilities: []string{"PhysicalStorageBufferAddresses",}, - Parameters: []Parameter{}, - Version: "1.5", - }, }, Bases: []*OperandKind {}, } @@ -20514,21 +27785,21 @@ Value: 0, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GLSL450", Value: 1, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OpenCL", Value: 2, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Vulkan", @@ -20537,13 +27808,6 @@ Parameters: []Parameter{}, Version: "1.5", }, - Enumerant{ - Enumerant: "VulkanKHR", - Value: 3, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, }, Bases: []*OperandKind {}, } @@ -20556,217 +27820,217 @@ Value: 0, Capabilities: []string{"Geometry",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Number of <<Invocation,invocations>>'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SpacingEqual", Value: 1, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SpacingFractionalEven", Value: 2, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SpacingFractionalOdd", Value: 3, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "VertexOrderCw", Value: 4, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "VertexOrderCcw", Value: 5, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PixelCenterInteger", Value: 6, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OriginUpperLeft", Value: 7, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OriginLowerLeft", Value: 8, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "EarlyFragmentTests", Value: 9, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PointMode", Value: 10, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Xfb", Value: 11, Capabilities: []string{"TransformFeedback",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DepthReplacing", Value: 12, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DepthGreater", Value: 14, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DepthLess", Value: 15, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DepthUnchanged", Value: 16, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "LocalSize", Value: 17, Capabilities: []string{}, Parameters: []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "LocalSizeHint", Value: 18, Capabilities: []string{"Kernel",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InputPoints", Value: 19, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InputLines", Value: 20, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InputLinesAdjacency", Value: 21, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Triangles", Value: 22, Capabilities: []string{"Geometry","Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InputTrianglesAdjacency", Value: 23, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Quads", Value: 24, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Isolines", Value: 25, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OutputVertices", Value: 26, - Capabilities: []string{"Geometry","Tessellation","MeshShadingNV",}, + Capabilities: []string{"Geometry","Tessellation","MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Vertex count'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OutputPoints", Value: 27, - Capabilities: []string{"Geometry","MeshShadingNV",}, + Capabilities: []string{"Geometry","MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OutputLineStrip", Value: 28, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "OutputTriangleStrip", Value: 29, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "VecTypeHint", Value: 30, Capabilities: []string{"Kernel",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Vector type'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ContractionOff", Value: 31, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Initializer", @@ -20814,10 +28078,38 @@ Enumerant: "LocalSizeHintId", Value: 39, Capabilities: []string{"Kernel",}, - Parameters: []Parameter{{OperandKindIdRef, "'Local Size Hint'"},}, + Parameters: []Parameter{{OperandKindIdRef, "'x size hint'"},{OperandKindIdRef, "'y size hint'"},{OperandKindIdRef, "'z size hint'"},}, Version: "1.2", }, Enumerant{ + Enumerant: "NonCoherentColorAttachmentReadEXT", + Value: 4169, + Capabilities: []string{"TileImageColorReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NonCoherentDepthAttachmentReadEXT", + Value: 4170, + Capabilities: []string{"TileImageDepthReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NonCoherentStencilAttachmentReadEXT", + Value: 4171, + Capabilities: []string{"TileImageStencilReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupUniformControlFlowKHR", + Value: 4421, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "PostDepthCoverage", Value: 4446, Capabilities: []string{"SampleMaskPostDepthCoverage",}, @@ -20860,6 +28152,13 @@ Version: "1.4", }, Enumerant{ + Enumerant: "EarlyAndLateFragmentTestsAMD", + Value: 5017, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "StencilRefReplacingEXT", Value: 5027, Capabilities: []string{"StencilExportEXT",}, @@ -20867,37 +28166,142 @@ Version: "None", }, Enumerant{ - Enumerant: "OutputLinesNV", - Value: 5269, - Capabilities: []string{"MeshShadingNV",}, + Enumerant: "CoalescingAMDX", + Value: 5069, + Capabilities: []string{"ShaderEnqueueAMDX",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "OutputPrimitivesNV", + Enumerant: "IsApiEntryAMDX", + Value: 5070, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Is Entry'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxNodeRecursionAMDX", + Value: 5071, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Number of recursions'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StaticNumWorkgroupsAMDX", + Value: 5072, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderIndexAMDX", + Value: 5073, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Shader Index'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxNumWorkgroupsAMDX", + Value: 5077, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefUnchangedFrontAMD", + Value: 5079, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefGreaterFrontAMD", + Value: 5080, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefLessFrontAMD", + Value: 5081, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefUnchangedBackAMD", + Value: 5082, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefGreaterBackAMD", + Value: 5083, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefLessBackAMD", + Value: 5084, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "QuadDerivativesKHR", + Value: 5088, + Capabilities: []string{"QuadControlKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RequireFullQuadsKHR", + Value: 5089, + Capabilities: []string{"QuadControlKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SharesInputWithAMDX", + Value: 5102, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Node Name'"},{OperandKindIdRef, "'Shader Index'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputLinesEXT", + Value: 5269, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputPrimitivesEXT", Value: 5270, - Capabilities: []string{"MeshShadingNV",}, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Primitive count'"},}, Version: "None", }, Enumerant{ - Enumerant: "DerivativeGroupQuadsNV", + Enumerant: "DerivativeGroupQuadsKHR", Value: 5289, - Capabilities: []string{"ComputeDerivativeGroupQuadsNV",}, + Capabilities: []string{"ComputeDerivativeGroupQuadsNV","ComputeDerivativeGroupQuadsKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "DerivativeGroupLinearNV", + Enumerant: "DerivativeGroupLinearKHR", Value: 5290, - Capabilities: []string{"ComputeDerivativeGroupLinearNV",}, + Capabilities: []string{"ComputeDerivativeGroupLinearNV","ComputeDerivativeGroupLinearKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "OutputTrianglesNV", + Enumerant: "OutputTrianglesEXT", Value: 5298, - Capabilities: []string{"MeshShadingNV",}, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, Version: "None", }, @@ -20943,6 +28347,132 @@ Parameters: []Parameter{}, Version: "None", }, + Enumerant{ + Enumerant: "SharedLocalMemorySizeINTEL", + Value: 5618, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RoundingModeRTPINTEL", + Value: 5620, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RoundingModeRTNINTEL", + Value: 5621, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatingPointModeALTINTEL", + Value: 5622, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatingPointModeIEEEINTEL", + Value: 5623, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxWorkgroupSizeINTEL", + Value: 5893, + Capabilities: []string{"KernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'max_x_size'"},{OperandKindLiteralInteger, "'max_y_size'"},{OperandKindLiteralInteger, "'max_z_size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxWorkDimINTEL", + Value: 5894, + Capabilities: []string{"KernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'max_dimensions'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoGlobalOffsetINTEL", + Value: 5895, + Capabilities: []string{"KernelAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NumSIMDWorkitemsINTEL", + Value: 5896, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'vector_width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SchedulerTargetFmaxMhzINTEL", + Value: 5903, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'target_fmax'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaximallyReconvergesKHR", + Value: 6023, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPFastMathDefault", + Value: 6028, + Capabilities: []string{"FloatControls2",}, + Parameters: []Parameter{{OperandKindIdRef, "'Target Type'"},{OperandKindIdRef, "'Fast-Math Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StreamingInterfaceINTEL", + Value: 6154, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'StallFreeReturn'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterMapInterfaceINTEL", + Value: 6160, + Capabilities: []string{"FPGAKernelAttributesv2INTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'WaitForDoneWrite'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NamedBarrierCountINTEL", + Value: 6417, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Barrier Count'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaximumRegistersINTEL", + Value: 6461, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Number of Registers'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaximumRegistersIdINTEL", + Value: 6462, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, "'Number of Registers'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NamedMaximumRegistersINTEL", + Value: 6463, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{{OperandKindNamedMaximumNumberOfRegisters, "'Named Maximum Number of Registers'"},}, + Version: "None", + }, }, Bases: []*OperandKind {}, } @@ -20955,84 +28485,84 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Input", Value: 1, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Uniform", Value: 2, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Output", Value: 3, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Workgroup", Value: 4, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "CrossWorkgroup", Value: 5, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Private", Value: 6, - Capabilities: []string{"Shader",}, + Capabilities: []string{"Shader","VectorComputeINTEL",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Function", Value: 7, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Generic", Value: 8, Capabilities: []string{"GenericPointer",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PushConstant", Value: 9, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "AtomicCounter", Value: 10, Capabilities: []string{"AtomicStorage",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Image", Value: 11, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageBuffer", @@ -21042,86 +28572,58 @@ Version: "1.3", }, Enumerant{ - Enumerant: "CallableDataNV", - Value: 5328, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Enumerant: "TileImageEXT", + Value: 4172, + Capabilities: []string{"TileImageColorReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NodePayloadAMDX", + Value: 5068, + Capabilities: []string{"ShaderEnqueueAMDX",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableDataKHR", Value: 5328, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "IncomingCallableDataNV", - Value: 5329, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingCallableDataKHR", Value: 5329, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "RayPayloadNV", - Value: 5338, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayPayloadKHR", Value: 5338, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "HitAttributeNV", - Value: 5339, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitAttributeKHR", Value: 5339, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "IncomingRayPayloadNV", - Value: 5342, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayPayloadKHR", Value: 5342, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ShaderRecordBufferNV", - Value: 5343, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ShaderRecordBufferKHR", Value: 5343, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -21133,11 +28635,39 @@ Version: "1.5", }, Enumerant{ - Enumerant: "PhysicalStorageBufferEXT", - Value: 5349, - Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Enumerant: "HitObjectAttributeNV", + Value: 5385, + Capabilities: []string{"ShaderInvocationReorderNV",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", + }, + Enumerant{ + Enumerant: "TaskPayloadWorkgroupEXT", + Value: 5402, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "CodeSectionINTEL", + Value: 5605, + Capabilities: []string{"FunctionPointersINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DeviceOnlyINTEL", + Value: 5936, + Capabilities: []string{"USMStorageClassesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HostOnlyINTEL", + Value: 5937, + Capabilities: []string{"USMStorageClassesINTEL",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -21149,51 +28679,58 @@ Enumerant{ Enumerant: "1D", Value: 0, - Capabilities: []string{"Sampled1D","Image1D",}, + Capabilities: []string{"Sampled1D",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "2D", Value: 1, - Capabilities: []string{"Shader","Kernel","ImageMSArray",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "3D", Value: 2, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Cube", Value: 3, - Capabilities: []string{"Shader","ImageCubeArray",}, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rect", Value: 4, - Capabilities: []string{"SampledRect","ImageRect",}, + Capabilities: []string{"SampledRect",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Buffer", Value: 5, - Capabilities: []string{"SampledBuffer","ImageBuffer",}, + Capabilities: []string{"SampledBuffer",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubpassData", Value: 6, Capabilities: []string{"InputAttachment",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "TileImageDataEXT", + Value: 4173, + Capabilities: []string{"TileImageColorReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -21205,37 +28742,37 @@ Enumerant{ Enumerant: "None", Value: 0, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ClampToEdge", Value: 1, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Clamp", Value: 2, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Repeat", Value: 3, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RepeatMirrored", Value: 4, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -21247,16 +28784,16 @@ Enumerant{ Enumerant: "Nearest", Value: 0, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Linear", Value: 1, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -21270,280 +28807,294 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba32f", Value: 1, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba16f", Value: 2, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R32f", Value: 3, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba8", Value: 4, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba8Snorm", Value: 5, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg32f", Value: 6, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg16f", Value: 7, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R11fG11fB10f", Value: 8, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R16f", Value: 9, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba16", Value: 10, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgb10A2", Value: 11, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg16", Value: 12, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg8", Value: 13, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R16", Value: 14, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R8", Value: 15, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba16Snorm", Value: 16, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg16Snorm", Value: 17, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg8Snorm", Value: 18, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R16Snorm", Value: 19, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R8Snorm", Value: 20, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba32i", Value: 21, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba16i", Value: 22, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba8i", Value: 23, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R32i", Value: 24, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg32i", Value: 25, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg16i", Value: 26, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg8i", Value: 27, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R16i", Value: 28, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R8i", Value: 29, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba32ui", Value: 30, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba16ui", Value: 31, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgba8ui", Value: 32, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R32ui", Value: 33, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rgb10a2ui", Value: 34, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg32ui", Value: 35, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg16ui", Value: 36, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rg8ui", Value: 37, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R16ui", Value: 38, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "R8ui", Value: 39, Capabilities: []string{"StorageImageExtendedFormats",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "R64ui", + Value: 40, + Capabilities: []string{"Int64ImageEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R64i", + Value: 41, + Capabilities: []string{"Int64ImageEXT",}, + Parameters: []Parameter{}, + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -21555,142 +29106,142 @@ Enumerant{ Enumerant: "R", Value: 0, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "A", Value: 1, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RG", Value: 2, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RA", Value: 3, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RGB", Value: 4, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RGBA", Value: 5, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "BGRA", Value: 6, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ARGB", Value: 7, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Intensity", Value: 8, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Luminance", Value: 9, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Rx", Value: 10, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RGx", Value: 11, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RGBx", Value: 12, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Depth", Value: 13, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DepthStencil", Value: 14, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "sRGB", Value: 15, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "sRGBx", Value: 16, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "sRGBA", Value: 17, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "sBGRA", Value: 18, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ABGR", Value: 19, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -21702,121 +29253,142 @@ Enumerant{ Enumerant: "SnormInt8", Value: 0, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SnormInt16", Value: 1, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormInt8", Value: 2, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormInt16", Value: 3, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormShort565", Value: 4, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormShort555", Value: 5, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormInt101010", Value: 6, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SignedInt8", Value: 7, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SignedInt16", Value: 8, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SignedInt32", Value: 9, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnsignedInt8", Value: 10, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnsignedInt16", Value: 11, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnsignedInt32", Value: 12, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "HalfFloat", Value: 13, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Float", Value: 14, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormInt24", Value: 15, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UnormInt101010_2", Value: 16, - Capabilities: []string{"Kernel",}, + Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedIntRaw10EXT", + Value: 19, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedIntRaw12EXT", + Value: 20, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt2_101010EXT", + Value: 21, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -21830,28 +29402,168 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RTZ", Value: 1, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RTP", Value: 2, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RTN", Value: 3, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPDenormMode = &OperandKind { + Kind: "FPDenormMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Preserve", + Value: 0, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FlushToZero", + Value: 1, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindQuantizationModes = &OperandKind { + Kind: "QuantizationModes", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "TRN", + Value: 0, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TRN_ZERO", + Value: 1, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND", + Value: 2, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_ZERO", + Value: 3, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_INF", + Value: 4, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_MIN_INF", + Value: 5, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_CONV", + Value: 6, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_CONV_ODD", + Value: 7, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPOperationMode = &OperandKind { + Kind: "FPOperationMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "IEEE", + Value: 0, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ALT", + Value: 1, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindOverflowModes = &OperandKind { + Kind: "OverflowModes", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "WRAP", + Value: 0, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SAT", + Value: 1, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SAT_ZERO", + Value: 2, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SAT_SYM", + Value: 3, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -21865,14 +29577,21 @@ Value: 0, Capabilities: []string{"Linkage",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Import", Value: 1, Capabilities: []string{"Linkage",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "LinkOnceODR", + Value: 2, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -21886,21 +29605,56 @@ Value: 0, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WriteOnly", Value: 1, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ReadWrite", Value: 2, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindHostAccessQualifier = &OperandKind { + Kind: "HostAccessQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoneINTEL", + Value: 0, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReadINTEL", + Value: 1, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WriteINTEL", + Value: 2, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReadWriteINTEL", + Value: 3, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", }, }, Bases: []*OperandKind {}, @@ -21914,56 +29668,63 @@ Value: 0, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Sext", Value: 1, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ByVal", Value: 2, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Sret", Value: 3, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NoAlias", Value: 4, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NoCapture", Value: 5, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NoWrite", Value: 6, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NoReadWrite", Value: 7, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "RuntimeAlignedINTEL", + Value: 5940, + Capabilities: []string{"RuntimeAlignedAttributeINTEL",}, + Parameters: []Parameter{}, + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -21977,187 +29738,187 @@ Value: 0, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SpecId", Value: 1, Capabilities: []string{"Shader","Kernel",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Specialization Constant ID'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Block", Value: 2, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "BufferBlock", Value: 3, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "RowMajor", Value: 4, Capabilities: []string{"Matrix",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ColMajor", Value: 5, Capabilities: []string{"Matrix",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ArrayStride", Value: 6, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Array Stride'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MatrixStride", Value: 7, Capabilities: []string{"Matrix",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Matrix Stride'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GLSLShared", Value: 8, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GLSLPacked", Value: 9, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "CPacked", Value: 10, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "BuiltIn", Value: 11, Capabilities: []string{}, Parameters: []Parameter{{OperandKindBuiltIn, ""},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NoPerspective", Value: 13, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Flat", Value: 14, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Patch", Value: 15, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Centroid", Value: 16, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Sample", Value: 17, Capabilities: []string{"SampleRateShading",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Invariant", Value: 18, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Restrict", Value: 19, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Aliased", Value: 20, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Volatile", Value: 21, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Constant", Value: 22, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Coherent", Value: 23, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NonWritable", Value: 24, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NonReadable", Value: 25, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Uniform", Value: 26, - Capabilities: []string{"Shader",}, + Capabilities: []string{"Shader","UniformDecoration",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UniformId", Value: 27, - Capabilities: []string{"Shader",}, + Capabilities: []string{"Shader","UniformDecoration",}, Parameters: []Parameter{{OperandKindIdScope, "'Execution'"},}, Version: "1.4", }, @@ -22166,119 +29927,119 @@ Value: 28, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Stream", Value: 29, Capabilities: []string{"GeometryStreams",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Stream Number'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Location", Value: 30, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Location'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Component", Value: 31, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Component'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Index", Value: 32, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Index'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Binding", Value: 33, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Binding Point'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DescriptorSet", Value: 34, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Descriptor Set'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Offset", Value: 35, Capabilities: []string{"Shader",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Byte Offset'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "XfbBuffer", Value: 36, Capabilities: []string{"TransformFeedback",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'XFB Buffer Number'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "XfbStride", Value: 37, Capabilities: []string{"TransformFeedback",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'XFB Stride'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "FuncParamAttr", Value: 38, Capabilities: []string{"Kernel",}, Parameters: []Parameter{{OperandKindFunctionParameterAttribute, "'Function Parameter Attribute'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "FPRoundingMode", Value: 39, Capabilities: []string{}, Parameters: []Parameter{{OperandKindFPRoundingMode, "'Floating-Point Rounding Mode'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "FPFastMathMode", Value: 40, - Capabilities: []string{"Kernel",}, + Capabilities: []string{"Kernel","FloatControls2",}, Parameters: []Parameter{{OperandKindFPFastMathMode, "'Fast-Math Mode'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "LinkageAttributes", Value: 41, Capabilities: []string{"Linkage",}, Parameters: []Parameter{{OperandKindLiteralString, "'Name'"},{OperandKindLinkageType, "'Linkage Type'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NoContraction", Value: 42, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InputAttachmentIndex", Value: 43, Capabilities: []string{"InputAttachment",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Attachment Index'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Alignment", Value: 44, Capabilities: []string{"Kernel",}, Parameters: []Parameter{{OperandKindLiteralInteger, "'Alignment'"},}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MaxByteOffset", @@ -22316,6 +30077,27 @@ Version: "1.4", }, Enumerant{ + Enumerant: "WeightTextureQCOM", + Value: 4487, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BlockMatchTextureQCOM", + Value: 4488, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BlockMatchSamplerQCOM", + Value: 4499, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "ExplicitInterpAMD", Value: 4999, Capabilities: []string{}, @@ -22323,6 +30105,76 @@ Version: "None", }, Enumerant{ + Enumerant: "NodeSharesPayloadLimitsWithAMDX", + Value: 5019, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Payload Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NodeMaxPayloadsAMDX", + Value: 5020, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Max number of payloads'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "TrackFinishWritingAMDX", + Value: 5078, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeNameAMDX", + Value: 5091, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Node Name'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeBaseIndexAMDX", + Value: 5098, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Base Index'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeSparseArrayAMDX", + Value: 5099, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeArraySizeAMDX", + Value: 5100, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Array Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadDispatchIndirectAMDX", + Value: 5105, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArrayStrideIdEXT", + Value: 5124, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindIdRef, "'Array Stride'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OffsetIdEXT", + Value: 5125, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindIdRef, "'Byte Offset'"},}, + Version: "1.0", + }, + Enumerant{ Enumerant: "OverrideCoverageNV", Value: 5248, Capabilities: []string{"SampleMaskOverrideCoverageNV",}, @@ -22351,9 +30203,9 @@ Version: "None", }, Enumerant{ - Enumerant: "PerPrimitiveNV", + Enumerant: "PerPrimitiveEXT", Value: 5271, - Capabilities: []string{"MeshShadingNV",}, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, Version: "None", }, @@ -22367,14 +30219,14 @@ Enumerant{ Enumerant: "PerTaskNV", Value: 5273, - Capabilities: []string{"MeshShadingNV",}, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "PerVertexNV", + Enumerant: "PerVertexKHR", Value: 5285, - Capabilities: []string{"FragmentBarycentricNV",}, + Capabilities: []string{"FragmentBarycentricKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -22386,13 +30238,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "NonUniformEXT", - Value: 5300, - Capabilities: []string{"ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "RestrictPointer", Value: 5355, Capabilities: []string{"PhysicalStorageBufferAddresses",}, @@ -22400,13 +30245,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "RestrictPointerEXT", - Value: 5355, - Capabilities: []string{"PhysicalStorageBufferAddresses",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "AliasedPointer", Value: 5356, Capabilities: []string{"PhysicalStorageBufferAddresses",}, @@ -22414,11 +30252,102 @@ Version: "1.5", }, Enumerant{ - Enumerant: "AliasedPointerEXT", - Value: 5356, - Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Enumerant: "HitObjectShaderRecordBufferNV", + Value: 5386, + Capabilities: []string{"ShaderInvocationReorderNV",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", + }, + Enumerant{ + Enumerant: "BindlessSamplerNV", + Value: 5398, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BindlessImageNV", + Value: 5399, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BoundSamplerNV", + Value: 5400, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BoundImageNV", + Value: 5401, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SIMTCallINTEL", + Value: 5599, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'N'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReferencedIndirectlyINTEL", + Value: 5602, + Capabilities: []string{"IndirectReferencesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClobberINTEL", + Value: 5607, + Capabilities: []string{"AsmINTEL",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Register'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SideEffectsINTEL", + Value: 5608, + Capabilities: []string{"AsmINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeVariableINTEL", + Value: 5624, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FuncParamIOKindINTEL", + Value: 5625, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Kind'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeFunctionINTEL", + Value: 5626, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StackCallINTEL", + Value: 5627, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GlobalVariableOffsetINTEL", + Value: 5628, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Offset'"},}, + Version: "None", }, Enumerant{ Enumerant: "CounterBuffer", @@ -22428,13 +30357,6 @@ Version: "1.4", }, Enumerant{ - Enumerant: "HlslCounterBufferGOOGLE", - Value: 5634, - Capabilities: []string{}, - Parameters: []Parameter{{OperandKindIdRef, "'Counter Buffer'"},}, - Version: "None", - }, - Enumerant{ Enumerant: "UserSemantic", Value: 5635, Capabilities: []string{}, @@ -22442,19 +30364,383 @@ Version: "1.4", }, Enumerant{ - Enumerant: "HlslSemanticGOOGLE", - Value: 5635, - Capabilities: []string{}, - Parameters: []Parameter{{OperandKindLiteralString, "'Semantic'"},}, - Version: "None", - }, - Enumerant{ Enumerant: "UserTypeGOOGLE", Value: 5636, Capabilities: []string{}, Parameters: []Parameter{{OperandKindLiteralString, "'User Type'"},}, Version: "None", }, + Enumerant{ + Enumerant: "FunctionRoundingModeINTEL", + Value: 5822, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPRoundingMode, "'FP Rounding Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionDenormModeINTEL", + Value: 5823, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPDenormMode, "'FP Denorm Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterINTEL", + Value: 5825, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MemoryINTEL", + Value: 5826, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Memory Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NumbanksINTEL", + Value: 5827, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Banks'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "BankwidthINTEL", + Value: 5828, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Bank Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxPrivateCopiesINTEL", + Value: 5829, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Maximum Copies'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SinglepumpINTEL", + Value: 5830, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DoublepumpINTEL", + Value: 5831, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxReplicatesINTEL", + Value: 5832, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Maximum Replicates'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SimpleDualPortINTEL", + Value: 5833, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MergeINTEL", + Value: 5834, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Merge Key'"},{OperandKindLiteralString, "'Merge Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "BankBitsINTEL", + Value: 5835, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Bank Bits'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ForcePow2DepthINTEL", + Value: 5836, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Force Key'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StridesizeINTEL", + Value: 5883, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Stride Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "WordsizeINTEL", + Value: 5884, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Word Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "TrueDualPortINTEL", + Value: 5885, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BurstCoalesceINTEL", + Value: 5899, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheSizeINTEL", + Value: 5900, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Size in bytes'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DontStaticallyCoalesceINTEL", + Value: 5901, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrefetchINTEL", + Value: 5902, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Prefetcher Size in bytes'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StallEnableINTEL", + Value: 5905, + Capabilities: []string{"FPGAClusterAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FuseLoopsInFunctionINTEL", + Value: 5907, + Capabilities: []string{"LoopFuseINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MathOpDSPModeINTEL", + Value: 5909, + Capabilities: []string{"FPGADSPControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Mode'"},{OperandKindLiteralInteger, "'Propagate'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "AliasScopeINTEL", + Value: 5914, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, "'Aliasing Scopes List'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoAliasINTEL", + Value: 5915, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, "'Aliasing Scopes List'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "InitiationIntervalINTEL", + Value: 5917, + Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cycles'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxConcurrencyINTEL", + Value: 5918, + Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Invocations'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PipelineEnableINTEL", + Value: 5919, + Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Enable'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "BufferLocationINTEL", + Value: 5921, + Capabilities: []string{"FPGABufferLocationINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Buffer Location ID'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "IOPipeStorageINTEL", + Value: 5944, + Capabilities: []string{"IOPipesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'IO Pipe ID'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionFloatingPointModeINTEL", + Value: 6080, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPOperationMode, "'FP Operation Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SingleElementVectorINTEL", + Value: 6085, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeCallableFunctionINTEL", + Value: 6087, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MediaBlockIOINTEL", + Value: 6140, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StallFreeINTEL", + Value: 6151, + Capabilities: []string{"FPGAClusterAttributesV2INTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPMaxErrorDecorationINTEL", + Value: 6170, + Capabilities: []string{"FPMaxErrorINTEL",}, + Parameters: []Parameter{{OperandKindLiteralFloat, "'Max Error'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "LatencyControlLabelINTEL", + Value: 6172, + Capabilities: []string{"FPGALatencyControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Latency Label'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "LatencyControlConstraintINTEL", + Value: 6173, + Capabilities: []string{"FPGALatencyControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Relative To'"},{OperandKindLiteralInteger, "'Control Type'"},{OperandKindLiteralInteger, "'Relative Cycle'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ConduitKernelArgumentINTEL", + Value: 6175, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterMapKernelArgumentINTEL", + Value: 6176, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceAddressWidthINTEL", + Value: 6177, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'AddressWidth'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceDataWidthINTEL", + Value: 6178, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'DataWidth'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceLatencyINTEL", + Value: 6179, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Latency'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceReadWriteModeINTEL", + Value: 6180, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindAccessQualifier, "'ReadWriteMode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceMaxBurstINTEL", + Value: 6181, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'MaxBurstCount'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceWaitRequestINTEL", + Value: 6182, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Waitrequest'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StableKernelArgumentINTEL", + Value: 6183, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HostAccessINTEL", + Value: 6188, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{{OperandKindHostAccessQualifier, "'Access'"},{OperandKindLiteralString, "'Name'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "InitModeINTEL", + Value: 6190, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{{OperandKindInitializationModeQualifier, "'Trigger'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImplementInRegisterMapINTEL", + Value: 6191, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "Value"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheControlLoadINTEL", + Value: 6442, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Level'"},{OperandKindLoadCacheControl, "'Cache Control'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheControlStoreINTEL", + Value: 6443, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Level'"},{OperandKindStoreCacheControl, "'Cache Control'"},}, + Version: "None", + }, }, Bases: []*OperandKind {}, } @@ -22467,287 +30753,322 @@ Value: 0, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PointSize", Value: 1, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ClipDistance", Value: 3, Capabilities: []string{"ClipDistance",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "CullDistance", Value: 4, Capabilities: []string{"CullDistance",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "VertexId", Value: 5, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InstanceId", Value: 6, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PrimitiveId", Value: 7, - Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingKHR","MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InvocationId", Value: 8, Capabilities: []string{"Geometry","Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Layer", Value: 9, - Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",}, + Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT","MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ViewportIndex", Value: 10, - Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",}, + Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT","MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TessLevelOuter", Value: 11, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TessLevelInner", Value: 12, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TessCoord", Value: 13, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PatchVertices", Value: 14, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "FragCoord", Value: 15, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "PointCoord", Value: 16, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "FrontFacing", Value: 17, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampleId", Value: 18, Capabilities: []string{"SampleRateShading",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SamplePosition", Value: 19, Capabilities: []string{"SampleRateShading",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampleMask", Value: 20, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "FragDepth", Value: 22, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "HelperInvocation", Value: 23, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NumWorkgroups", Value: 24, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WorkgroupSize", Value: 25, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WorkgroupId", Value: 26, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "LocalInvocationId", Value: 27, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GlobalInvocationId", Value: 28, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "LocalInvocationIndex", Value: 29, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WorkDim", Value: 30, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GlobalSize", Value: 31, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "EnqueuedWorkgroupSize", Value: 32, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GlobalOffset", Value: 33, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GlobalLinearId", Value: 34, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupSize", Value: 36, Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupMaxSize", Value: 37, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NumSubgroups", Value: 38, Capabilities: []string{"Kernel","GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "NumEnqueuedSubgroups", Value: 39, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupId", Value: 40, Capabilities: []string{"Kernel","GroupNonUniform",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupLocalInvocationId", Value: 41, Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "VertexIndex", Value: 42, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InstanceIndex", Value: 43, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", + }, + Enumerant{ + Enumerant: "CoreIDARM", + Value: 4160, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CoreCountARM", + Value: 4161, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CoreMaxIDARM", + Value: 4162, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WarpIDARM", + Value: 4163, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WarpMaxIDARM", + Value: 4164, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupEqMask", @@ -22785,41 +31106,6 @@ Version: "1.3", }, Enumerant{ - Enumerant: "SubgroupEqMaskKHR", - Value: 4416, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ - Enumerant: "SubgroupGeMaskKHR", - Value: 4417, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ - Enumerant: "SubgroupGtMaskKHR", - Value: 4418, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ - Enumerant: "SubgroupLeMaskKHR", - Value: 4419, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ - Enumerant: "SubgroupLtMaskKHR", - Value: 4420, - Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ Enumerant: "BaseVertex", Value: 4424, Capabilities: []string{"DrawParameters",}, @@ -22836,11 +31122,18 @@ Enumerant{ Enumerant: "DrawIndex", Value: 4426, - Capabilities: []string{"DrawParameters","MeshShadingNV",}, + Capabilities: []string{"DrawParameters","MeshShadingNV","MeshShadingEXT",}, Parameters: []Parameter{}, Version: "1.3", }, Enumerant{ + Enumerant: "PrimitiveShadingRateKHR", + Value: 4432, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "DeviceIndex", Value: 4438, Capabilities: []string{"DeviceGroup",}, @@ -22855,6 +31148,13 @@ Version: "1.3", }, Enumerant{ + Enumerant: "ShadingRateKHR", + Value: 4444, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "BaryCoordNoPerspAMD", Value: 4992, Capabilities: []string{}, @@ -22911,6 +31211,20 @@ Version: "None", }, Enumerant{ + Enumerant: "RemainingRecursionLevelsAMDX", + Value: 5021, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderIndexAMDX", + Value: 5073, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "ViewportMaskNV", Value: 5253, Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",}, @@ -23009,247 +31323,191 @@ Version: "None", }, Enumerant{ - Enumerant: "BaryCoordNV", + Enumerant: "BaryCoordKHR", Value: 5286, - Capabilities: []string{"FragmentBarycentricNV",}, + Capabilities: []string{"FragmentBarycentricKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "BaryCoordNoPerspNV", + Enumerant: "BaryCoordNoPerspKHR", Value: 5287, - Capabilities: []string{"FragmentBarycentricNV",}, + Capabilities: []string{"FragmentBarycentricKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "FragSizeEXT", Value: 5292, - Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FragmentSizeNV", - Value: 5292, - Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, + Capabilities: []string{"FragmentDensityEXT",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "FragInvocationCountEXT", Value: 5293, - Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, + Capabilities: []string{"FragmentDensityEXT",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "InvocationsPerPixelNV", - Value: 5293, - Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, + Enumerant: "PrimitivePointIndicesEXT", + Value: 5294, + Capabilities: []string{"MeshShadingEXT",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "LaunchIdNV", - Value: 5319, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Enumerant: "PrimitiveLineIndicesEXT", + Value: 5295, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveTriangleIndicesEXT", + Value: 5296, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullPrimitiveEXT", + Value: 5299, + Capabilities: []string{"MeshShadingEXT",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchIdKHR", Value: 5319, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "LaunchSizeNV", - Value: 5320, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchSizeKHR", Value: 5320, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "WorldRayOriginNV", - Value: 5321, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayOriginKHR", Value: 5321, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "WorldRayDirectionNV", - Value: 5322, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayDirectionKHR", Value: 5322, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ObjectRayOriginNV", - Value: 5323, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayOriginKHR", Value: 5323, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ObjectRayDirectionNV", - Value: 5324, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayDirectionKHR", Value: 5324, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "RayTminNV", - Value: 5325, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTminKHR", Value: 5325, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "RayTmaxNV", - Value: 5326, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTmaxKHR", Value: 5326, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "InstanceCustomIndexNV", - Value: 5327, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "InstanceCustomIndexKHR", Value: 5327, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "ObjectToWorldNV", - Value: 5330, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectToWorldKHR", Value: 5330, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "WorldToObjectNV", - Value: 5331, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldToObjectKHR", Value: 5331, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitTNV", Value: 5332, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "HitTKHR", - Value: 5332, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "HitKindNV", - Value: 5333, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitKindKHR", Value: 5333, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "IncomingRayFlagsNV", - Value: 5351, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Enumerant: "CurrentRayTimeNV", + Value: 5334, + Capabilities: []string{"RayTracingMotionBlurNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitTriangleVertexPositionsKHR", + Value: 5335, + Capabilities: []string{"RayTracingPositionFetchKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitMicroTriangleVertexPositionsNV", + Value: 5337, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitMicroTriangleVertexBarycentricsNV", + Value: 5344, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayFlagsKHR", Value: 5351, - Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayGeometryIndexKHR", Value: 5352, - Capabilities: []string{"RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -23281,6 +31539,27 @@ Parameters: []Parameter{}, Version: "None", }, + Enumerant{ + Enumerant: "HitKindFrontFacingMicroTriangleNV", + Value: 5405, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitKindBackFacingMicroTriangleNV", + Value: 5406, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullMaskKHR", + Value: 6021, + Capabilities: []string{"RayCullMaskKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, }, Bases: []*OperandKind {}, } @@ -23293,35 +31572,35 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Device", Value: 1, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Workgroup", Value: 2, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Subgroup", Value: 3, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Invocation", Value: 4, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "QueueFamily", @@ -23331,18 +31610,11 @@ Version: "1.5", }, Enumerant{ - Enumerant: "QueueFamilyKHR", - Value: 5, - Capabilities: []string{"VulkanMemoryModel",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "ShaderCallKHR", Value: 6, - Capabilities: []string{"RayTracingProvisionalKHR",}, + Capabilities: []string{"RayTracingKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, }, Bases: []*OperandKind {}, @@ -23356,21 +31628,21 @@ Value: 0, Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InclusiveScan", Value: 1, Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ExclusiveScan", Value: 2, Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ClusteredReduce", @@ -23412,21 +31684,21 @@ Value: 0, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WaitKernel", Value: 1, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "WaitWorkGroup", Value: 2, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, }, Bases: []*OperandKind {}, @@ -23440,392 +31712,392 @@ Value: 0, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Shader", Value: 1, Capabilities: []string{"Matrix",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Geometry", Value: 2, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Tessellation", Value: 3, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Addresses", Value: 4, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Linkage", Value: 5, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Kernel", Value: 6, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Vector16", Value: 7, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Float16Buffer", Value: 8, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Float16", Value: 9, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Float64", Value: 10, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Int64", Value: 11, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Int64Atomics", Value: 12, Capabilities: []string{"Int64",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageBasic", Value: 13, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageReadWrite", Value: 14, Capabilities: []string{"ImageBasic",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageMipmap", Value: 15, Capabilities: []string{"ImageBasic",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Pipes", Value: 17, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Groups", Value: 18, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DeviceEnqueue", Value: 19, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "LiteralSampler", Value: 20, Capabilities: []string{"Kernel",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "AtomicStorage", Value: 21, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Int16", Value: 22, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TessellationPointSize", Value: 23, Capabilities: []string{"Tessellation",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GeometryPointSize", Value: 24, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageGatherExtended", Value: 25, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageImageMultisample", Value: 27, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "UniformBufferArrayDynamicIndexing", Value: 28, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampledImageArrayDynamicIndexing", Value: 29, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageBufferArrayDynamicIndexing", Value: 30, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageImageArrayDynamicIndexing", Value: 31, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ClipDistance", Value: 32, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "CullDistance", Value: 33, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageCubeArray", Value: 34, Capabilities: []string{"SampledCubeArray",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampleRateShading", Value: 35, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageRect", Value: 36, Capabilities: []string{"SampledRect",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampledRect", Value: 37, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GenericPointer", Value: 38, Capabilities: []string{"Addresses",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Int8", Value: 39, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InputAttachment", Value: 40, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SparseResidency", Value: 41, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MinLod", Value: 42, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Sampled1D", Value: 43, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "Image1D", Value: 44, Capabilities: []string{"Sampled1D",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampledCubeArray", Value: 45, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SampledBuffer", Value: 46, Capabilities: []string{}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageBuffer", Value: 47, Capabilities: []string{"SampledBuffer",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageMSArray", Value: 48, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageImageExtendedFormats", Value: 49, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "ImageQuery", Value: 50, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "DerivativeControl", Value: 51, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "InterpolationFunction", Value: 52, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "TransformFeedback", Value: 53, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "GeometryStreams", Value: 54, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageImageReadWithoutFormat", Value: 55, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "StorageImageWriteWithoutFormat", Value: 56, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "MultiViewport", Value: 57, Capabilities: []string{"Geometry",}, Parameters: []Parameter{}, - Version: "", + Version: "1.0", }, Enumerant{ Enumerant: "SubgroupDispatch", @@ -23919,6 +32191,55 @@ Version: "1.5", }, Enumerant{ + Enumerant: "UniformDecoration", + Value: 71, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "CoreBuiltinsARM", + Value: 4165, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TileImageColorReadAccessEXT", + Value: 4166, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TileImageDepthReadAccessEXT", + Value: 4167, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TileImageStencilReadAccessEXT", + Value: 4168, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixLayoutsARM", + Value: 4201, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShadingRateKHR", + Value: 4422, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "SubgroupBallotKHR", Value: 4423, Capabilities: []string{}, @@ -23933,6 +32254,27 @@ Version: "1.3", }, Enumerant{ + Enumerant: "WorkgroupMemoryExplicitLayoutKHR", + Value: 4428, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorkgroupMemoryExplicitLayout8BitAccessKHR", + Value: 4429, + Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorkgroupMemoryExplicitLayout16BitAccessKHR", + Value: 4430, + Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "SubgroupVoteKHR", Value: 4431, Capabilities: []string{}, @@ -23947,23 +32289,9 @@ Version: "1.3", }, Enumerant{ - Enumerant: "StorageUniformBufferBlock16", - Value: 4433, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ Enumerant: "UniformAndStorageBuffer16BitAccess", Value: 4434, - Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, - Parameters: []Parameter{}, - Version: "1.3", - }, - Enumerant{ - Enumerant: "StorageUniform16", - Value: 4434, - Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, + Capabilities: []string{"StorageBuffer16BitAccess",}, Parameters: []Parameter{}, Version: "1.3", }, @@ -24087,9 +32415,58 @@ Version: "None", }, Enumerant{ - Enumerant: "RayTraversalPrimitiveCullingProvisionalKHR", + Enumerant: "RayQueryKHR", + Value: 4472, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "UntypedPointersKHR", + Value: 4473, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTraversalPrimitiveCullingKHR", Value: 4478, - Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingKHR", + Value: 4479, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureSampleWeightedQCOM", + Value: 4484, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureBoxFilterQCOM", + Value: 4485, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureBlockMatchQCOM", + Value: 4486, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureBlockMatch2QCOM", + Value: 4498, + Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, @@ -24129,13 +32506,34 @@ Version: "None", }, Enumerant{ + Enumerant: "Int64ImageEXT", + Value: 5016, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "ShaderClockKHR", Value: 5055, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderEnqueueAMDX", + Value: 5067, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ + Enumerant: "QuadControlKHR", + Value: 5087, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "SampleMaskOverrideCoverageNV", Value: 5249, Capabilities: []string{"SampleRateShading",}, @@ -24157,16 +32555,9 @@ Version: "None", }, Enumerant{ - Enumerant: "ShaderViewportIndexLayerNV", - Value: 5254, - Capabilities: []string{"MultiViewport",}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ Enumerant: "ShaderViewportMaskNV", Value: 5255, - Capabilities: []string{"ShaderViewportIndexLayerNV",}, + Capabilities: []string{"ShaderViewportIndexLayerEXT",}, Parameters: []Parameter{}, Version: "None", }, @@ -24206,28 +32597,28 @@ Version: "None", }, Enumerant{ - Enumerant: "FragmentBarycentricNV", + Enumerant: "MeshShadingEXT", + Value: 5283, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentBarycentricKHR", Value: 5284, Capabilities: []string{}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ComputeDerivativeGroupQuadsNV", + Enumerant: "ComputeDerivativeGroupQuadsKHR", Value: 5288, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "None", - }, - Enumerant{ - Enumerant: "FragmentDensityEXT", - Value: 5291, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "ShadingRateNV", + Enumerant: "FragmentDensityEXT", Value: 5291, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, @@ -24248,13 +32639,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "ShaderNonUniformEXT", - Value: 5301, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "RuntimeDescriptorArray", Value: 5302, Capabilities: []string{"Shader",}, @@ -24262,13 +32646,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "RuntimeDescriptorArrayEXT", - Value: 5302, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "InputAttachmentArrayDynamicIndexing", Value: 5303, Capabilities: []string{"InputAttachment",}, @@ -24276,13 +32653,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "InputAttachmentArrayDynamicIndexingEXT", - Value: 5303, - Capabilities: []string{"InputAttachment",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "UniformTexelBufferArrayDynamicIndexing", Value: 5304, Capabilities: []string{"SampledBuffer",}, @@ -24290,13 +32660,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "UniformTexelBufferArrayDynamicIndexingEXT", - Value: 5304, - Capabilities: []string{"SampledBuffer",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "StorageTexelBufferArrayDynamicIndexing", Value: 5305, Capabilities: []string{"ImageBuffer",}, @@ -24304,13 +32667,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "StorageTexelBufferArrayDynamicIndexingEXT", - Value: 5305, - Capabilities: []string{"ImageBuffer",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "UniformBufferArrayNonUniformIndexing", Value: 5306, Capabilities: []string{"ShaderNonUniform",}, @@ -24318,13 +32674,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "UniformBufferArrayNonUniformIndexingEXT", - Value: 5306, - Capabilities: []string{"ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "SampledImageArrayNonUniformIndexing", Value: 5307, Capabilities: []string{"ShaderNonUniform",}, @@ -24332,13 +32681,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "SampledImageArrayNonUniformIndexingEXT", - Value: 5307, - Capabilities: []string{"ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "StorageBufferArrayNonUniformIndexing", Value: 5308, Capabilities: []string{"ShaderNonUniform",}, @@ -24346,13 +32688,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "StorageBufferArrayNonUniformIndexingEXT", - Value: 5308, - Capabilities: []string{"ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "StorageImageArrayNonUniformIndexing", Value: 5309, Capabilities: []string{"ShaderNonUniform",}, @@ -24360,13 +32695,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "StorageImageArrayNonUniformIndexingEXT", - Value: 5309, - Capabilities: []string{"ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "InputAttachmentArrayNonUniformIndexing", Value: 5310, Capabilities: []string{"InputAttachment","ShaderNonUniform",}, @@ -24374,13 +32702,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "InputAttachmentArrayNonUniformIndexingEXT", - Value: 5310, - Capabilities: []string{"InputAttachment","ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "UniformTexelBufferArrayNonUniformIndexing", Value: 5311, Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, @@ -24388,13 +32709,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "UniformTexelBufferArrayNonUniformIndexingEXT", - Value: 5311, - Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "StorageTexelBufferArrayNonUniformIndexing", Value: 5312, Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, @@ -24402,11 +32716,11 @@ Version: "1.5", }, Enumerant{ - Enumerant: "StorageTexelBufferArrayNonUniformIndexingEXT", - Value: 5312, - Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, + Enumerant: "RayTracingPositionFetchKHR", + Value: 5336, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, Enumerant{ Enumerant: "RayTracingNV", @@ -24416,14 +32730,14 @@ Version: "None", }, Enumerant{ - Enumerant: "VulkanMemoryModel", - Value: 5345, - Capabilities: []string{}, + Enumerant: "RayTracingMotionBlurNV", + Value: 5341, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, - Version: "1.5", + Version: "None", }, Enumerant{ - Enumerant: "VulkanMemoryModelKHR", + Enumerant: "VulkanMemoryModel", Value: 5345, Capabilities: []string{}, Parameters: []Parameter{}, @@ -24437,13 +32751,6 @@ Version: "1.5", }, Enumerant{ - Enumerant: "VulkanMemoryModelDeviceScopeKHR", - Value: 5346, - Capabilities: []string{}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ Enumerant: "PhysicalStorageBufferAddresses", Value: 5347, Capabilities: []string{"Shader",}, @@ -24451,16 +32758,9 @@ Version: "1.5", }, Enumerant{ - Enumerant: "PhysicalStorageBufferAddressesEXT", - Value: 5347, - Capabilities: []string{"Shader",}, - Parameters: []Parameter{}, - Version: "1.5", - }, - Enumerant{ - Enumerant: "ComputeDerivativeGroupLinearNV", + Enumerant: "ComputeDerivativeGroupLinearKHR", Value: 5350, - Capabilities: []string{}, + Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, @@ -24507,10 +32807,108 @@ Version: "None", }, Enumerant{ - Enumerant: "DemoteToHelperInvocationEXT", + Enumerant: "DemoteToHelperInvocation", Value: 5379, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DisplacementMicromapNV", + Value: 5380, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingOpacityMicromapEXT", + Value: 5381, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderInvocationReorderNV", + Value: 5383, + Capabilities: []string{"RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BindlessTextureNV", + Value: 5390, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryPositionFetchKHR", + Value: 5391, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat16VectorNV", + Value: 5404, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingDisplacementMicromapNV", + Value: 5409, + Capabilities: []string{"RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RawAccessChainsNV", + Value: 5414, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixReductionsNV", + Value: 5430, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixConversionsNV", + Value: 5431, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixPerElementOperationsNV", + Value: 5432, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixTensorAddressingNV", + Value: 5433, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixBlockLoadsNV", + Value: 5434, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TensorAddressingNV", + Value: 5439, + Capabilities: []string{}, + Parameters: []Parameter{}, Version: "None", }, Enumerant{ @@ -24542,6 +32940,20 @@ Version: "None", }, Enumerant{ + Enumerant: "RoundToInfinityINTEL", + Value: 5582, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatingPointModeINTEL", + Value: 5583, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "IntegerFunctions2INTEL", Value: 5584, Capabilities: []string{"Shader",}, @@ -24549,6 +32961,69 @@ Version: "None", }, Enumerant{ + Enumerant: "FunctionPointersINTEL", + Value: 5603, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IndirectReferencesINTEL", + Value: 5604, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AsmINTEL", + Value: 5606, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat32MinMaxEXT", + Value: 5612, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat64MinMaxEXT", + Value: 5613, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat16MinMaxEXT", + Value: 5616, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeINTEL", + Value: 5617, + Capabilities: []string{"VectorAnyINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorAnyINTEL", + Value: 5619, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ExpectAssumeKHR", + Value: 5629, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ Enumerant: "SubgroupAvcMotionEstimationINTEL", Value: 5696, Capabilities: []string{}, @@ -24569,6 +33044,384 @@ Parameters: []Parameter{}, Version: "None", }, + Enumerant{ + Enumerant: "VariableLengthArrayINTEL", + Value: 5817, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionFloatControlINTEL", + Value: 5821, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAMemoryAttributesINTEL", + Value: 5824, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPFastMathModeINTEL", + Value: 5837, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArbitraryPrecisionIntegersINTEL", + Value: 5844, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArbitraryPrecisionFloatingPointINTEL", + Value: 5845, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "UnstructuredLoopControlsINTEL", + Value: 5886, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGALoopControlsINTEL", + Value: 5888, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "KernelAttributesINTEL", + Value: 5892, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAKernelAttributesINTEL", + Value: 5897, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAMemoryAccessesINTEL", + Value: 5898, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAClusterAttributesINTEL", + Value: 5904, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LoopFuseINTEL", + Value: 5906, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGADSPControlINTEL", + Value: 5908, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MemoryAccessAliasingINTEL", + Value: 5910, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAInvocationPipeliningAttributesINTEL", + Value: 5916, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGABufferLocationINTEL", + Value: 5920, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArbitraryPrecisionFixedPointINTEL", + Value: 5922, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "USMStorageClassesINTEL", + Value: 5935, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RuntimeAlignedAttributeINTEL", + Value: 5939, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IOPipesINTEL", + Value: 5943, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BlockingPipesINTEL", + Value: 5945, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGARegINTEL", + Value: 5948, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DotProductInputAll", + Value: 6016, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DotProductInput4x8Bit", + Value: 6017, + Capabilities: []string{"Int8",}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DotProductInput4x8BitPacked", + Value: 6018, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DotProduct", + Value: 6019, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "RayCullMaskKHR", + Value: 6020, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixKHR", + Value: 6022, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReplicatedCompositesEXT", + Value: 6024, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BitInstructions", + Value: 6025, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GroupNonUniformRotateKHR", + Value: 6026, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatControls2", + Value: 6029, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat32AddEXT", + Value: 6033, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat64AddEXT", + Value: 6034, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LongCompositesINTEL", + Value: 6089, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OptNoneEXT", + Value: 6094, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat16AddEXT", + Value: 6095, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DebugInfoModuleINTEL", + Value: 6114, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BFloat16ConversionINTEL", + Value: 6115, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SplitBarrierINTEL", + Value: 6141, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArithmeticFenceEXT", + Value: 6144, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAClusterAttributesV2INTEL", + Value: 6150, + Capabilities: []string{"FPGAClusterAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAKernelAttributesv2INTEL", + Value: 6161, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPMaxErrorINTEL", + Value: 6169, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGALatencyControlINTEL", + Value: 6171, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAArgumentInterfacesINTEL", + Value: 6174, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GlobalVariableHostAccessINTEL", + Value: 6187, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GlobalVariableFPGADecorationsINTEL", + Value: 6189, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupBufferPrefetchINTEL", + Value: 6220, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GroupUniformArithmeticKHR", + Value: 6400, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaskedGatherScatterINTEL", + Value: 6427, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheControlsINTEL", + Value: 6441, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterLimitsINTEL", + Value: 6460, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, }, Bases: []*OperandKind {}, } @@ -24579,16 +33432,16 @@ Enumerant{ Enumerant: "RayQueryCandidateIntersectionKHR", Value: 0, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionKHR", Value: 1, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, }, Bases: []*OperandKind {}, @@ -24600,23 +33453,23 @@ Enumerant{ Enumerant: "RayQueryCommittedIntersectionNoneKHR", Value: 0, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionTriangleKHR", Value: 1, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionGeneratedKHR", Value: 2, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, }, Bases: []*OperandKind {}, @@ -24628,20 +33481,363 @@ Enumerant{ Enumerant: "RayQueryCandidateIntersectionTriangleKHR", Value: 0, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, Enumerant{ Enumerant: "RayQueryCandidateIntersectionAABBKHR", Value: 1, - Capabilities: []string{"RayQueryProvisionalKHR",}, + Capabilities: []string{"RayQueryKHR",}, Parameters: []Parameter{}, - Version: "", + Version: "None", }, }, Bases: []*OperandKind {}, } + OperandKindPackedVectorFormat = &OperandKind { + Kind: "PackedVectorFormat", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "PackedVectorFormat4x8Bit", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixOperands = &OperandKind { + Kind: "CooperativeMatrixOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoneKHR", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixASignedComponentsKHR", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixBSignedComponentsKHR", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixCSignedComponentsKHR", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixResultSignedComponentsKHR", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SaturatingAccumulationKHR", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixLayout = &OperandKind { + Kind: "CooperativeMatrixLayout", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RowMajorKHR", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ColumnMajorKHR", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RowBlockedInterleavedARM", + Value: 4202, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ColumnBlockedInterleavedARM", + Value: 4203, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixUse = &OperandKind { + Kind: "CooperativeMatrixUse", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "MatrixAKHR", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixBKHR", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixAccumulatorKHR", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixReduce = &OperandKind { + Kind: "CooperativeMatrixReduce", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Row", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Column", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "2x2", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindTensorClampMode = &OperandKind { + Kind: "TensorClampMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Undefined", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Constant", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClampToEdge", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Repeat", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RepeatMirrored", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindTensorAddressingOperands = &OperandKind { + Kind: "TensorAddressingOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TensorView", + Value: 0x0001, + Capabilities: []string{"CooperativeMatrixTensorAddressingNV",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DecodeFunc", + Value: 0x0002, + Capabilities: []string{"CooperativeMatrixBlockLoadsNV",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindInitializationModeQualifier = &OperandKind { + Kind: "InitializationModeQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "InitOnDeviceReprogramINTEL", + Value: 0, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InitOnDeviceResetINTEL", + Value: 1, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLoadCacheControl = &OperandKind { + Kind: "LoadCacheControl", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "UncachedINTEL", + Value: 0, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CachedINTEL", + Value: 1, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StreamingINTEL", + Value: 2, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InvalidateAfterReadINTEL", + Value: 3, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ConstCachedINTEL", + Value: 4, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindStoreCacheControl = &OperandKind { + Kind: "StoreCacheControl", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "UncachedINTEL", + Value: 0, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WriteThroughINTEL", + Value: 1, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WriteBackINTEL", + Value: 2, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StreamingINTEL", + Value: 3, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindNamedMaximumNumberOfRegisters = &OperandKind { + Kind: "NamedMaximumNumberOfRegisters", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "AutoINTEL", + Value: 0, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPEncoding = &OperandKind { + Kind: "FPEncoding", + Category: "ValueEnum", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } OperandKindIdResultType = &OperandKind { Kind: "IdResultType", Category: "Id", @@ -24691,6 +33887,13 @@ }, Bases: []*OperandKind {}, } + OperandKindLiteralFloat = &OperandKind { + Kind: "LiteralFloat", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } OperandKindLiteralContextDependentNumber = &OperandKind { Kind: "LiteralContextDependentNumber", Category: "Literal", @@ -24738,6 +33941,13 @@ Category: "BitEnum", Enumerants: []Enumerant { Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ Enumerant: "FlagIsProtected", Value: 0x01, Capabilities: []string{},