Squashed 'third_party/SPIRV-Tools/' changes from 51892874b..dd4b663e1 dd4b663e1 Prepare release v2024.2 (#5651) be6fb2a54 build(deps): bump the github-actions group across 1 directory with 4 updates (#5650) dadb3012d Add SPIRV_TOOLS_EXPORT to public C++ API (#5591) 53c073606 A fix to support of SPV_QCOM_image_processing2 (#5646) 67a3ed670 opt: add GroupNonUniformPartitionedNV capability to trim pass (#5648) 2904985ae spirv-val: Add Vulkan check for Rect Dim in OpTypeImage (#5644) 02470f606 Validate duplicate decorations and execution modes (#5641) 6761288d3 Validator: Support SPV_NV_raw_access_chains (#5568) 3983d15a1 Fix rebuilding types with circular references (#5623). (#5637) ade1f7cfd Add AliasedPointer decoration (#5635) 24f2cdad8 build(deps): bump the github-actions group with 1 update (#5634) 58ab8baf7 docs: explain LunarG is the source of truth for releases (#5627) 7fe5f75e5 Roll external/re2/ 6598a8ecd..917047f36 (3 commits) (#5626) 87721a100 Roll external/spirv-headers/ 7d500c4d7..4f7b471f1 (1 commit) (#5625) fe7bae090 Minor fix to cmakelists to avoid rerunning command each build (#5620) fc4286556 build(deps): bump the github-actions group with 2 updates (#5621) 67451ebf6 Roll external/spirv-headers/ 04db24d69..7d500c4d7 (1 commit) (#5619) dda7731e9 build(deps): bump the github-actions group with 2 updates (#5618) 3fafcc20e Roll external/spirv-headers/ 8b246ff75..04db24d69 (1 commit) (#5617) 3a0471c3b build(deps): bump the github-actions group with 1 update (#5615) 6c3f632a2 roll deps (#5614) c6615779e Roll external/googletest/ b479e7a3c..c231e6f5b (1 commit) (#5613) f20663ca7 add support for vulkan-shader-profiler external passes (#5512) f74f4e74c Roll external/re2/ ed9fc269e..d00d1e937 (2 commits) (#5589) e39cabca2 build(deps): bump the github-actions group with 2 updates (#5610) efb0fce2d Use bazel 7 and bzlmod (#5601) 02c79e908 kokoro: Update bazel to 7.0.2 for Linux builds (#5609) f869d391a [OPT] Fix handling of analyses rebuild (#5608) d15a7aa25 kokoro: Update bazel to 7.0.2 for Mac builds (#5606) 04896c462 Prepare release v2024.1 (#5605) 7c363050d Add operand types for SPV_NV_raw_access_chains (#5602) 5bc7c2876 build(deps): bump the github-actions group with 2 updates (#5598) 75ad1345d Remove redundant function declarations from source/operand.h (#5584) 9bd44d028 Suppot for SPV_QCOM_image_processing2 (#5582) 0b027bafa Support operand kind for SPV_INTEL_maximum_registers (#5580) fbc7a14b3 Fix access chain struct checks (#5592) 99a3ad32f build(deps): bump the github-actions group with 1 update (#5594) c3a9ffd74 build(deps): bump the github-actions group with 1 update (#5593) 1b643eac5 spirv-val: Make Constant evaluation consistent (#5587) dc6676445 Roll external/googletest/ 6eb225cb8..5df0241ea (2 commits) (#5583) 7da2c941f Update WORKSPACE (#5588) 16af142c1 build(deps): bump the github-actions group with 1 update (#5586) b0a5c4ac1 SPV_NV_shader_atomic_fp16_vector (#5581) 55cb3989e build(deps): bump the github-actions group with 1 update (#5578) 11afeb4bb roll deps (#5576) 7604147c2 [OPT] Add removed unused interface var pass to legalization passes (#5579) f9184c650 spirv-val: Revert Validate PhysicalStorageBuffer Stage Interface (#5575) 20ad38c18 spirv-val: Multiple interface var with same SC (#5528) e08c012b1 [OPT] Identify arrays with unknown length in copy prop arrays (#5570) 56a51dd94 Roll external/spirv-headers/ e77d03080..d3c2a6fa9 (1 commit) (#5574) 0c986f596 update image enum tests to remove Kernel capability (#5562) b7413609c [OPT] Use new instruction folder for for all opcodes in spec consti folding (#5569) 784b064f9 spirv-val: Validate PhysicalStorageBuffer Stage Interface (#5539) a8959dc65 Fold 64-bit int operations (#5561) 80926d97a roll deps (#5566) 9a7b1af90 build(deps): bump the github-actions group with 1 update (#5564) 1a2cbabd8 Roll external/googletest/ 48729681a..64be1c79f (1 commit) (#5563) 7657cb1c6 build(deps): bump the github-actions group with 3 updates (#5560) 032c15aaf [NFC] Refactor code to fold instruction in fold tests. (#5558) 9938f5bc2 Roll external/googletest/ 456574145..48729681a (1 commit) (#5559) ab59dc608 opt: prevent meld to merge block with MaximalReconvergence (#5557) 6c11c2bd4 Roll external/re2/ 283636ffb..ab7c5918b (2 commits) (#5555) a8afbe941 roll deps (#5550) 8d3ee2e8f spirv-opt: Fix OpCompositeExtract relaxation with struct operands (#5536) 61c51d4ba spirv-val: Add Mesh Primitive Built-In validaiton (#5529) 5d3c8b73f opt: Add OpEntryPoint to DescriptorScalarReplacement pass (#5553) de65e8174 [NFC] Remove unused code (#5554) ad11927e6 opt: add SPV_EXT_mesh_shader to opt allowlist (#5551) 27ffe976e build(deps): bump the github-actions group with 2 updates (#5549) e5fcb7fac Roll external/re2/ 264e71e88..826ad10e5 (1 commit) (#5538) 80bc99c3d Skip entire test/ folder if SPIRV_SKIP_TESTS is set. (#5548) 0a6f0d189 opt: Add TrimCapabilities pass to spirv-opt tool (#5545) b951948ea SPV_KHR_quad_control (#5547) 69197ba90 Add modify-maximal-reconvergence to spirv-opt help (#5546) 0045b01ff opt: Add VulkanMemoryModelDeviceScope to trim (#5544) ef2f43236 Add support for SPV_KHR_float_controls2 (#5543) de3d5acc0 Add tooling support for SPV_KHR_maximal_reconvergence (#5542) 14000ad47 Use python3 explicitly. (#5540) 359012927 workflow: add vulkan-sdk tags as release tags (#5518) 3e6bdd0f9 build(deps): bump the github-actions group with 3 updates (#5537) ed6835aff Roll external/re2/ c042630ed..32c181e0a (1 commit) (#5532) c96fe8b94 spirv-val: Re-enable OpControlBarrier VU (#5527) 5dbdc7b60 build(deps): bump the github-actions group with 4 updates (#5531) 155728b2e Add preserver-interface option to spirv-opt (#5524) 01ee1bf31 Roll external/googletest/ b10fad38c..76bb2afb8 (1 commit) (#5485) 36be541ee Remove unnecessary debug code (#5523) c7affa170 opt: add Int16 and Float16 to capability trim pass (#5519) 0a9f3d1f2 Revert "Fix(cmake): CMake doesn't find system installed SPIRV-Headers (#5422)" (#5517) 7d2429594 Fix(cmake): CMake doesn't find system installed SPIRV-Headers (#5422) f0cc85efd Prepare release v2023.6 (#5510) e03c8f5c8 Fix broken build (#5505) d75b3cfbb Zero initialize local variables (#5501) 6b4f0c9d0 instrument: Fix handling of gl_InvocationID (#5493) b5d60826e printf: Remove stage specific info (#5495) e7a52b70f build(deps): bump the github-actions group with 1 update (#5498) 2da75e152 Do not crash when tryingto fold unsupported spec constant (#5496) 0d8784553 Remove uses of std::system(nullptr) (#5494) f4a73dd7a std::system requires include of <cstdlib> (#5486) ffe645023 Add iOS build to CI (#5490) afaf8fda2 Fix iOS / Android CMake builds (#5482) 7d2a618bf build(deps): bump the github-actions group with 1 update (#5484) 2a238ed24 Roll external/spirv-headers/ 38f39dae5..cca08c63c (2 commits) (#5480) 246e6d4c6 spirv-val: Loosen restriction on base type of DebugTypePointer and DebugTypeQualifier (#5479) 0df791f97 Fix nullptr argument in MarkInsertChain (#5465) fb91e6f0e Flush stdout before changing mode back to text (#5477) 560eea6d7 build(deps): bump the github-actions group with 1 update (#5478) c8510a5e8 Fix python warning seen on Fedora 39 (#5474) 8ee3ae524 Add comment to --inst-debug-printf option (#5466) f43c464d5 opt: add PhysicalStorageBufferAddresses to trim (#5476) c91e9d09b opt: add StorageImageReadWithoutFormat to cap trim (#5475) d88742fbd fix(build): git describe all tagged versions (#5447) 6b1e609ef Support missing git in update_build_version.py (#5473) fbf047cc8 Roll external/re2/ 24d460a9d..974f44c8d (4 commits) (#5470) 9e7a1f2dd Fix array size calculation (#5463) eacc969b7 build(deps): bump the github-actions group with 2 updates (#5457) 7210d247c Roll external/googletest/ 518387203..5b7fd63d6 (1 commit) (#5454) a08f648c8 Remove references to __FILE__ (#5462) c87755bb9 spirv-val: Add WorkgroupMemoryExplicitLayoutKHR check for Block (#5461) 4f014aff9 Roll external/re2/ 601d9ea3e..a0b3bc60c (1 commit) (#5453) 33bac5144 Roll external/googletest/ 116b7e552..518387203 (1 commit) (#5450) 01e851be9 Roll external/re2/ 928a015e6..601d9ea3e (1 commit) (#5448) 1928c76cd Roll external/googletest/ 2dd1c1319..829c19901 (1 commit) (#5444) 73876defc opt: support 64-bit OpAccessChain index in FixStorageClass (#5446) 5084f58e5 build(deps): bump the github-actions group with 4 updates (#5445) a9c61d137 update_build_version.py produce deterministic header. (#5426) 5bb595091 Add ComputeDerivativeGroup*NV capabilities to trim capabilities pass. (#5430) 3985f0da0 Roll external/spirv-headers/ e867c0663..4183b260f (1 commit) (#5439) 661f429b1 Roll external/re2/ b673de358..ece4cecab (2 commits) (#5437) 360d469b9 Prepare release v2023.5.rc1 (#5423) 74005dfa6 Roll external/re2/ 35bb195de..b673de358 (2 commits) (#5433) 933db564c roll deps (#5432) ce995b319 Hash pin workflows and config dependabot (#5412) df2f2a031 build(deps): bump get-func-name from 2.0.0 to 2.0.2 in /tools/sva (#5418) 866e60def Roll external/spirv-headers/ 79743b899..e867c0663 (2 commits) (#5427) 023a8c79e opt: add Float64 capability to trim pass (#5428) 4fab7435b Roll external/googletest/ e47544ad3..beb552fb4 (2 commits) (#5424) 847715d6c instrument: Ensure linking works even of nothing is changed (#5419) dc9900967 Update BUILD.gn to include header for new pass (#5421) 1bc0e6f59 Add a new legalization pass to dedupe invocation interlock instructions (#5409) 48c97c131 roll deps (#5415) 27673a054 Remove reviewer from autoroller (#5414) ee7598d49 instrument: Use Import linkage for instrumentation functions (#5355) a40483d31 roll deps (#5408) a996591b1 Update SPIRV-Headers, add cache control operand kinds (#5406) fc54e178e Change autoroll pr review id (#5404) 2d6996f73 Check for git repository before git commands (#5403) 361638cfd Make sure that fragment shader interlock instructions are not removed by DCE (#5400) 5e6054c1c Roll external/re2/ e0077036c..a807e8a3a (6 commits) (#5401) 47b63a4d7 val: re-add ImageMSArray validation (#5394) d660bb55b Add SPV_KHR_physical_storage_buffer to allowlists (#5402) 440f018cc Fix `AddMemberDecoration` variable names. (#5399) 4e0b94ed7 opt: add ImageMSArray capability to trim pass. (#5395) d474a0708 Add SPV_EXT_fragment_shader_interlock to allow lists (#5393) 1f07f483e opt: add raytracing/rayquery to trim pass (#5397) 158bc7bd6 Roll external/re2/ 523f9b097..e0077036c (2 commits) (#5391) 1121c2319 opt: add Int64 capability to trim pass (#5398) 3cc7e1c4c NFC: rename tests using capability as prefix (#5396) 4c16c35b1 opt: add FragmentShader*InterlockEXT to capability trim pass (#5390) 9b923f7cc QNX has support for ANSI ESC codes, default terminal is QANSI. (#5387) 51367c40f Enable OpenSSF Scorecard and Badge (#5377) d09c753a4 Roll external/re2/ 73031bbc0..523f9b097 (1 commit) (#5389) b6893ccdf Roll external/googletest/ 460ae9826..8a6feabf0 (1 commit) (#5388) 1b3c4cb68 roll deps (#5386) abd548b81 roll deps (#5384) 2601f644e Roll external/googletest/ 9fce54804..61332bd7e (2 commits) (#5383) 714966003 opt: Add SwitchDescriptorSetPass (#5375) 6520d83ef linker: Add --use-highest-version option (#5376) bfc94f63a roll deps (#5382) b12fc2904 Roll external/googletest/ 7e33b6a1c..987e22561 (5 commits) (#5381) 89ca3aa57 SPV_QCOM_image_processing support (#5223) c55888661 Fix failing action when PR is already open. (#5380) 0f17d05c4 opt: add bitmask support for capability trimming (#5372) fddcc8ced Roll external/re2/ 9dc7ae7b5..6148386f0 (3 commits) (#5379) 7ddc65c72 Support 2 Intel extensions (#5357) 43b888649 roll deps (#5374) d6300ee92 Fix -Wunreachable-code-loop-increment warning (#5373) 8714d7fad enable StorageUniform16 (#5371) 8e3da01b4 Move token version/cap/ext checks from parsing to validation (#5370) 4788ff157 opt: add StorageUniformBufferBlock16 to trim pass (#5367) ebda56e35 opt: add StoragePushConstant16 to trim pass (#5366) 3af4244ae Roll external/googletest/ 46db91ef6..89b25572d (1 commit) (#5365) 60e684fe7 opt: fix StorageInputOutput16 trimming. (#5359) 13892fe86 Roll external/googletest/ 6f6ab4212..e7fd109b5 (2 commits) (#5356) 727f4346d docs: update references to `main` branch (#5363) e553b884c Prepare release for v2023.4.rc2 (#5362) 4a9881fe9 Use absolute path to depot_tools (#5360) 09b76c23e Update SPIRV-Headers; test some coop matrix enums (#5361) 1d14d84f2 opt: fix missing CreateTrimCapabilitiesPass definition (#5353) 47fff21d5 instrument: Reduce number of inst_bindless_stream_write_6 calls (#5327) 02cd71d41 roll deps (#5352) e68fe9be4 Add SPV_EXT_shader_atomic_float_add to allow lists (#5348) c6d0b0480 build: fix missing files in BUILD.gn (#5351) b5f600c08 Roll external/googletest/ 01e18376e..40412d851 (1 commit) (#5347) a0f1c8727 opt: Fix incorrect half float conversion (#5349) 35d8b05de opt: add capability trimming pass (not default). (#5278) ec90d2872 roll deps (#5345) d52c39c37 Do not crash when folding 16-bit OpFDiv (#5338) 17d9669d5 enumset: add iterator based constructor/insert (#5344) daad2295c Roll external/googletest/ cc366710b..d66ce5851 (2 commits) (#5337) 45f7e55af Bump word-wrap from 1.2.3 to 1.2.4 in /tools/sva (#5343) bf03d4092 opt: change Get* functions to return const& (#5331) 876ccc6cd Add /bigobj to test_opt for VS 2017 (#5336) c50bc49f5 Fix link flags for Clang-based MinGW cross compile (#5342) 2813da268 kokoro: rename glslang (#5339) 883417544 Set cmake_policy CMP0128 (#5341) 6c7e1acc5 NFC: fix missing algorithm include in enumset file (#5334) 61221e7d6 Add python3 requirement for the script (#5326) 4b6bd5a66 Prepare release v2023.4 (#5330) 9e0b780ff Create SECURITY.md (#5325) 7dd5f95d2 [spirv-opt] Handle OpFunction in GetPtr (#5316) 6add9ccf0 Add support for LiteralFloat type (#5323) 85a448213 NFC: makes the FeatureManager immutable for users (#5329) 29431859f NFC: replace EnumSet::ForEach with range-based-for (#5322) d6b9389f6 Roll external/spirv-headers/ d0006a393..f1ba373ef (2 commits) (#5320) 5b4fb072e enumset: fix bug in the new iterator class (#5321) 9ab811a12 NFC: fix missing comments on functions (#5318) 9266197c3 instrument: Cast gl_VertexIndex and InstanceIndex to uint (#5319) ee50fa7d8 Roll external/googletest/ 4a1a299b2..cc366710b (1 commit) (#5317) 3424b16c1 enumset: STL-ize container (#5311) 7ff331af6 source: Give better message if using new Source Language (#5314) abcd228d9 Update README to say Android NDK r25c is required (#5312) 0530a532f Validate GroupNonUniform instructions (#5296) 4594ffce9 Roll external/re2/ a57a1d646..e66463312 (1 commit) (#5313) 4be7d0e3c Use android ndk r25 (#5309) e751c7e7d Treat spir-v.xml as utf-8 (#5306) 0f3bea06e NFC: rewrite EnumSet to handle larger enums. (#5289) 870fd1e17 spirv-val: Label SPV_KHR_cooperative_matrix VUID (#5301) a1e8fff14 Roll external/re2/ 2d39b703d..1c1ffbe3c (1 commit) (#5304) 58459c2b1 roll deps (#5300) d3b0a522c Roll external/googletest/ 687c58994..251e72039 (1 commit) (#5299) ea5af2fb5 roll deps (#5297) f83f50d23 Roll external/googletest/ ec4fed932..8e32de89c (2 commits) (#5294) 7520bfa6b build: remove last references of c++11 (#5295) e090ce9c4 Update CMakeLists.txt (#5293) bfb40a240 fix ndk build standard to c++17 (#5290) 310a67020 Validate layouts for PhysicalStorageBuffer pointers (#5291) c640b1934 Update CMakeLists.txt (#5288) cfb99efd7 Roll external/googletest/ af39146b4..ec4fed932 (1 commit) (#5287) 04cdb2d34 SPV_KHR_cooperative_matrix (#5286) 16098b3c1 Have effcee add abseil subdirectory (#5281) daee1e7d3 instrument: Combine descriptor length and init state checking (#5274) a68ef7b2c cmake: Remove unused SPIRV-Headers variables (#5284) b12c0fe6f Roll external/googletest/ fb11778f4..af39146b4 (1 commit) (#5285) 54691dcd7 Migrate `exec_tools` back to `tools`. (#5280) a6b57f2f0 Roll external/googletest/ 9b12f749f..fb11778f4 (4 commits) (#5279) a63ac9f73 cmake: Use modern Python3 CMake support (#5277) 951980e5a Enable vector constant folding (#4913) (#5272) a720a6926 Roll external/googletest/ 18fa6a4db..9b12f749f (1 commit) (#5276) 6b9fc7933 Fold negation of integer vectors (#5269) 285f6cefa roll deps (#5273) d33bea584 instrument: Fix buffer address length calculations (#5257) b4f352e54 Expose preserve_interface in Optimizer::Register*Passes. (#5268) 40dde04ca Roll external/googletest/ 65cfeca1a..e9078161e (1 commit) (#5267) 6d0e3cf6a Roll external/googletest/ 334704df2..65cfeca1a (1 commit) (#5265) 9c66587d1 spirv-diff: Update test expectations (#5264) ae1843b67 spirv-diff: Leave undefined ids unpaired. (#5262) 93c13345e spirv-diff: Properly match SPV_KHR_ray_query types. (#5259) 9da026922 roll deps (#5263) 1d7dec3c5 Use windows 2019 to workaround bazel issue (#5261) 59b4febd8 Allow OpTypeBool in UniformConstant (#5237) 5ed21eb1e Add folding rule for OpTranspose (#5241) ec244c859 Increase tested Android API level (#5253) c7e436921 roll deps (#5243) 182fd9ebc Allow physical storage buffer pointer in IO (#5251) 226c3bbe6 Fix broken link in README (#5250) 9ed2ac257 Fix pairing of function parameters. (#5225) 8841d560c Add c++ version to .bazelrc (#5247) cf62673e4 Error for invalid location type (#5249) 673d8bfcb Checkout abseil in the smoketest (#5248) 06bbd7f53 Update deps in sva (#5246) 23cb9b96c spirv-val: Remove VUID from 1.3.251 spec (#5244) 1021ec302 Add Abseil dep to the README (#5242) 3e82fa067 Revert "Disable RE2 autoroll (#5234)" (#5239) e0936b646 Roll external/spirv-headers/ bdbfd019b..69155b22b (1 commit) (#5238) af27ece75 Check if const is zero before getting components. (#5217) 235800182 Add Abseil as a dep and update RE2 (#5236) f29e11dcb diff: Don't give up entry point matching too early. (#5224) 82b1a87b2 Add SPV_NV_bindless_texture to spirv optimizations (#5231) 60c546f3f Roll external/googletest/ bc860af08..bb2941fcc (1 commit) (#5220) dcfea36ab Have the macos bazel build us git-sync-deps (#5235) 44c9da6fe Remove const zero image operands (#5232) e357a36cc Disable RE2 autoroll (#5234) e7c6084fd Prepare release 2023.3 (#5222) 17a26b45f Improve an error message in the assembler (#5219) 7c39951f6 spirv-val: Label Interface Location/Component VUIDs (#5221) git-subtree-dir: third_party/SPIRV-Tools git-subtree-split: dd4b663e13c07fea4fbb3f70c1c91c86731099f7 Change-Id: I7efa466f44ce461394a16f123cd326553bd0a5a2
diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..79ad594 --- /dev/null +++ b/.bazelrc
@@ -0,0 +1,7 @@ +# Enable Bzlmod for every Bazel command +common --enable_bzlmod + +build --enable_platform_specific_config +build:linux --cxxopt=-std=c++17 +build:macos --cxxopt=-std=c++17 +build:windows --cxxopt=/std:c++17
diff --git a/.bazelversion b/.bazelversion index 0062ac9..a8907c0 100644 --- a/.bazelversion +++ b/.bazelversion
@@ -1 +1 @@ -5.0.0 +7.0.2
diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..dca857a --- /dev/null +++ b/.github/dependabot.yml
@@ -0,0 +1,25 @@ +# Copyright 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. + +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + groups: + github-actions: + patterns: + - "*" + open-pull-requests-limit: 3
diff --git a/.github/workflows/autoroll.yml b/.github/workflows/autoroll.yml index 4520309..ed33622 100644 --- a/.github/workflows/autoroll.yml +++ b/.github/workflows/autoroll.yml
@@ -16,14 +16,14 @@ runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 # Checkout the depot tools they are needed by roll_deps.sh - name: Checkout depot tools run: git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git - name: Update PATH - run: echo "./depot_tools" >> $GITHUB_PATH + run: echo "$(pwd)/depot_tools" >> $GITHUB_PATH - name: Download dependencies run: python3 utils/git-sync-deps @@ -47,6 +47,10 @@ if: steps.update_dependencies.outputs.changed == 'true' run: | git push --force --set-upstream origin roll_deps - gh pr create --label 'kokoro:run' --base main -f -r s-perron + # Create a PR. If it aready exists, the command fails, so ignore the return code. + gh pr create --base main -f || true + # Add the 'kokoro:run' label so that the kokoro tests will be run. + gh pr edit --add-label 'kokoro:run' + gh pr merge --auto --squash env: GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 88700c4..43c99d6 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml
@@ -13,30 +13,22 @@ timeout-minutes: 120 strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-latest, windows-2019] runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: '0' - name: Download dependencies run: python3 utils/git-sync-deps - name: Mount Bazel cache - uses: actions/cache@v3 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: ~/.bazel/cache key: bazel-cache-${{ runner.os }} - - name: Build All (Windows) - if: ${{matrix.os == 'windows-latest' }} - run: bazel --output_user_root=~/.bazel/cache build --cxxopt=/std:c++17 //... - - name: Test All (Windows) - if: ${{matrix.os == 'windows-latest' }} - run: bazel --output_user_root=~/.bazel/cache test --cxxopt=/std:c++17 //... - - name: Build All (Linux, MacOS) - if: ${{ matrix.os != 'windows-latest' }} - run: bazel --output_user_root=~/.bazel/cache build --cxxopt=-std=c++17 //... - - name: Test All (Linux, MacOS) - if: ${{ matrix.os != 'windows-latest' }} - run: bazel --output_user_root=~/.bazel/cache test --cxxopt=-std=c++17 //... + - name: Build All + run: bazel --output_user_root=~/.bazel/cache build //... + - name: Test All + run: bazel --output_user_root=~/.bazel/cache test //...
diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml new file mode 100644 index 0000000..feb64a7 --- /dev/null +++ b/.github/workflows/ios.yml
@@ -0,0 +1,30 @@ +name: iOS +permissions: + contents: read + +on: [push, pull_request, workflow_dispatch] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ macos-12, macos-13 ] + steps: + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: lukka/get-cmake@4931ab1fc1604964c055eb330edb3f6b26ba0cfa # v3.29.2 + - name: Download dependencies + run: python3 utils/git-sync-deps + # NOTE: The MacOS SDK ships universal binaries. CI should reflect this. + - name: Configure Universal Binary for iOS + run: | + cmake -S . -B build \ + -D CMAKE_BUILD_TYPE=Debug \ + -D CMAKE_SYSTEM_NAME=iOS \ + "-D CMAKE_OSX_ARCHITECTURES=arm64;x86_64" \ + -G Ninja + env: + # Linker warnings as errors + LDFLAGS: -Wl,-fatal_warnings + - run: cmake --build build + - run: cmake --install build --prefix /tmp
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ada9431..583c8f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml
@@ -6,13 +6,14 @@ push: tags: - 'v[0-9]+.[0-9]+' + - 'vulkan-sdk-[0-9]+.[0-9]+.[0-9]+.[0-9]+' - '!v[0-9]+.[0-9]+.rc*' jobs: prepare-release-job: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - 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 new file mode 100644 index 0000000..adcfa76 --- /dev/null +++ b/.github/workflows/scorecard.yml
@@ -0,0 +1,53 @@ +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '36 17 * * 5' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + security-events: write # to upload the results to code-scanning dashboard + id-token: write # to publish results and get a badge + + steps: + - name: "Checkout code" + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + # To enable Branch-Protection uncomment the `repo_token` line below + # To create the Fine-grained PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + publish_results: true # allows the repo to include the Scorecard badge + + # 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@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + with: + sarif_file: results.sarif
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 62c9af3..6807b3d 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml
@@ -9,7 +9,7 @@ runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: '0' - name: Build web
diff --git a/.gitignore b/.gitignore index ec709ba..e85cea9 100644 --- a/.gitignore +++ b/.gitignore
@@ -4,6 +4,7 @@ compile_commands.json /build*/ /buildtools/ +/external/abseil_cpp/ /external/googletest /external/SPIRV-Headers /external/spirv-headers @@ -22,6 +23,7 @@ bazel-spirv-tools bazel-SPIRV-Tools bazel-testlogs +MODULE.bazel.lock # Vim [._]*.s[a-w][a-z]
diff --git a/Android.mk b/Android.mk index a4e7615..e3e30bb 100644 --- a/Android.mk +++ b/Android.mk
@@ -136,6 +136,7 @@ source/opt/instrument_pass.cpp \ source/opt/interface_var_sroa.cpp \ source/opt/interp_fixup_pass.cpp \ + source/opt/invocation_interlock_placement_pass.cpp \ source/opt/ir_context.cpp \ source/opt/ir_loader.cpp \ source/opt/licm_pass.cpp \ @@ -156,6 +157,7 @@ source/opt/loop_utils.cpp \ source/opt/mem_pass.cpp \ source/opt/merge_return_pass.cpp \ + source/opt/modify_maximal_reconvergence.cpp \ source/opt/module.cpp \ source/opt/optimizer.cpp \ source/opt/pass.cpp \ @@ -182,6 +184,8 @@ source/opt/strip_debug_info_pass.cpp \ source/opt/strip_nonsemantic_info_pass.cpp \ source/opt/struct_cfg_analysis.cpp \ + source/opt/switch_descriptorset_pass.cpp \ + source/opt/trim_capabilities_pass.cpp \ source/opt/type_manager.cpp \ source/opt/types.cpp \ source/opt/unify_const_pass.cpp \ @@ -285,6 +289,7 @@ $(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,"")) define gen_spvtools_enum_string_mapping $(call generate-file-dir,$(1)/extension_enum.inc.inc) @@ -340,7 +345,7 @@ $(SPVTOOLS_OUT_PATH) LOCAL_EXPORT_C_INCLUDES := \ $(LOCAL_PATH)/include -LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror LOCAL_SRC_FILES:= $(SPVTOOLS_SRC_FILES) include $(BUILD_STATIC_LIBRARY) @@ -351,7 +356,7 @@ $(LOCAL_PATH)/source \ $(SPVHEADERS_LOCAL_PATH)/include \ $(SPVTOOLS_OUT_PATH) -LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror LOCAL_STATIC_LIBRARIES:=SPIRV-Tools LOCAL_SRC_FILES:= $(SPVTOOLS_OPT_SRC_FILES) include $(BUILD_STATIC_LIBRARY)
diff --git a/BUILD.bazel b/BUILD.bazel index ae7f35c..48a688e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel
@@ -58,6 +58,8 @@ generate_vendor_tables(extension = "nonsemantic.clspvreflection") +generate_vendor_tables(extension = "nonsemantic.vkspreflection") + generate_vendor_tables( extension = "opencl.debuginfo.100", operand_kind_prefix = "CLDEBUG100_", @@ -94,7 +96,7 @@ outs = ["generators.inc"], cmd = "$(location :generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", cmd_bat = "$(location :generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", - exec_tools = [":generate_registry_tables"], + tools = [":generate_registry_tables"], ) py_binary( @@ -108,10 +110,8 @@ outs = ["build-version.inc"], cmd = "SOURCE_DATE_EPOCH=0 $(location :update_build_version) $(location CHANGES) $(location build-version.inc)", cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location :update_build_version) $(location CHANGES) $(location build-version.inc)", - # This is explicitly tools and not exec_tools because we run it locally (on the host platform) instead of - # (potentially remotely) on the execution platform. - tools = [":update_build_version"], local = True, + tools = [":update_build_version"], ) # Libraries @@ -146,15 +146,16 @@ ":gen_extinst_lang_headers_OpenCLDebugInfo100", ":gen_glsl_tables_unified1", ":gen_opencl_tables_unified1", - ":generators_inc", ":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 = [ "include/spirv-tools/libspirv.h", @@ -307,17 +308,17 @@ cc_binary( name = "spirv-objdump", srcs = [ - "tools/objdump/objdump.cpp", "tools/objdump/extract_source.cpp", "tools/objdump/extract_source.h", + "tools/objdump/objdump.cpp", ], copts = COMMON_COPTS, visibility = ["//visibility:public"], deps = [ - ":tools_io", - ":tools_util", ":spirv_tools_internal", ":spirv_tools_opt_internal", + ":tools_io", + ":tools_util", "@spirv_headers//:spirv_cpp_headers", ], ) @@ -428,7 +429,7 @@ copts = TEST_COPTS, deps = [ ":spirv_tools_internal", - "@com_google_googletest//:gtest", + "@googletest//:gtest", ], ) @@ -439,23 +440,25 @@ name = "base_{testcase}_test".format(testcase = f[len("test/"):-len("_test.cpp")]), size = "small", srcs = [f], - copts = TEST_COPTS + ['-DTESTING'], + copts = TEST_COPTS + ["-DTESTING"], linkstatic = 1, target_compatible_with = { "test/timer_test.cpp": incompatible_with(["@bazel_tools//src/conditions:windows"]), }.get(f, []), deps = [ + "tools_util", ":spirv_tools_internal", ":test_lib", - "tools_util", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob( - ["test/*_test.cpp", "test/tools/*_test.cpp"], + [ + "test/*_test.cpp", + "test/tools/*_test.cpp", + ], exclude = [ "test/cpp_interface_test.cpp", - "test/log_test.cpp", "test/pch_test.cpp", ], )] @@ -467,8 +470,8 @@ linkstatic = 1, deps = [ ":spirv_tools_opt_internal", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", "@spirv_headers//:spirv_cpp11_headers", ], ) @@ -481,21 +484,8 @@ linkstatic = 1, deps = [ ":spirv_tools_internal", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "base_log_test", - size = "small", - srcs = ["test/log_test.cpp"], - copts = TEST_COPTS, - linkstatic = 1, - deps = [ - ":spirv_tools_opt_internal", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -521,8 +511,8 @@ linkstatic = 1, deps = [ ":link_test_lib", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob( ["test/link/*_test.cpp"], @@ -538,8 +528,8 @@ ":spirv_tools", ":spirv_tools_lint_internal", ":spirv_tools_opt_internal", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob( ["test/lint/*_test.cpp"], @@ -563,7 +553,7 @@ ":spirv_tools_internal", ":spirv_tools_opt_internal", "@com_google_effcee//:effcee", - "@com_google_googletest//:gtest", + "@googletest//:gtest", ], ) @@ -579,8 +569,8 @@ ":spirv_tools_opt_internal", ":test_lib", "@com_google_effcee//:effcee", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob(["test/opt/*_test.cpp"])] @@ -593,8 +583,8 @@ deps = [ ":opt_test_lib", ":spirv_tools_opt_internal", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob( ["test/opt/dominator_tree/*.cpp"], @@ -612,8 +602,8 @@ ":spirv_tools", ":spirv_tools_opt_internal", "@com_google_effcee//:effcee", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob( ["test/opt/loop_optimizations/*.cpp"], @@ -634,7 +624,7 @@ ":spirv_tools_reduce", ":test_lib", ":tools_io", - "@com_google_googletest//:gtest", + "@googletest//:gtest", ], ) @@ -649,7 +639,7 @@ ":spirv_tools_internal", ":spirv_tools_opt_internal", ":spirv_tools_reduce", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest_main", ], ) for f in glob(["test/reduce/*_test.cpp"])] @@ -661,8 +651,8 @@ linkstatic = 1, deps = [ ":spirv_tools_internal", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob(["test/util/*_test.cpp"])] @@ -693,8 +683,8 @@ ":spirv_tools_internal", ":test_lib", ":val_test_lib", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) for f in glob( ["test/val/val_*_test.cpp"], @@ -715,8 +705,8 @@ ":spirv_tools_internal", ":test_lib", ":val_test_lib", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -732,7 +722,7 @@ deps = [ ":test_lib", ":val_test_lib", - "@com_google_googletest//:gtest", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], )
diff --git a/BUILD.gn b/BUILD.gn index 44954b2..5d0ab86 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -328,6 +328,10 @@ "...nil...", ], [ + "nonsemantic.vkspreflection", + "...nil...", + ], + [ "nonsemantic.shader.debuginfo.100", "SHDEBUG100_", ], @@ -693,6 +697,8 @@ "source/opt/interface_var_sroa.h", "source/opt/interp_fixup_pass.cpp", "source/opt/interp_fixup_pass.h", + "source/opt/invocation_interlock_placement_pass.cpp", + "source/opt/invocation_interlock_placement_pass.h", "source/opt/ir_builder.h", "source/opt/ir_context.cpp", "source/opt/ir_context.h", @@ -735,6 +741,8 @@ "source/opt/mem_pass.h", "source/opt/merge_return_pass.cpp", "source/opt/merge_return_pass.h", + "source/opt/modify_maximal_reconvergence.cpp", + "source/opt/modify_maximal_reconvergence.h", "source/opt/module.cpp", "source/opt/module.h", "source/opt/null_pass.h", @@ -789,7 +797,11 @@ "source/opt/strip_nonsemantic_info_pass.h", "source/opt/struct_cfg_analysis.cpp", "source/opt/struct_cfg_analysis.h", + "source/opt/switch_descriptorset_pass.cpp", + "source/opt/switch_descriptorset_pass.h", "source/opt/tree_iterator.h", + "source/opt/trim_capabilities_pass.cpp", + "source/opt/trim_capabilities_pass.h", "source/opt/type_manager.cpp", "source/opt/type_manager.h", "source/opt/types.cpp",
diff --git a/CHANGES b/CHANGES index dbe31a0..102703a 100644 --- a/CHANGES +++ b/CHANGES
@@ -1,5 +1,150 @@ Revision history for SPIRV-Tools +v2024.2 2024-04-22 + - General + - Add SPIRV_TOOLS_EXPORT to public C++ API (#5591) + - Use bazel 7 and bzlmod (#5601) + - Optimizer + - opt: add GroupNonUniformPartitionedNV capability to trim pass (#5648) + - Fix rebuilding types with circular references. (#5637) + - Add AliasedPointer decoration (#5635) + - add support for vulkan-shader-profiler external passes (#5512) + - Validator + - A fix to support of SPV_QCOM_image_processing2 (#5646) + - spirv-val: Add Vulkan check for Rect Dim in OpTypeImage (#5644) + - Validate duplicate decorations and execution modes (#5641) + - Validator: Support SPV_NV_raw_access_chains (#5568) + +v2024.1 2024-03-06 + - General + - Add tooling support for SPV_KHR_maximal_reconvergence (#5542) + - Add support for SPV_KHR_float_controls2 (#5543) + - SPV_KHR_quad_control (#5547) + - Fold 64-bit int operations (#5561) + - update image enum tests to remove Kernel capability (#5562) + - Support operand kind for SPV_INTEL_maximum_registers (#5580) + - SPV_NV_shader_atomic_fp16_vector (#5581) + - Support for SPV_QCOM_image_processing2 (#5582) + - Fix access chain struct checks (#5592) + - Optimizer + - opt: add Int16 and Float16 to capability trim pass (#5519) + - Add preserver-interface option to spirv-opt (#5524) + - spirv-opt: Fix OpCompositeExtract relaxation with struct operands (#5536) + - opt: Add VulkanMemoryModelDeviceScope to trim (#5544) + - opt: Add TrimCapabilities pass to spirv-opt tool (#5545) + - Add modify-maximal-reconvergence to spirv-opt help (#5546) + - opt: add SPV_EXT_mesh_shader to opt allowlist (#5551) + - opt: Add OpEntryPoint to DescriptorScalarReplacement pass (#5553) + - opt: prevent meld to merge block with MaximalReconvergence (#5557) + - [OPT] Use new instruction folder for for all opcodes in spec consti folding (#5569) + - [OPT] Identify arrays with unknown length in copy prop arrays (#5570) + - [OPT] Add removed unused interface var pass to legalization passes (#5579) + - Validator + - spirv-val: Re-enable OpControlBarrier VU (#5527) + - spirv-val: Add Mesh Primitive Built-In validaiton (#5529) + - spirv-val: Validate PhysicalStorageBuffer Stage Interface (#5539) + - spirv-val: Multiple interface var with same SC (#5528) + - spirv-val: Revert Validate PhysicalStorageBuffer Stage Interface (#5575) + - spirv-val: Make Constant evaluation consistent (#5587) + +v2023.6 2023-12-18 + - General + - update_build_version.py produce deterministic header. (#5426) + - Support missing git in update_build_version.py (#5473) + - Optimizer + - Add ComputeDerivativeGroup*NV capabilities to trim capabilities pass. (#5430) + - Do not crash when tryingto fold unsupported spec constant (#5496) + - instrument: Fix handling of gl_InvocationID (#5493) + - Fix nullptr argument in MarkInsertChain (#5465) + - opt: support 64-bit OpAccessChain index in FixStorageClass (#5446) + - opt: add StorageImageReadWithoutFormat to cap trim (#5475) + - opt: add PhysicalStorageBufferAddresses to trim (#5476) + - Fix array size calculation (#5463 + - Validator + - spirv-val: Loosen restriction on base type of DebugTypePointer and DebugTypeQualifier (#5479) + - spirv-val: Add WorkgroupMemoryExplicitLayoutKHR check for Block (#5461) + +v2023.5 2023-10-15 + - General + - Support 2 Intel extensions (#5357) + - SPV_QCOM_image_processing support (#5223) + - Optimizer + - opt: fix StorageInputOutput16 trimming. (#5359) + - opt: add StoragePushConstant16 to trim pass (#5366) + - opt: enable StorageUniform16 (#5371) + - opt: add bitmask support for capability trimming (#5372) + - opt: Add SwitchDescriptorSetPass (#5375) + - opt: add FragmentShader*InterlockEXT to capability trim pass (#5390) + - opt: add Int64 capability to trim pass (#5398) + - opt: add Float64 capability to trim pass (#5428) + - opt: add raytracing/rayquery to trim pass (#5397) + - opt: add ImageMSArray capability to trim pass. (#5395) + - Add SPV_KHR_physical_storage_buffer to allowlists (#5402) + - Add SPV_EXT_fragment_shader_interlock to allow lists (#5393) + - Make sure that fragment shader interlock instructions are not removed by DCE (#5400) + - instrument: Use Import linkage for instrumentation functions (#5355) + - Add a new legalization pass to dedupe invocation interlock instructions (#5409) + - instrument: Ensure linking works even of nothing is changed (#5419) + - Validator + - Move token version/cap/ext checks from parsing to validation (#5370) + - val: re-add ImageMSArray validation (#5394) + - Linker + - linker: Add --use-highest-version option + +v2023.4 2023-07-17 + - General + - Set cmake_policy CMP0128 (#5341) + - Add python3 requirement for the script (#5326) + - Add support for LiteralFloat type (#5323) + - SPV_KHR_cooperative_matrix (#5286) + - Allow OpTypeBool in UniformConstant (#5237) + - Allow physical storage buffer pointer in IO (#5251) + - Remove const zero image operands (#5232) + - Optimizer + - Enable vector constant folding (#4913) (#5272) + - Fold negation of integer vectors (#5269) + - Add folding rule for OpTranspose (#5241) + - Add SPV_NV_bindless_texture to spirv optimizations (#5231) + - Fix incorrect half float conversion (#5349) + - Add SPV_EXT_shader_atomic_float_add to allow lists (#5348) + - Instrument + - instrument: Cast gl_VertexIndex and InstanceIndex to uint (#5319) + - instrument: Fix buffer address length calculations (#5257) + - instrument: Reduce number of inst_bindless_stream_write_6 calls (#5327) + - Validator + - Validate GroupNonUniform instructions (#5296) + - spirv-val: Label SPV_KHR_cooperative_matrix VUID (#5301) + - Validate layouts for PhysicalStorageBuffer pointers (#5291) + - spirv-val: Remove VUID from 1.3.251 spec (#5244) + - Diff + - spirv-diff: Update test expectations (#5264) + - spirv-diff: Leave undefined ids unpaired. (#5262) + - spirv-diff: Properly match SPV_KHR_ray_query types. (#5259) + - diff: Don't give up entry point matching too early. (#5224) + +v2023.3 2023-05-15 + - General + - Update spirv_headers to include SPV_KHR_ray_tracing_position_fetch (#5205) + - spirv-tools: Add support for QNX (#5211) + - build: set std=c++17 for BUILD.gn (#5162) + - Optimizer + - Run ADCE when the printf extension is used. (#5215) + - Don't convert struct members to half (#5201) + - Apply scalar replacement on vars with Pointer decorations (#5208) + - opt: Fix null deref in OpMatrixTimesVector and OpVectorTimesMatrix (#5199) + - instrument: Add set and binding to bindless error records (#5204) + - instrument: Change descriptor state storage format (#5178) + - Fix LICMPass (#5087) + - Add Vulkan memory model to allow lists (#5173) + - Do not remove control barrier after spv1.3 (#5174) + - Validator + - spirv-val: Label Interface Location/Component VUIDs (#5221) + - Add support for SPV_EXT_shader_tile_image (#5188) + - Fix vector OpConstantComposite type validation (#5191) + - spirv-val: Label new Vulkan VUID 07951 (#5154) + - Fuzz + - Do not define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE if it is already defined. (#5200) + v2023.2 2023-03-10 - General - build: move from c++11 to c++17 (#4983)
diff --git a/CMakeLists.txt b/CMakeLists.txt index 71cdc00..0ba173f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -16,6 +16,16 @@ project(spirv-tools) +# Avoid a bug in CMake 3.22.1. By default it will set -std=c++11 for +# targets in test/*, when those tests need -std=c++17. +# https://github.com/KhronosGroup/SPIRV-Tools/issues/5340 +# The bug is fixed in CMake 3.22.2 +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.22.1") + if (${CMAKE_VERSION} VERSION_LESS "3.22.2") + cmake_policy(SET CMP0128 NEW) + endif() +endif() + set_property(GLOBAL PROPERTY USE_FOLDERS ON) enable_testing() @@ -53,6 +63,8 @@ add_definitions(-DSPIRV_IOS) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "tvOS") add_definitions(-DSPIRV_TVOS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "visionOS") + add_definitions(-DSPIRV_VISIONOS) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") add_definitions(-DSPIRV_ANDROID) set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) @@ -231,7 +243,7 @@ # For MinGW cross compile, statically link to the C++ runtime. # But it still depends on MSVCRT.dll. if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - if (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + if (NOT MSVC) set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -static -static-libgcc -static-libstdc++) endif() @@ -250,7 +262,7 @@ endif() # Tests require Python3 -find_host_package(PythonInterp 3 REQUIRED) +find_host_package(Python3 REQUIRED) # Check for symbol exports on Linux. # At the moment, this check will fail on the OSX build machines for the Android NDK. @@ -259,7 +271,7 @@ macro(spvtools_check_symbol_exports TARGET) if (NOT "${SPIRV_SKIP_TESTS}") add_test(NAME spirv-tools-symbol-exports-${TARGET} - COMMAND ${PYTHON_EXECUTABLE} + COMMAND Python3::Interpreter ${spirv-tools_SOURCE_DIR}/utils/check_symbol_exports.py "$<TARGET_FILE:${TARGET}>") endif() endmacro() @@ -292,15 +304,23 @@ endmacro() endif() -# Defaults to OFF if the user didn't set it. -option(SPIRV_SKIP_EXECUTABLES - "Skip building the executable and tests along with the library" - ${SPIRV_SKIP_EXECUTABLES}) -option(SPIRV_SKIP_TESTS - "Skip building tests along with the library" ${SPIRV_SKIP_TESTS}) -if ("${SPIRV_SKIP_EXECUTABLES}") +# Currently iOS and Android are very similar. +# They both have their own packaging (APP/APK). +# Which makes regular executables/testing problematic. +# +# Currently the only deliverables for these platforms are +# libraries (either STATIC or SHARED). +# +# Furthermore testing is equally problematic. +if (IOS OR ANDROID) + set(SPIRV_SKIP_EXECUTABLES ON) +endif() + +option(SPIRV_SKIP_EXECUTABLES "Skip building the executable and tests along with the library") +if (SPIRV_SKIP_EXECUTABLES) set(SPIRV_SKIP_TESTS ON) endif() +option(SPIRV_SKIP_TESTS "Skip building tests along with the library") # Defaults to ON. The checks can be time consuming. # Turn off if they take too long. @@ -358,7 +378,7 @@ if (NOT "${SPIRV_SKIP_TESTS}") add_test(NAME spirv-tools-copyrights - COMMAND ${PYTHON_EXECUTABLE} utils/check_copyright.py + COMMAND Python3::Interpreter utils/check_copyright.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() @@ -367,7 +387,8 @@ # Build pkg-config file # Use a first-class target so it's regenerated when relevant files are updated. -add_custom_target(spirv-tools-pkg-config ALL +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc COMMAND ${CMAKE_COMMAND} -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in @@ -377,8 +398,9 @@ -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} -DSPIRV_LIBRARIES=${SPIRV_LIBRARIES} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake - DEPENDS "CHANGES" "cmake/SPIRV-Tools.pc.in" "cmake/write_pkg_config.cmake") -add_custom_target(spirv-tools-shared-pkg-config ALL + DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake") +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc COMMAND ${CMAKE_COMMAND} -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in @@ -388,7 +410,10 @@ -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} -DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake - DEPENDS "CHANGES" "cmake/SPIRV-Tools-shared.pc.in" "cmake/write_pkg_config.cmake") + DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake") +add_custom_target(spirv-tools-pkg-config + ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc) # Install pkg-config file if (ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 893998e..11fb4e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md
@@ -3,7 +3,7 @@ ## For users: Reporting bugs and requesting features We organize known future work in GitHub projects. See -[Tracking SPIRV-Tools work with GitHub projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/docs/projects.md) +[Tracking SPIRV-Tools work with GitHub projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/main/docs/projects.md) for more. To report a new bug or request a new feature, please file a GitHub issue. Please @@ -46,7 +46,7 @@ approved it, but you must do it before we can put your code into our codebase. See -[README.md](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/README.md) +[README.md](https://github.com/KhronosGroup/SPIRV-Tools/blob/main/README.md) for instruction on how to get, build, and test the source. Once you have made your changes: @@ -59,7 +59,7 @@ * If your patch completely fixes bug 1234, the commit message should say `Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1234` When you do this, the issue will be closed automatically when the commit goes into - master. Also, this helps us update the [CHANGES](CHANGES) file. + main. Also, this helps us update the [CHANGES](CHANGES) file. * Watch the continuous builds to make sure they pass. * Request a code review. @@ -107,7 +107,7 @@ ## For maintainers: Merging a PR -We intend to maintain a linear history on the GitHub master branch, and the +We intend to maintain a linear history on the GitHub main branch, and the build and its tests should pass at each commit in that history. A linear always-working history is easier to understand and to bisect in case we want to find which commit introduced a bug. The
diff --git a/DEPS b/DEPS index 9b6039e..8413d1b 100644 --- a/DEPS +++ b/DEPS
@@ -3,18 +3,23 @@ vars = { 'github': 'https://github.com', - 'effcee_revision': '66edefd2bb641de8a2f46b476de21f227fc03a28', + 'abseil_revision': '79ca5d7aad63973c83a4962a66ab07cd623131ea', - 'googletest_revision': 'bc860af08783b8113005ca7697da5f5d49a8056f', + 'effcee_revision': '19b4aa87af25cb4ee779a071409732f34bfc305c', + + 'googletest_revision': '5a37b517ad4ab6738556f0284c256cae1466c5b4', # Use protobufs before they gained the dependency on abseil 'protobuf_revision': 'v21.12', - 're2_revision': 'c9cba76063cf4235c1a15dd14a24a4ef8d623761', - 'spirv_headers_revision': '268a061764ee69f09a477a695bf6a11ffe311b8d', + 're2_revision': '917047f3606d3ba9e2de0d383c3cd80c94ed732c', + 'spirv_headers_revision': '4f7b471f1a66b6d06462cd4ba57628cc0cd087d7', } deps = { + 'external/abseil_cpp': + Var('github') + '/abseil/abseil-cpp.git@' + Var('abseil_revision'), + 'external/effcee': Var('github') + '/google/effcee.git@' + Var('effcee_revision'),
diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..c36fe45 --- /dev/null +++ b/MODULE.bazel
@@ -0,0 +1,7 @@ +bazel_dep(name = "bazel_skylib", version = "1.5.0") + +bazel_dep(name = "googletest", dev_dependency = True) +local_path_override( + module_name = "googletest", + path = "external/googletest", +)
diff --git a/README.md b/README.md index 92e4d3c..7db5bd4 100644 --- a/README.md +++ b/README.md
@@ -1,4 +1,5 @@ # SPIR-V Tools +[](https://securityscorecards.dev/viewer/?uri=github.com/KhronosGroup/SPIRV-Tools) NEWS 2023-01-11: Development occurs on the `main` branch. @@ -23,6 +24,13 @@ ## Downloads +The official releases for SPIRV-Tools can be found on LunarG's +[SDK download page](https://vulkan.lunarg.com/sdk/home). + +For convenience, here are also links to the latest builds (HEAD). +Those are untested automated builds. Those are not official releases, nor +are guaranteed to work. Official releases builds are in the Vulkan SDK. + <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) @@ -48,17 +56,14 @@ ## Releases -Some versions of SPIRV-Tools are tagged as stable releases (see -[tags](https://github.com/KhronosGroup/SPIRV-Tools/tags) on github). -These versions undergo extra testing. -Releases are not directly related to releases (or versions) of -[SPIRV-Headers][spirv-headers]. -Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed -in the [DEPS](DEPS) file. -The release generally uses the most recent compatible version of SPIRV-Headers -available at the time of release. -No version of SPIRV-Headers other than the one listed in the DEPS file is -guaranteed to work with the SPIRV-Tools release. +The official releases for SPIRV-Tools can be found on LunarG's +[SDK download page](https://vulkan.lunarg.com/sdk/home). + +You can find either the prebuilt, and QA tested binaries, or download the +SDK Config, which lists the commits to use to build the release from scratch. + +GitHub releases are deprecated, and we will not publish new releases until +further notice. ## Supported features @@ -292,16 +297,18 @@ git clone https://github.com/google/googletest.git spirv-tools/external/googletest git clone https://github.com/google/effcee.git spirv-tools/external/effcee git clone https://github.com/google/re2.git spirv-tools/external/re2 + git clone https://github.com/abseil/abseil-cpp.git spirv-tools/external/abseil_cpp #### Dependency on Effcee Some tests depend on the [Effcee][effcee] library for stateful matching. -Effcee itself depends on [RE2][re2]. +Effcee itself depends on [RE2][re2], and RE2 depends on [Abseil][abseil-cpp]. * If SPIRV-Tools is configured as part of a larger project that already uses Effcee, then that project should include Effcee before SPIRV-Tools. -* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` - and RE2 sources to appear in `external/re2`. +* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`, + RE2 sources to appear in `external/re2`, and Abseil sources to appear in + `external/abseil_cpp`. ### Source code organization @@ -313,6 +320,9 @@ * `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already configured by an enclosing project. (The Effcee project already requires RE2.) +* `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.) * `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 @@ -381,15 +391,8 @@ ### Build using Bazel You can also use [Bazel](https://bazel.build/) to build the project. -On linux: ```sh -cd <spirv-dir> -bazel build --cxxopt=-std=c++17 :all -``` - -On windows: -```sh -bazel build --cxxopt=/std:c++17 :all +bazel build :all ``` ### Build a node.js package using Emscripten @@ -427,7 +430,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 5.0.0 on your machine. Other versions may +you need to install Bazel Version 7.0.2 on your machine. Other versions may also work, but are not verified. - [Emscripten SDK](https://emscripten.org) (optional): if building the WebAssembly module. @@ -480,12 +483,12 @@ ### Android ndk-build SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and -`libSPIRV-Tools-opt.a` for Android: +`libSPIRV-Tools-opt.a` for Android. Using the Android NDK r25c or later: ``` cd <spirv-dir> -export ANDROID_NDK=/path/to/your/ndk +export ANDROID_NDK=/path/to/your/ndk # NDK r25c or later mkdir build && cd build mkdir libs @@ -798,6 +801,7 @@ [googletest-issue-610]: https://github.com/google/googletest/issues/610 [effcee]: https://github.com/google/effcee [re2]: https://github.com/google/re2 +[abseil-cpp]: https://github.com/abseil/abseil-cpp [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/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..99c5f44 --- /dev/null +++ b/SECURITY.md
@@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +Security updates are applied only to the latest release. + +## Reporting a Vulnerability + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/KhronosGroup/SPIRV-Tools/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
diff --git a/WORKSPACE b/WORKSPACE index 5abfc98..6e78059 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -4,11 +4,6 @@ ) local_repository( - name = "com_google_googletest", - path = "external/googletest", -) - -local_repository( name = "com_googlesource_code_re2", path = "external/re2", ) @@ -17,3 +12,8 @@ name = "com_google_effcee", path = "external/effcee", ) + +local_repository( + name = "abseil-cpp", + path = "external/abseil_cpp", +)
diff --git a/android_test/Android.mk b/android_test/Android.mk index dbaf93b..b9a0014 100644 --- a/android_test/Android.mk +++ b/android_test/Android.mk
@@ -5,7 +5,7 @@ LOCAL_SRC_FILES:=test.cpp LOCAL_MODULE:=spirvtools_test LOCAL_LDLIBS:=-landroid -LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror LOCAL_STATIC_LIBRARIES=SPIRV-Tools SPIRV-Tools-opt include $(BUILD_SHARED_LIBRARY)
diff --git a/android_test/jni/Application.mk b/android_test/jni/Application.mk index 4e66465..47c0acf 100644 --- a/android_test/jni/Application.mk +++ b/android_test/jni/Application.mk
@@ -1,5 +1,5 @@ APP_ABI := all APP_BUILD_SCRIPT := Android.mk APP_STL := c++_static -APP_PLATFORM := android-9 +APP_PLATFORM := android-24 NDK_TOOLCHAIN_VERSION := 4.9
diff --git a/build_defs.bzl b/build_defs.bzl index 4d6f15c..76bf3e7 100644 --- a/build_defs.bzl +++ b/build_defs.bzl
@@ -88,7 +88,7 @@ outs = outs.values(), cmd = cmd, cmd_bat = cmd, - exec_tools = [":generate_grammar_tables"], + tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -123,7 +123,7 @@ outs = outs.values(), cmd = cmd, cmd_bat = cmd, - exec_tools = [":generate_grammar_tables"], + tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -151,7 +151,7 @@ outs = outs.values(), cmd = cmd, cmd_bat = cmd, - exec_tools = [":generate_grammar_tables"], + tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -179,7 +179,7 @@ outs = outs.values(), cmd = cmd, cmd_bat = cmd, - exec_tools = [":generate_grammar_tables"], + tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -207,7 +207,7 @@ outs = outs.values(), cmd = cmd, cmd_bat = cmd, - exec_tools = [":generate_grammar_tables"], + tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -229,6 +229,6 @@ outs = outs.values(), cmd = cmd, cmd_bat = cmd, - exec_tools = [":generate_language_headers"], + tools = [":generate_language_headers"], visibility = ["//visibility:private"], )
diff --git a/docs/downloads.md b/docs/downloads.md index 168937a..0454b9e 100644 --- a/docs/downloads.md +++ b/docs/downloads.md
@@ -1,8 +1,24 @@ # Downloads -## Latest builds +## Vulkan SDK -Download the latest builds of the [master](https://github.com/KhronosGroup/SPIRV-Tools/tree/master) branch. +The official releases for SPIRV-Tools can be found on LunarG's +[SDK download page](https://vulkan.lunarg.com/sdk/home). +The Vulkan SDK is updated approximately every six weeks. + +## Android NDK + +SPIRV-Tools host executables, and library sources are published as +part of the [Android NDK](https://developer.android.com/ndk/downloads). + +## Automated builds + +For convenience, here are also links to the latest builds (HEAD). +Those are untested automated builds. Those are not official releases, nor +are guaranteed to work. Official releases builds are in the Android NDK or +Vulkan SDK. + +Download the latest builds of the [main](https://github.com/KhronosGroup/SPIRV-Tools/tree/main) branch. ### Release build | Windows | Linux | MacOS | @@ -15,14 +31,3 @@ | --- | --- | --- | | [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) | | | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | | - - -## Vulkan SDK - -SPIRV-Tools is published as part of the [LunarG Vulkan SDK](https://www.lunarg.com/vulkan-sdk/). -The Vulkan SDK is updated approximately every six weeks. - -## Android NDK - -SPIRV-Tools host executables, and library sources are published as -part of the [Android NDK](https://developer.android.com/ndk/downloads).
diff --git a/docs/projects.md b/docs/projects.md index 8f7f0bc..cc88cb3 100644 --- a/docs/projects.md +++ b/docs/projects.md
@@ -34,7 +34,7 @@ ones. * They determine if the work for a card has been completed. * Normally they are the person (or persons) who can approve and merge a pull - request into the `master` branch. + request into the `main` branch. Our projects organize cards into the following columns: * `Ideas`: Work which could be done, captured either as Cards or Notes. @@ -51,7 +51,7 @@ claimed by someone. * `Done`: Issues which have been resolved, by completing their work. * The changes have been applied to the repository, typically by being pushed - into the `master` branch. + into the `main` branch. * Other kinds of work could update repository settings, for example. * `Rejected ideas`: Work which has been considered, but which we don't want implemented.
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 6ee37d9..5d8a3da 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt
@@ -41,8 +41,6 @@ # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find # headers to include. if (NOT DEFINED SPIRV-Headers_SOURCE_DIR) - set(SPIRV_HEADERS_SKIP_INSTALL ON) - set(SPIRV_HEADERS_SKIP_EXAMPLES ON) add_subdirectory(${SPIRV_HEADER_DIR}) endif() else() @@ -93,10 +91,22 @@ # Find Effcee and RE2, for testing. + # RE2 depends on Abseil. We set absl_SOURCE_DIR if it is not already set, so + # that effcee can find abseil. + if(NOT TARGET absl::base) + if (NOT absl_SOURCE_DIR) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/abseil_cpp) + set(absl_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/abseil_cpp" CACHE STRING "Abseil source dir" ) + endif() + endif() + endif() + # First find RE2, since Effcee depends on it. # If already configured, then use that. Otherwise, prefer to find it under 're2' # in this directory. if (NOT TARGET re2) + + # If we are configuring RE2, then turn off its testing. It takes a long time and # does not add much value for us. If an enclosing project configured RE2, then it # has already chosen whether to enable RE2 testing.
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp index 448cf8a..0a6e630 100644 --- a/include/spirv-tools/instrument.hpp +++ b/include/spirv-tools/instrument.hpp
@@ -73,213 +73,14 @@ // which generated the validation error. static const int kInstCommonOutInstructionIdx = 2; -// This is the stage which generated the validation error. This word is used -// to determine the contents of the next two words in the record. -// 0:Vert, 1:TessCtrl, 2:TessEval, 3:Geom, 4:Frag, 5:Compute -static const int kInstCommonOutStageIdx = 3; -static const int kInstCommonOutCnt = 4; - -// Stage-specific Stream Record Offsets -// -// Each stage will contain different values in the next set of words of the -// record used to identify which instantiation of the shader generated the -// validation error. -// -// Vertex Shader Output Record Offsets -static const int kInstVertOutVertexIndex = kInstCommonOutCnt; -static const int kInstVertOutInstanceIndex = kInstCommonOutCnt + 1; -static const int kInstVertOutUnused = kInstCommonOutCnt + 2; - -// Frag Shader Output Record Offsets -static const int kInstFragOutFragCoordX = kInstCommonOutCnt; -static const int kInstFragOutFragCoordY = kInstCommonOutCnt + 1; -static const int kInstFragOutUnused = kInstCommonOutCnt + 2; - -// Compute Shader Output Record Offsets -static const int kInstCompOutGlobalInvocationIdX = kInstCommonOutCnt; -static const int kInstCompOutGlobalInvocationIdY = kInstCommonOutCnt + 1; -static const int kInstCompOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; - -// Tessellation Control Shader Output Record Offsets -static const int kInstTessCtlOutInvocationId = kInstCommonOutCnt; -static const int kInstTessCtlOutPrimitiveId = kInstCommonOutCnt + 1; -static const int kInstTessCtlOutUnused = kInstCommonOutCnt + 2; - -// Tessellation Eval Shader Output Record Offsets -static const int kInstTessEvalOutPrimitiveId = kInstCommonOutCnt; -static const int kInstTessEvalOutTessCoordU = kInstCommonOutCnt + 1; -static const int kInstTessEvalOutTessCoordV = kInstCommonOutCnt + 2; - -// Geometry Shader Output Record Offsets -static const int kInstGeomOutPrimitiveId = kInstCommonOutCnt; -static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1; -static const int kInstGeomOutUnused = kInstCommonOutCnt + 2; - -// Ray Tracing Shader Output Record Offsets -static const int kInstRayTracingOutLaunchIdX = kInstCommonOutCnt; -static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1; -static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2; - -// Mesh Shader Output Record Offsets -static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt; -static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1; -static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; - -// Task Shader Output Record Offsets -static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt; -static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1; -static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; - -// Size of Common and Stage-specific Members -static const int kInstStageOutCnt = kInstCommonOutCnt + 3; - -// Validation Error Code Offset -// -// This identifies the validation error. It also helps to identify -// how many words follow in the record and their meaning. -static const int kInstValidationOutError = kInstStageOutCnt; - -// Validation-specific Output Record Offsets -// -// Each different validation will generate a potentially different -// number of words at the end of the record giving more specifics -// about the validation error. -// -// A bindless bounds error will output the index and the bound. -static const int kInstBindlessBoundsOutDescSet = kInstStageOutCnt + 1; -static const int kInstBindlessBoundsOutDescBinding = kInstStageOutCnt + 2; -static const int kInstBindlessBoundsOutDescIndex = kInstStageOutCnt + 3; -static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 4; -static const int kInstBindlessBoundsOutUnused = kInstStageOutCnt + 5; -static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 6; - -// A descriptor uninitialized error will output the index. -static const int kInstBindlessUninitOutDescSet = kInstStageOutCnt + 1; -static const int kInstBindlessUninitOutBinding = kInstStageOutCnt + 2; -static const int kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 3; -static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 4; -static const int kInstBindlessUninitOutUnused2 = kInstStageOutCnt + 5; -static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 6; - -// A buffer out-of-bounds error will output the descriptor -// index, the buffer offset and the buffer size -static const int kInstBindlessBuffOOBOutDescSet = kInstStageOutCnt + 1; -static const int kInstBindlessBuffOOBOutDescBinding = kInstStageOutCnt + 2; -static const int kInstBindlessBuffOOBOutDescIndex = kInstStageOutCnt + 3; -static const int kInstBindlessBuffOOBOutBuffOff = kInstStageOutCnt + 4; -static const int kInstBindlessBuffOOBOutBuffSize = kInstStageOutCnt + 5; -static const int kInstBindlessBuffOOBOutCnt = kInstStageOutCnt + 6; - -// A buffer address unalloc error will output the 64-bit pointer in -// two 32-bit pieces, lower bits first. -static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1; -static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2; -static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3; - -// Maximum Output Record Member Count -static const int kInstMaxOutCnt = kInstStageOutCnt + 6; - -// Validation Error Codes -// -// These are the possible validation error codes. -static const int kInstErrorBindlessBounds = 0; -static const int kInstErrorBindlessUninit = 1; -static const int kInstErrorBuffAddrUnallocRef = 2; -// Deleted: static const int kInstErrorBindlessBuffOOB = 3; -// This comment will will remain for 2 releases to allow -// for the transition of all builds. Buffer OOB is -// generating the following four differentiated codes instead: -static const int kInstErrorBuffOOBUniform = 4; -static const int kInstErrorBuffOOBStorage = 5; -static const int kInstErrorBuffOOBUniformTexel = 6; -static const int kInstErrorBuffOOBStorageTexel = 7; -static const int kInstErrorMax = kInstErrorBuffOOBStorageTexel; - -// Direct Input Buffer Offsets -// -// The following values provide member offsets into the input buffers -// consumed by InstrumentPass::GenDebugDirectRead(). This method is utilized -// by InstBindlessCheckPass. -// -// The only object in an input buffer is a runtime array of unsigned -// integers. Each validation will have its own formatting of this array. -static const int kDebugInputDataOffset = 0; - // Debug Buffer Bindings // // These are the bindings for the different buffers which are // read or written by the instrumentation passes. // -// This is the output buffer written by InstBindlessCheckPass, -// InstBuffAddrCheckPass, and possibly other future validations. -static const int kDebugOutputBindingStream = 0; - -// The binding for the input buffer read by InstBindlessCheckPass. -static const int kDebugInputBindingBindless = 1; - -// The binding for the input buffer read by InstBuffAddrCheckPass. -static const int kDebugInputBindingBuffAddr = 2; - // This is the output buffer written by InstDebugPrintfPass. static const int kDebugOutputPrintfStream = 3; -// clang-format off -// Bindless Validation Input Buffer Format -// -// An input buffer for bindless validation has this structure: -// GLSL: -// layout(buffer_reference, std430, buffer_reference_align = 8) buffer DescriptorSetData { -// uint num_bindings; -// uint data[]; -// }; -// -// layout(set = 7, binding = 1, std430) buffer inst_bindless_InputBuffer -// { -// DescriptorSetData desc_sets[32]; -// } inst_bindless_input_buffer; -// -// -// To look up the length of a binding: -// uint length = inst_bindless_input_buffer[set].data[binding]; -// Scalar bindings have a length of 1. -// -// To look up the initialization state of a descriptor in a binding: -// uint num_bindings = inst_bindless_input_buffer[set].num_bindings; -// uint binding_state_start = inst_bindless_input_buffer[set].data[num_bindings + binding]; -// uint init_state = inst_bindless_input_buffer[set].data[binding_state_start + index]; -// -// For scalar bindings, use 0 for the index. -// clang-format on -// -// The size of the inst_bindless_input_buffer array, regardless of how many -// descriptor sets the device supports. -static const int kDebugInputBindlessMaxDescSets = 32; - -// Buffer Device Address Input Buffer Format -// -// An input buffer for buffer device address validation consists of a single -// array of unsigned 64-bit integers we will call Data[]. This array is -// formatted as follows: -// -// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer -// addresses. The list is terminated with the address 0xffffffffffffffff. -// If 0x0 is not a valid buffer address, this address is inserted at the -// start of the list. -// -static const int kDebugInputBuffAddrPtrOffset = 1; -// -// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which -// gives an offset to the start of the buffer length data. More -// specifically, for a buffer whose pointer is located at input buffer offset -// i, the length is located at: -// -// Data[ i - kDebugInputBuffAddrPtrOffset -// + Data[ kDebugInputBuffAddrLengthOffset ] ] -// -// The length associated with the 0xffffffffffffffff address is zero. If -// not a valid buffer, the length associated with the 0x0 address is zero. -static const int kDebugInputBuffAddrLengthOffset = 0; - } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 542b745..83b1a8e 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h
@@ -33,15 +33,19 @@ #else #define SPIRV_TOOLS_EXPORT __declspec(dllimport) #endif +#define SPIRV_TOOLS_LOCAL #else #if defined(SPIRV_TOOLS_IMPLEMENTATION) #define SPIRV_TOOLS_EXPORT __attribute__((visibility("default"))) +#define SPIRV_TOOLS_LOCAL __attribute__((visibility("hidden"))) #else #define SPIRV_TOOLS_EXPORT +#define SPIRV_TOOLS_LOCAL #endif #endif #else #define SPIRV_TOOLS_EXPORT +#define SPIRV_TOOLS_LOCAL #endif // Helpers @@ -143,6 +147,7 @@ // may be larger than 32, which would require such a typed literal value to // occupy multiple SPIR-V words. SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + SPV_OPERAND_TYPE_LITERAL_FLOAT, // Always 32-bit float. // Set 3: The literal string operand type. SPV_OPERAND_TYPE_LITERAL_STRING, @@ -285,6 +290,28 @@ // An optional packed vector format SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT, + // Concrete operand types for cooperative matrix. + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS, + // An optional cooperative matrix operands + SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS, + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT, + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE, + + // Enum type from SPV_INTEL_global_variable_fpga_decorations + SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER, + // Enum type from SPV_INTEL_global_variable_host_access + SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER, + // Enum type from SPV_INTEL_cache_controls + SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL, + // Enum type from SPV_INTEL_cache_controls + SPV_OPERAND_TYPE_STORE_CACHE_CONTROL, + // Enum type from SPV_INTEL_maximum_registers + SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS, + // Enum type from SPV_NV_raw_access_chains + SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS, + // Optional enum type from SPV_NV_raw_access_chains + SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS, + // This is a sentinel value, and does not represent an operand type. // It should come last. SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, @@ -310,6 +337,7 @@ SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise @@ -949,9 +977,16 @@ spv_optimizer_t* optimizer, const char* flag); // Registers passes specified by length number of flags in an optimizer object. +// Passes may remove interface variables that are unused. SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags( spv_optimizer_t* optimizer, const char** flags, const size_t flag_count); +// Registers passes specified by length number of flags in an optimizer object. +// Passes will not remove interface variables. +SPIRV_TOOLS_EXPORT bool +spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count); + // Optimizes the SPIR-V code of size |word_count| pointed to by |binary| and // returns an optimized spv_binary in |optimized_binary|. //
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index ee6c846..59ff82b 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp
@@ -37,7 +37,7 @@ std::function<spv_result_t(const spv_parsed_instruction_t& instruction)>; // C++ RAII wrapper around the C context object spv_context. -class Context { +class SPIRV_TOOLS_EXPORT Context { public: // Constructs a context targeting the given environment |env|. // @@ -73,7 +73,7 @@ }; // A RAII wrapper around a validator options object. -class ValidatorOptions { +class SPIRV_TOOLS_EXPORT ValidatorOptions { public: ValidatorOptions() : options_(spvValidatorOptionsCreate()) {} ~ValidatorOptions() { spvValidatorOptionsDestroy(options_); } @@ -163,7 +163,7 @@ }; // A C++ wrapper around an optimization options object. -class OptimizerOptions { +class SPIRV_TOOLS_EXPORT OptimizerOptions { public: OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } @@ -205,7 +205,7 @@ }; // A C++ wrapper around a reducer options object. -class ReducerOptions { +class SPIRV_TOOLS_EXPORT ReducerOptions { public: ReducerOptions() : options_(spvReducerOptionsCreate()) {} ~ReducerOptions() { spvReducerOptionsDestroy(options_); } @@ -236,7 +236,7 @@ }; // A C++ wrapper around a fuzzer options object. -class FuzzerOptions { +class SPIRV_TOOLS_EXPORT FuzzerOptions { public: FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } @@ -283,7 +283,7 @@ // provides methods for assembling, disassembling, and validating. // // Instances of this class provide basic thread-safety guarantee. -class SpirvTools { +class SPIRV_TOOLS_EXPORT SpirvTools { public: enum { // Default assembling option used by assemble(): @@ -388,7 +388,8 @@ bool IsValid() const; private: - struct Impl; // Opaque struct for holding the data fields used by this class. + struct SPIRV_TOOLS_LOCAL + Impl; // Opaque struct for holding the data fields used by this class. std::unique_ptr<Impl> impl_; // Unique pointer to implementation data. };
diff --git a/include/spirv-tools/linker.hpp b/include/spirv-tools/linker.hpp index d2f3e72..6ba6e96 100644 --- a/include/spirv-tools/linker.hpp +++ b/include/spirv-tools/linker.hpp
@@ -24,13 +24,8 @@ namespace spvtools { -class LinkerOptions { +class SPIRV_TOOLS_EXPORT LinkerOptions { public: - LinkerOptions() - : create_library_(false), - verify_ids_(false), - allow_partial_linkage_(false) {} - // Returns whether a library or an executable should be produced by the // linking phase. // @@ -63,10 +58,16 @@ allow_partial_linkage_ = allow_partial_linkage; } + bool GetUseHighestVersion() const { return use_highest_version_; } + void SetUseHighestVersion(bool use_highest_vers) { + use_highest_version_ = use_highest_vers; + } + private: - bool create_library_; - bool verify_ids_; - bool allow_partial_linkage_; + bool create_library_{false}; + bool verify_ids_{false}; + bool allow_partial_linkage_{false}; + bool use_highest_version_{false}; }; // Links one or more SPIR-V modules into a new SPIR-V module. That is, combine @@ -83,14 +84,15 @@ // * Some entry points were defined multiple times; // * Some imported symbols did not have an exported counterpart; // * Possibly other reasons. -spv_result_t Link(const Context& context, - const std::vector<std::vector<uint32_t>>& binaries, - std::vector<uint32_t>* linked_binary, - const LinkerOptions& options = LinkerOptions()); -spv_result_t Link(const Context& context, const uint32_t* const* binaries, - const size_t* binary_sizes, size_t num_binaries, - std::vector<uint32_t>* linked_binary, - const LinkerOptions& options = LinkerOptions()); +SPIRV_TOOLS_EXPORT spv_result_t +Link(const Context& context, const std::vector<std::vector<uint32_t>>& binaries, + std::vector<uint32_t>* linked_binary, + const LinkerOptions& options = LinkerOptions()); +SPIRV_TOOLS_EXPORT spv_result_t +Link(const Context& context, const uint32_t* const* binaries, + const size_t* binary_sizes, size_t num_binaries, + std::vector<uint32_t>* linked_binary, + const LinkerOptions& options = LinkerOptions()); } // namespace spvtools
diff --git a/include/spirv-tools/linter.hpp b/include/spirv-tools/linter.hpp index 52ed5a4..ccbcf0c 100644 --- a/include/spirv-tools/linter.hpp +++ b/include/spirv-tools/linter.hpp
@@ -24,7 +24,7 @@ // provides a method for linting. // // Instances of this class provides basic thread-safety guarantee. -class Linter { +class SPIRV_TOOLS_EXPORT Linter { public: explicit Linter(spv_target_env env); @@ -40,7 +40,7 @@ bool Run(const uint32_t* binary, size_t binary_size); private: - struct Impl; + struct SPIRV_TOOLS_LOCAL Impl; std::unique_ptr<Impl> impl_; }; } // namespace spvtools
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 8bdd4e8..a3119d9 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp
@@ -37,14 +37,14 @@ // provides methods for registering optimization passes and optimizing. // // Instances of this class provides basic thread-safety guarantee. -class Optimizer { +class SPIRV_TOOLS_EXPORT Optimizer { public: // The token for an optimization pass. It is returned via one of the // Create*Pass() standalone functions at the end of this header file and // consumed by the RegisterPass() method. Tokens are one-time objects that // only support move; copying is not allowed. struct PassToken { - struct Impl; // Opaque struct for holding internal data. + struct SPIRV_TOOLS_LOCAL Impl; // Opaque struct for holding internal data. PassToken(std::unique_ptr<Impl>); @@ -97,12 +97,20 @@ // Registers passes that attempt to improve performance of generated code. // This sequence of passes is subject to constant review and will change // from time to time. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. Optimizer& RegisterPerformancePasses(); + Optimizer& RegisterPerformancePasses(bool preserve_interface); // Registers passes that attempt to improve the size of generated code. // This sequence of passes is subject to constant review and will change // from time to time. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. Optimizer& RegisterSizePasses(); + Optimizer& RegisterSizePasses(bool preserve_interface); // Registers passes that attempt to legalize the generated code. // @@ -112,7 +120,11 @@ // // This sequence of passes is subject to constant review and will change // from time to time. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. Optimizer& RegisterLegalizationPasses(); + Optimizer& RegisterLegalizationPasses(bool preserve_interface); // Register passes specified in the list of |flags|. Each flag must be a // string of a form accepted by Optimizer::FlagHasValidForm(). @@ -121,8 +133,13 @@ // error message is emitted to the MessageConsumer object (use // Optimizer::SetMessageConsumer to define a message consumer, if needed). // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + // // If all the passes are registered successfully, it returns true. bool RegisterPassesFromFlags(const std::vector<std::string>& flags); + bool RegisterPassesFromFlags(const std::vector<std::string>& flags, + bool preserve_interface); // Registers the optimization pass associated with |flag|. This only accepts // |flag| values of the form "--pass_name[=pass_args]". If no such pass @@ -139,7 +156,11 @@ // // --legalize-hlsl: Registers all passes that legalize SPIR-V generated by an // HLSL front-end. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. bool RegisterPassFromFlag(const std::string& flag); + bool RegisterPassFromFlag(const std::string& flag, bool preserve_interface); // Validates that |flag| has a valid format. Strings accepted: // @@ -218,7 +239,7 @@ Optimizer& SetValidateAfterAll(bool validate); private: - struct Impl; // Opaque struct for holding internal data. + struct SPIRV_TOOLS_LOCAL Impl; // Opaque struct for holding internal data. std::unique_ptr<Impl> impl_; // Unique pointer to internal data. }; @@ -748,19 +769,9 @@ // potentially de-optimizing the instrument code, for example, inlining // the debug record output function throughout the module. // -// The instrumentation will read and write buffers in debug -// descriptor set |desc_set|. It will write |shader_id| in each output record +// The instrumentation will write |shader_id| in each output record // to identify the shader module which generated the record. -// |desc_length_enable| controls instrumentation of runtime descriptor array -// references, |desc_init_enable| controls instrumentation of descriptor -// initialization checking, and |buff_oob_enable| controls instrumentation -// of storage and uniform buffer bounds checking, all of which require input -// buffer support. |texbuff_oob_enable| controls instrumentation of texel -// buffers, which does not require input buffer support. -Optimizer::PassToken CreateInstBindlessCheckPass( - uint32_t desc_set, uint32_t shader_id, bool desc_length_enable = false, - bool desc_init_enable = false, bool buff_oob_enable = false, - bool texbuff_oob_enable = false); +Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id); // Create a pass to instrument physical buffer address checking // This pass instruments all physical buffer address references to check that @@ -781,8 +792,7 @@ // The instrumentation will read and write buffers in debug // descriptor set |desc_set|. It will write |shader_id| in each output record // to identify the shader module which generated the record. -Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, - uint32_t shader_id); +Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id); // Create a pass to instrument OpDebugPrintf instructions. // This pass replaces all OpDebugPrintf instructions with instructions to write @@ -971,6 +981,32 @@ // object, currently the pass would remove accesschain pointer argument passed // to the function Optimizer::PassToken CreateFixFuncCallArgumentsPass(); + +// Creates a trim-capabilities pass. +// This pass removes unused capabilities for a given module, and if possible, +// associated extensions. +// See `trim_capabilities.h` for the list of supported capabilities. +// +// If the module contains unsupported capabilities, this pass will ignore them. +// This should be fine in most cases, but could yield to incorrect results if +// the unknown capability interacts with one of the trimmed capabilities. +Optimizer::PassToken CreateTrimCapabilitiesPass(); + +// Creates a switch-descriptorset pass. +// This pass changes any DescriptorSet decorations with the value |ds_from| to +// use the new value |ds_to|. +Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t ds_from, + uint32_t ds_to); + +// Creates an invocation interlock placement pass. +// This pass ensures that an entry point will have at most one +// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that +// order. +Optimizer::PassToken CreateInvocationInterlockPlacementPass(); + +// 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); } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh index 2465d9c..4bb889a 100644 --- a/kokoro/macos-clang-release-bazel/build.sh +++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -30,20 +30,16 @@ git config --global --add safe.directory $SRC 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 +/usr/bin/python3 utils/git-sync-deps --treeless -# Get bazel 5.0.0 -gsutil cp gs://bazel/5.0.0/release/bazel-5.0.0-darwin-x86_64 . -chmod +x bazel-5.0.0-darwin-x86_64 +# 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 echo $(date): Build everything... -./bazel-5.0.0-darwin-x86_64 build --cxxopt=-std=c++17 :all +./bazel-7.0.2-darwin-x86_64 build --cxxopt=-std=c++17 :all echo $(date): Build completed. echo $(date): Starting bazel test... -./bazel-5.0.0-darwin-x86_64 test --cxxopt=-std=c++17 :all +./bazel-7.0.2-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 f2a06e0..e47037d 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh
@@ -131,6 +131,7 @@ git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers git clone https://github.com/google/re2 git clone https://github.com/google/effcee + git clone https://github.com/abseil/abseil-cpp abseil_cpp cd $SHADERC_DIR mkdir build @@ -141,7 +142,7 @@ cmake -GNinja -DRE2_BUILD_TESTING=OFF -DCMAKE_BUILD_TYPE="Release" .. echo $(date): Build glslang... - ninja glslangValidator + ninja glslang-standalone echo $(date): Build everything... ninja @@ -155,7 +156,7 @@ echo $(date): ctest completed. elif [ $TOOL = "cmake-android-ndk" ]; then using cmake-3.17.2 - using ndk-r21d + using ndk-r25c using ninja-1.10.0 clean_dir "$ROOT_DIR/build" @@ -163,7 +164,7 @@ echo $(date): Starting build... cmake -DCMAKE_BUILD_TYPE=Release \ - -DANDROID_NATIVE_API_LEVEL=android-16 \ + -DANDROID_NATIVE_API_LEVEL=android-24 \ -DANDROID_ABI="armeabi-v7a with NEON" \ -DSPIRV_SKIP_TESTS=ON \ -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ @@ -175,7 +176,7 @@ ninja echo $(date): Build completed. elif [ $TOOL = "android-ndk-build" ]; then - using ndk-r21d + using ndk-r25c clean_dir "$ROOT_DIR/build" cd "$ROOT_DIR/build" @@ -190,7 +191,7 @@ echo $(date): ndk-build completed. elif [ $TOOL = "bazel" ]; then - using bazel-5.0.0 + using bazel-7.0.2 echo $(date): Build everything... bazel build --cxxopt=-std=c++17 :all
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat index bb14da3..fe15f2d 100644 --- a/kokoro/scripts/windows/build.bat +++ b/kokoro/scripts/windows/build.bat
@@ -30,6 +30,9 @@ 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..." @@ -56,6 +59,10 @@ :: Build spirv-fuzz set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON +if "%BUILD_TESTS%" == "NO" ( + set CMAKE_FLAGS=-DSPIRV_SKIP_TESTS=ON %CMAKE_FLAGS% +) + cmake %CMAKE_FLAGS% .. if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% @@ -71,10 +78,12 @@ :: ################################################ :: Run the tests :: ################################################ -echo "Running Tests... %DATE% %TIME%" -ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 -if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! -echo "Tests Completed %DATE% %TIME%" +if "%BUILD_TESTS%" NEQ "NO" ( + echo "Running Tests... %DATE% %TIME%" + ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 + if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! + echo "Tests Completed %DATE% %TIME%" +) :: ################################################ :: Install and package.
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index acfa0c1..d0454c6c 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt
@@ -31,7 +31,7 @@ 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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + 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} @@ -53,7 +53,7 @@ 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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + 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} @@ -75,7 +75,7 @@ set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json") set(VIMSYNTAX_FILE "${spirv-tools_BINARY_DIR}/spvasm.vim") add_custom_command(OUTPUT ${VIMSYNTAX_FILE} - COMMAND ${PYTHON_EXECUTABLE} ${VIMSYNTAX_PROCESSING_SCRIPT} + COMMAND Python3::Interpreter ${VIMSYNTAX_PROCESSING_SCRIPT} --spirv-core-grammar=${GRAMMAR_JSON_FILE} --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} @@ -91,7 +91,7 @@ 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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} --glsl-insts-output=${GRAMMAR_INC_FILE} --output-language=c++ @@ -105,7 +105,7 @@ 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 ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + 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} @@ -120,7 +120,7 @@ set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") endif() add_custom_command(OUTPUT ${INSTS_FILE} - COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} --extinst-vendor-grammar=${GRAMMAR_FILE} --vendor-insts-output=${INSTS_FILE} --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX} @@ -134,7 +134,7 @@ macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE) set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h) add_custom_command(OUTPUT ${OUT_H} - COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT} + COMMAND Python3::Interpreter ${LANG_HEADER_PROCESSING_SCRIPT} --extinst-grammar=${GRAMMAR_FILE} --extinst-output-path=${OUT_H} DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE} @@ -156,6 +156,7 @@ 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}) @@ -168,7 +169,7 @@ set(GENERATOR_INC_FILE ${spirv-tools_BINARY_DIR}/generators.inc) set(SPIRV_XML_REGISTRY_FILE ${SPIRV_HEADER_INCLUDE_DIR}/spirv/spir-v.xml) add_custom_command(OUTPUT ${GENERATOR_INC_FILE} - COMMAND ${PYTHON_EXECUTABLE} ${XML_REGISTRY_PROCESSING_SCRIPT} + COMMAND Python3::Interpreter ${XML_REGISTRY_PROCESSING_SCRIPT} --xml=${SPIRV_XML_REGISTRY_FILE} --generator-output=${GENERATOR_INC_FILE} DEPENDS ${XML_REGISTRY_PROCESSING_SCRIPT} ${SPIRV_XML_REGISTRY_FILE} @@ -198,7 +199,7 @@ set(SPIRV_TOOLS_CHANGES_FILE ${spirv-tools_SOURCE_DIR}/CHANGES) add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC} - COMMAND ${PYTHON_EXECUTABLE} + COMMAND Python3::Interpreter ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC} DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} @@ -418,12 +419,6 @@ endif() endif() -if (ANDROID) - foreach(target ${SPIRV_TOOLS_TARGETS}) - target_link_libraries(${target} PRIVATE android log) - endforeach() -endif() - if(ENABLE_SPIRV_TOOLS_INSTALL) install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets) export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp index 6df823e..0092d01 100644 --- a/source/assembly_grammar.cpp +++ b/source/assembly_grammar.cpp
@@ -21,6 +21,7 @@ #include "source/ext_inst.h" #include "source/opcode.h" #include "source/operand.h" +#include "source/spirv_target_env.h" #include "source/table.h" namespace spvtools { @@ -154,11 +155,12 @@ CASE(InBoundsAccessChain), CASE(PtrAccessChain), CASE(InBoundsPtrAccessChain), - CASE(CooperativeMatrixLengthNV) + CASE(CooperativeMatrixLengthNV), + CASE(CooperativeMatrixLengthKHR) }; // The 60 is determined by counting the opcodes listed in the spec. -static_assert(60 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]), +static_assert(61 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]), "OpSpecConstantOp opcode table is incomplete"); #undef CASE // clang-format on @@ -175,15 +177,18 @@ 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 cap_desc = {}; + spv_operand_desc entry = {}; if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, static_cast<uint32_t>(cap_array[i]), - &cap_desc)) { - // spvOperandTableValueLookup() filters capabilities internally - // according to the current target environment by itself. So we - // should be safe to add this capability if the lookup succeeds. - cap_set.Add(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) { + cap_set.insert(cap_array[i]); + } } } return cap_set;
diff --git a/source/binary.cpp b/source/binary.cpp index beb56be..cf1f0b7 100644 --- a/source/binary.cpp +++ b/source/binary.cpp
@@ -546,6 +546,13 @@ parsed_operand.number_bit_width = 32; break; + case SPV_OPERAND_TYPE_LITERAL_FLOAT: + // These are regular single-word literal float operands. + parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_FLOAT; + parsed_operand.number_kind = SPV_NUMBER_FLOATING; + parsed_operand.number_bit_width = 32; + break; + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER; @@ -626,7 +633,6 @@ } break; case SPV_OPERAND_TYPE_CAPABILITY: - case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: case SPV_OPERAND_TYPE_EXECUTION_MODEL: case SPV_OPERAND_TYPE_ADDRESSING_MODEL: case SPV_OPERAND_TYPE_MEMORY_MODEL: @@ -664,7 +670,8 @@ case SPV_OPERAND_TYPE_QUANTIZATION_MODES: case SPV_OPERAND_TYPE_OVERFLOW_MODES: case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: - case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: { + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: { // A single word that is a plain enum value. // Map an optional operand type to its corresponding concrete type. @@ -683,22 +690,44 @@ spvPushOperandTypes(entry->operandTypes, expected_operands); } break; + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: { + spv_operand_desc entry; + if (grammar_.lookupOperand(type, word, &entry)) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(parsed_operand.type) + << " operand: " << word + << ", if you are creating a new source language please use " + "value 0 " + "(Unknown) and when ready, add your source language to " + "SPRIV-Headers"; + } + // Prepare to accept operands to this operand, if needed. + spvPushOperandTypes(entry->operandTypes, expected_operands); + } break; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: case SPV_OPERAND_TYPE_FUNCTION_CONTROL: case SPV_OPERAND_TYPE_LOOP_CONTROL: case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: - case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: { + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: { // This operand is a mask. // Map an optional operand type to its corresponding concrete type. if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE) parsed_operand.type = SPV_OPERAND_TYPE_IMAGE; - else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS) + if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS) parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS; + if (type == SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS) + 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; // Check validity of set mask bits. Also prepare for operands for those // masks if they have any. To get operand order correct, scan from
diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp index 6daed32..6269af5 100644 --- a/source/diff/diff.cpp +++ b/source/diff/diff.cpp
@@ -101,9 +101,12 @@ return from < id_map_.size() && id_map_[from] != 0; } - // Map any ids in src and dst that have not been mapped to new ids in dst and - // src respectively. - void MapUnmatchedIds(IdMap& other_way); + bool IsMapped(const opt::Instruction* from_inst) const { + assert(from_inst != nullptr); + assert(!from_inst->HasResultId()); + + return inst_map_.find(from_inst) != inst_map_.end(); + } // Some instructions don't have result ids. Those are mapped by pointer. void MapInsts(const opt::Instruction* from_inst, @@ -117,6 +120,12 @@ uint32_t IdBound() const { return static_cast<uint32_t>(id_map_.size()); } + // Generate a fresh id in this mapping's domain. + uint32_t MakeFreshId() { + id_map_.push_back(0); + return static_cast<uint32_t>(id_map_.size()) - 1; + } + private: // Given an id, returns the corresponding id in the other module, or 0 if not // matched yet. @@ -150,10 +159,16 @@ bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); } bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); } + bool IsDstMapped(const opt::Instruction* dst_inst) { + return dst_to_src_.IsMapped(dst_inst); + } // Map any ids in src and dst that have not been mapped to new ids in dst and - // src respectively. - void MapUnmatchedIds(); + // src respectively. Use src_insn_defined and dst_insn_defined to ignore ids + // that are simply never defined. (Since we assume the inputs are valid + // SPIR-V, this implies they are also never used.) + void MapUnmatchedIds(std::function<bool(uint32_t)> src_insn_defined, + std::function<bool(uint32_t)> dst_insn_defined); // Some instructions don't have result ids. Those are mapped by pointer. void MapInsts(const opt::Instruction* src_inst, @@ -203,6 +218,11 @@ void MapIdToInstruction(uint32_t id, const opt::Instruction* inst); + // Return true if id is mapped to any instruction, false otherwise. + bool IsDefined(uint32_t id) { + return id < inst_map_.size() && inst_map_[id] != nullptr; + } + void MapIdsToInstruction( opt::IteratorRange<opt::Module::const_inst_iterator> section); void MapIdsToInfos( @@ -338,6 +358,59 @@ std::function<void(const IdGroup& src_group, const IdGroup& dst_group)> match_group); + // Bucket `src_ids` and `dst_ids` by the key ids returned by `get_group`, and + // then call `match_group` on pairs of buckets whose key ids are matched with + // each other. + // + // For example, suppose we want to pair up groups of instructions with the + // same type. Naturally, the source instructions refer to their types by their + // ids in the source, and the destination instructions use destination type + // ids, so simply comparing source and destination type ids as integers, as + // `GroupIdsAndMatch` would do, is meaningless. But if a prior call to + // `MatchTypeIds` has established type matches between the two modules, then + // we can consult those to pair source and destination buckets whose types are + // equivalent. + // + // Suppose our input groups are as follows: + // + // - src_ids: { 1 -> 100, 2 -> 300, 3 -> 100, 4 -> 200 } + // - dst_ids: { 5 -> 10, 6 -> 20, 7 -> 10, 8 -> 300 } + // + // Here, `X -> Y` means that the instruction with SPIR-V id `X` is a member of + // the group, and `Y` is the id of its type. If we use + // `Differ::GroupIdsHelperGetTypeId` for `get_group`, then + // `get_group(X) == Y`. + // + // These instructions are bucketed by type as follows: + // + // - source: [1, 3] -> 100 + // [4] -> 200 + // [2] -> 300 + // + // - destination: [5, 7] -> 10 + // [6] -> 20 + // [8] -> 300 + // + // Now suppose that we have previously matched up src type 100 with dst type + // 10, and src type 200 with dst type 20, but no other types are matched. + // + // Then `match_group` is called twice: + // - Once with ([1,3], [5, 7]), corresponding to 100/10 + // - Once with ([4],[6]), corresponding to 200/20 + // + // The source type 300 isn't matched with anything, so the fact that there's a + // destination type 300 is irrelevant, and thus 2 and 8 are never passed to + // `match_group`. + // + // This function isn't specific to types; it simply buckets by the ids + // returned from `get_group`, and consults existing matches to pair up the + // resulting buckets. + void GroupIdsAndMatchByMappedId( + const IdGroup& src_ids, const IdGroup& dst_ids, + uint32_t (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function<void(const IdGroup& src_group, const IdGroup& dst_group)> + match_group); + // Helper functions that determine if two instructions match bool DoIdsMatch(uint32_t src_id, uint32_t dst_id); bool DoesOperandMatch(const opt::Operand& src_operand, @@ -504,36 +577,27 @@ FunctionMap dst_funcs_; }; -void IdMap::MapUnmatchedIds(IdMap& other_way) { - const uint32_t src_id_bound = static_cast<uint32_t>(id_map_.size()); - const uint32_t dst_id_bound = static_cast<uint32_t>(other_way.id_map_.size()); - - uint32_t next_src_id = src_id_bound; - uint32_t next_dst_id = dst_id_bound; +void SrcDstIdMap::MapUnmatchedIds( + std::function<bool(uint32_t)> src_insn_defined, + std::function<bool(uint32_t)> dst_insn_defined) { + const uint32_t src_id_bound = static_cast<uint32_t>(src_to_dst_.IdBound()); + const uint32_t dst_id_bound = static_cast<uint32_t>(dst_to_src_.IdBound()); for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) { - if (!IsMapped(src_id)) { - MapIds(src_id, next_dst_id); - - other_way.id_map_.push_back(0); - other_way.MapIds(next_dst_id++, src_id); + if (!src_to_dst_.IsMapped(src_id) && src_insn_defined(src_id)) { + uint32_t fresh_dst_id = dst_to_src_.MakeFreshId(); + MapIds(src_id, fresh_dst_id); } } for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) { - if (!other_way.IsMapped(dst_id)) { - id_map_.push_back(0); - MapIds(next_src_id, dst_id); - - other_way.MapIds(dst_id, next_src_id++); + if (!dst_to_src_.IsMapped(dst_id) && dst_insn_defined(dst_id)) { + uint32_t fresh_src_id = src_to_dst_.MakeFreshId(); + MapIds(fresh_src_id, dst_id); } } } -void SrcDstIdMap::MapUnmatchedIds() { - src_to_dst_.MapUnmatchedIds(dst_to_src_); -} - void IdInstructions::MapIdToInstruction(uint32_t id, const opt::Instruction* inst) { assert(id != 0); @@ -889,6 +953,37 @@ } } +void Differ::GroupIdsAndMatchByMappedId( + const IdGroup& src_ids, const IdGroup& dst_ids, + uint32_t (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function<void(const IdGroup& src_group, const IdGroup& dst_group)> + match_group) { + // Group the ids based on a key (get_group) + std::map<uint32_t, IdGroup> src_groups; + std::map<uint32_t, IdGroup> dst_groups; + + GroupIds<uint32_t>(src_ids, true, &src_groups, get_group); + GroupIds<uint32_t>(dst_ids, false, &dst_groups, get_group); + + // Iterate over pairs of groups whose keys map to each other. + for (const auto& iter : src_groups) { + const uint32_t& src_key = iter.first; + const IdGroup& src_group = iter.second; + + if (src_key == 0) { + continue; + } + + if (id_map_.IsSrcMapped(src_key)) { + const uint32_t& dst_key = id_map_.MappedDstId(src_key); + const IdGroup& dst_group = dst_groups[dst_key]; + + // Let the caller match the groups as appropriate. + match_group(src_group, dst_group); + } + } +} + bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) { assert(dst_id != 0); return id_map_.MappedDstId(src_id) == dst_id; @@ -1419,7 +1514,6 @@ GroupIdsAndMatch<std::string>( src, dst, "", &Differ::GetSanitizedName, [this](const IdGroup& src_group, const IdGroup& dst_group) { - // Match only if there's a unique forward declaration with this debug // name. if (src_group.size() == 1 && dst_group.size() == 1) { @@ -1574,6 +1668,8 @@ id_map_.MapIds(match_result.src_id, match_result.dst_id); + MatchFunctionParamIds(src_funcs_[match_result.src_id], + dst_funcs_[match_result.dst_id]); MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id), dst_func_insts.at(match_result.dst_id), match_result.src_match, match_result.dst_match, 0); @@ -1598,7 +1694,6 @@ GroupIdsAndMatch<std::string>( src_params, dst_params, "", &Differ::GetSanitizedName, [this](const IdGroup& src_group, const IdGroup& dst_group) { - // There shouldn't be two parameters with the same name, so the ids // should match. There is nothing restricting the SPIR-V however to have // two parameters with the same name, so be resilient against that. @@ -1609,17 +1704,17 @@ // Then match the parameters by their type. If there are multiple of them, // match them by their order. - GroupIdsAndMatch<uint32_t>( - src_params, dst_params, 0, &Differ::GroupIdsHelperGetTypeId, + GroupIdsAndMatchByMappedId( + src_params, dst_params, &Differ::GroupIdsHelperGetTypeId, [this](const IdGroup& src_group_by_type_id, const IdGroup& dst_group_by_type_id) { - const size_t shared_param_count = std::min(src_group_by_type_id.size(), dst_group_by_type_id.size()); for (size_t param_index = 0; param_index < shared_param_count; ++param_index) { - id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]); + id_map_.MapIds(src_group_by_type_id[param_index], + dst_group_by_type_id[param_index]); } }); } @@ -1943,6 +2038,10 @@ // Always unsigned integers. *number_bit_width = 32; return SPV_NUMBER_UNSIGNED_INT; + case SPV_OPERAND_TYPE_LITERAL_FLOAT: + // Always float. + *number_bit_width = 32; + return SPV_NUMBER_FLOATING; case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: switch (inst.opcode()) { @@ -2064,9 +2163,10 @@ } // Otherwise match them by name. - bool matched = false; for (const opt::Instruction* src_inst : src_insts) { for (const opt::Instruction* dst_inst : dst_insts) { + if (id_map_.IsDstMapped(dst_inst)) continue; + const opt::Operand& src_name = src_inst->GetOperand(2); const opt::Operand& dst_name = dst_inst->GetOperand(2); @@ -2075,13 +2175,9 @@ uint32_t dst_id = dst_inst->GetSingleWordOperand(1); id_map_.MapIds(src_id, dst_id); id_map_.MapInsts(src_inst, dst_inst); - matched = true; break; } } - if (matched) { - break; - } } } } @@ -2126,7 +2222,6 @@ spv::StorageClass::Max, &Differ::GroupIdsHelperGetTypePointerStorageClass, [this](const IdGroup& src_group_by_storage_class, const IdGroup& dst_group_by_storage_class) { - // Group them further by the type they are pointing to and loop over // them. GroupIdsAndMatch<spv::Op>( @@ -2134,7 +2229,6 @@ spv::Op::Max, &Differ::GroupIdsHelperGetTypePointerTypeOp, [this](const IdGroup& src_group_by_type_op, const IdGroup& dst_group_by_type_op) { - // Group them even further by debug info, if possible and match by // debug name. MatchTypeForwardPointersByName(src_group_by_type_op, @@ -2199,7 +2293,9 @@ case spv::Op::OpTypeVoid: case spv::Op::OpTypeBool: case spv::Op::OpTypeSampler: - // void, bool and sampler are unique, match them. + case spv::Op::OpTypeAccelerationStructureNV: + case spv::Op::OpTypeRayQueryKHR: + // the above types have no operands and are unique, match them. return true; case spv::Op::OpTypeInt: case spv::Op::OpTypeFloat: @@ -2378,7 +2474,6 @@ GroupIdsAndMatch<std::string>( src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName, [this](const IdGroup& src_group, const IdGroup& dst_group) { - // If there is a single function with this name in src and dst, it's a // definite match. if (src_group.size() == 1 && dst_group.size() == 1) { @@ -2392,7 +2487,6 @@ &Differ::GroupIdsHelperGetTypeId, [this](const IdGroup& src_group_by_type_id, const IdGroup& dst_group_by_type_id) { - if (src_group_by_type_id.size() == 1 && dst_group_by_type_id.size() == 1) { id_map_.MapIds(src_group_by_type_id[0], @@ -2437,7 +2531,6 @@ src_func_ids, dst_func_ids, 0, &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, src_func_insts_, dst_func_insts_); }); @@ -2647,7 +2740,9 @@ } spv_result_t Differ::Output() { - id_map_.MapUnmatchedIds(); + id_map_.MapUnmatchedIds( + [this](uint32_t src_id) { return src_id_to_.IsDefined(src_id); }, + [this](uint32_t dst_id) { return dst_id_to_.IsDefined(dst_id); }); src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr); dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr);
diff --git a/source/disassemble.cpp b/source/disassemble.cpp index f862efd..f8f6f44 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp
@@ -357,7 +357,8 @@ stream_ << opcode_desc->name; } break; case SPV_OPERAND_TYPE_LITERAL_INTEGER: - case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_LITERAL_FLOAT: { SetRed(); EmitNumericLiteral(&stream_, inst, operand); ResetColor(); @@ -424,6 +425,7 @@ case SPV_OPERAND_TYPE_SELECTION_CONTROL: case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS: EmitMaskOperand(operand.type, word); break; default:
diff --git a/source/enum_set.h b/source/enum_set.h index 28ee5fe..a375138 100644 --- a/source/enum_set.h +++ b/source/enum_set.h
@@ -1,4 +1,4 @@ -// Copyright (c) 2016 Google Inc. +// Copyright (c) 2023 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,195 +12,456 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <functional> +#include <initializer_list> +#include <limits> +#include <type_traits> +#include <vector> + #ifndef SOURCE_ENUM_SET_H_ #define SOURCE_ENUM_SET_H_ -#include <cstdint> -#include <functional> -#include <memory> -#include <set> -#include <utility> - #include "source/latest_version_spirv_header.h" -#include "source/util/make_unique.h" namespace spvtools { -// A set of values of a 32-bit enum type. -// It is fast and compact for the common case, where enum values -// are at most 63. But it can represent enums with larger values, -// as may appear in extensions. -template <typename EnumType> +// This container is optimized to store and retrieve unsigned enum values. +// The base model for this implementation is an open-addressing hashtable with +// linear probing. For small enums (max index < 64), all operations are O(1). +// +// - Enums are stored in buckets (64 contiguous values max per bucket) +// - Buckets ranges don't overlap, but don't have to be contiguous. +// - Enums are packed into 64-bits buckets, using 1 bit per enum value. +// +// Example: +// - MyEnum { A = 0, B = 1, C = 64, D = 65 } +// - 2 buckets are required: +// - bucket 0, storing values in the range [ 0; 64[ +// - bucket 1, storing values in the range [64; 128[ +// +// - Buckets are stored in a sorted vector (sorted by bucket range). +// - Retrieval is done by computing the theoretical bucket index using the enum +// value, and +// doing a linear scan from this position. +// - Insertion is done by retrieving the bucket and either: +// - inserting a new bucket in the sorted vector when no buckets has a +// compatible range. +// - setting the corresponding bit in the bucket. +// This means insertion in the middle/beginning can cause a memmove when no +// bucket is available. In our case, this happens at most 23 times for the +// largest enum we have (Opcodes). +template <typename T> class EnumSet { private: - // The ForEach method will call the functor on enum values in - // enum value order (lowest to highest). To make that easier, use - // an ordered set for the overflow values. - using OverflowSetType = std::set<uint32_t>; + using BucketType = uint64_t; + using ElementType = std::underlying_type_t<T>; + static_assert(std::is_enum_v<T>, "EnumSets only works with enums."); + static_assert(std::is_signed_v<ElementType> == false, + "EnumSet doesn't supports signed enums."); + + // Each bucket can hold up to `kBucketSize` distinct, contiguous enum values. + // The first value a bucket can hold must be aligned on `kBucketSize`. + struct Bucket { + // bit mask to store `kBucketSize` enums. + BucketType data; + // 1st enum this bucket can represent. + T start; + + friend bool operator==(const Bucket& lhs, const Bucket& rhs) { + return lhs.start == rhs.start && lhs.data == rhs.data; + } + }; + + // How many distinct values can a bucket hold? 1 bit per value. + static constexpr size_t kBucketSize = sizeof(BucketType) * 8ULL; public: - // Construct an empty set. - EnumSet() {} - // Construct an set with just the given enum value. - explicit EnumSet(EnumType c) { Add(c); } - // Construct an set from an initializer list of enum values. - EnumSet(std::initializer_list<EnumType> cs) { - for (auto c : cs) Add(c); - } - EnumSet(uint32_t count, const EnumType* ptr) { - for (uint32_t i = 0; i < count; ++i) Add(ptr[i]); - } - // Copy constructor. - EnumSet(const EnumSet& other) { *this = other; } - // Move constructor. The moved-from set is emptied. - EnumSet(EnumSet&& other) { - mask_ = other.mask_; - overflow_ = std::move(other.overflow_); - other.mask_ = 0; - other.overflow_.reset(nullptr); - } - // Assignment operator. - EnumSet& operator=(const EnumSet& other) { - if (&other != this) { - mask_ = other.mask_; - overflow_.reset(other.overflow_ ? new OverflowSetType(*other.overflow_) - : nullptr); + class Iterator { + public: + typedef Iterator self_type; + typedef T value_type; + typedef T& reference; + typedef T* pointer; + typedef std::forward_iterator_tag iterator_category; + typedef size_t difference_type; + + Iterator(const Iterator& other) + : set_(other.set_), + bucketIndex_(other.bucketIndex_), + bucketOffset_(other.bucketOffset_) {} + + Iterator& operator++() { + do { + if (bucketIndex_ >= set_->buckets_.size()) { + bucketIndex_ = set_->buckets_.size(); + bucketOffset_ = 0; + break; + } + + if (bucketOffset_ + 1 == kBucketSize) { + bucketOffset_ = 0; + ++bucketIndex_; + } else { + ++bucketOffset_; + } + + } while (bucketIndex_ < set_->buckets_.size() && + !set_->HasEnumAt(bucketIndex_, bucketOffset_)); + return *this; } + + Iterator operator++(int) { + Iterator old = *this; + operator++(); + return old; + } + + T operator*() const { + assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && + "operator*() called on an invalid iterator."); + return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); + } + + bool operator!=(const Iterator& other) const { + return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || + bucketIndex_ != other.bucketIndex_; + } + + bool operator==(const Iterator& other) const { + return !(operator!=(other)); + } + + Iterator& operator=(const Iterator& other) { + set_ = other.set_; + bucketIndex_ = other.bucketIndex_; + bucketOffset_ = other.bucketOffset_; + return *this; + } + + private: + Iterator(const EnumSet* set, size_t bucketIndex, ElementType bucketOffset) + : set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {} + + private: + const EnumSet* set_ = nullptr; + // Index of the bucket in the vector. + size_t bucketIndex_ = 0; + // Offset in bits in the current bucket. + ElementType bucketOffset_ = 0; + + friend class EnumSet; + }; + + // Required to allow the use of std::inserter. + using value_type = T; + using const_iterator = Iterator; + using iterator = Iterator; + + public: + iterator cbegin() const noexcept { + auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); + if (buckets_.size() == 0) { + return it; + } + + // The iterator has the logic to find the next valid bit. If the value 0 + // is not stored, use it to find the next valid bit. + if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { + ++it; + } + + return it; + } + + iterator begin() const noexcept { return cbegin(); } + + iterator cend() const noexcept { + return iterator(this, buckets_.size(), /* bucketOffset= */ 0); + } + + iterator end() const noexcept { return cend(); } + + // Creates an empty set. + EnumSet() : buckets_(0), size_(0) {} + + // Creates a set and store `value` in it. + EnumSet(T value) : EnumSet() { insert(value); } + + // Creates a set and stores each `values` in it. + EnumSet(std::initializer_list<T> values) : EnumSet() { + for (auto item : values) { + insert(item); + } + } + + // Creates a set, and insert `count` enum values pointed by `array` in it. + EnumSet(ElementType count, const T* array) : EnumSet() { + for (ElementType i = 0; i < count; i++) { + insert(array[i]); + } + } + + // Creates a set initialized with the content of the range [begin; end[. + template <class InputIt> + EnumSet(InputIt begin, InputIt end) : EnumSet() { + for (; begin != end; ++begin) { + insert(*begin); + } + } + + // Copies the EnumSet `other` into a new EnumSet. + EnumSet(const EnumSet& other) + : buckets_(other.buckets_), size_(other.size_) {} + + // Moves the EnumSet `other` into a new EnumSet. + EnumSet(EnumSet&& other) + : buckets_(std::move(other.buckets_)), size_(other.size_) {} + + // Deep-copies the EnumSet `other` into this EnumSet. + EnumSet& operator=(const EnumSet& other) { + buckets_ = other.buckets_; + size_ = other.size_; return *this; } - friend bool operator==(const EnumSet& a, const EnumSet& b) { - if (a.mask_ != b.mask_) { - return false; + // Matches std::unordered_set::insert behavior. + std::pair<iterator, bool> insert(const T& value) { + const size_t index = FindBucketForValue(value); + const ElementType offset = ComputeBucketOffset(value); + + if (index >= buckets_.size() || + buckets_[index].start != ComputeBucketStart(value)) { + size_ += 1; + InsertBucketFor(index, value); + return std::make_pair(Iterator(this, index, offset), true); } - if (a.overflow_ == nullptr && b.overflow_ == nullptr) { + auto& bucket = buckets_[index]; + const auto mask = ComputeMaskForValue(value); + if (bucket.data & mask) { + return std::make_pair(Iterator(this, index, offset), false); + } + + size_ += 1; + bucket.data |= ComputeMaskForValue(value); + return std::make_pair(Iterator(this, index, offset), true); + } + + // Inserts `value` in the set if possible. + // Similar to `std::unordered_set::insert`, except the hint is ignored. + // Returns an iterator to the inserted element, or the element preventing + // insertion. + iterator insert(const_iterator, const T& value) { + return insert(value).first; + } + + // Inserts `value` in the set if possible. + // Similar to `std::unordered_set::insert`, except the hint is ignored. + // Returns an iterator to the inserted element, or the element preventing + // insertion. + iterator insert(const_iterator, T&& value) { return insert(value).first; } + + // Inserts all the values in the range [`first`; `last[. + // Similar to `std::unordered_set::insert`. + template <class InputIt> + void insert(InputIt first, InputIt last) { + for (auto it = first; it != last; ++it) { + insert(*it); + } + } + + // Removes the value `value` into the set. + // Similar to `std::unordered_set::erase`. + // Returns the number of erased elements. + size_t erase(const T& value) { + const size_t index = FindBucketForValue(value); + if (index >= buckets_.size() || + buckets_[index].start != ComputeBucketStart(value)) { + return 0; + } + + auto& bucket = buckets_[index]; + const auto mask = ComputeMaskForValue(value); + if (!(bucket.data & mask)) { + return 0; + } + + size_ -= 1; + bucket.data &= ~mask; + if (bucket.data == 0) { + buckets_.erase(buckets_.cbegin() + index); + } + return 1; + } + + // Returns true if `value` is present in the set. + bool contains(T value) const { + const size_t index = FindBucketForValue(value); + if (index >= buckets_.size() || + buckets_[index].start != ComputeBucketStart(value)) { + return false; + } + auto& bucket = buckets_[index]; + return bucket.data & ComputeMaskForValue(value); + } + + // Returns the 1 if `value` is present in the set, `0` otherwise. + inline size_t count(T value) const { return contains(value) ? 1 : 0; } + + // Returns true if the set is holds no values. + inline bool empty() const { return size_ == 0; } + + // Returns the number of enums stored in this set. + size_t size() const { return size_; } + + // Returns true if this set contains at least one value contained in `in_set`. + // Note: If `in_set` is empty, this function returns true. + bool HasAnyOf(const EnumSet<T>& in_set) const { + if (in_set.empty()) { return true; } - if (a.overflow_ == nullptr || b.overflow_ == nullptr) { - return false; - } + auto lhs = buckets_.cbegin(); + auto rhs = in_set.buckets_.cbegin(); - return *a.overflow_ == *b.overflow_; - } + while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { + if (lhs->start == rhs->start) { + if (lhs->data & rhs->data) { + // At least 1 bit is shared. Early return. + return true; + } - friend bool operator!=(const EnumSet& a, const EnumSet& b) { - return !(a == b); - } + lhs++; + rhs++; + continue; + } - // Adds the given enum value to the set. This has no effect if the - // enum value is already in the set. - void Add(EnumType c) { AddWord(ToWord(c)); } + // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. + if (lhs->start < rhs->start) { + lhs++; + continue; + } - // Removes the given enum value from the set. This has no effect if the - // enum value is not in the set. - void Remove(EnumType c) { RemoveWord(ToWord(c)); } - - // Returns true if this enum value is in the set. - bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); } - - // Applies f to each enum in the set, in order from smallest enum - // value to largest. - void ForEach(std::function<void(EnumType)> f) const { - for (uint32_t i = 0; i < 64; ++i) { - if (mask_ & AsMask(i)) f(static_cast<EnumType>(i)); - } - if (overflow_) { - for (uint32_t c : *overflow_) f(static_cast<EnumType>(c)); - } - } - - // Returns true if the set is empty. - bool IsEmpty() const { - if (mask_) return false; - if (overflow_ && !overflow_->empty()) return false; - return true; - } - - // Returns true if the set contains ANY of the elements of |in_set|, - // or if |in_set| is empty. - bool HasAnyOf(const EnumSet<EnumType>& in_set) const { - if (in_set.IsEmpty()) return true; - - if (mask_ & in_set.mask_) return true; - - if (!overflow_ || !in_set.overflow_) return false; - - for (uint32_t item : *in_set.overflow_) { - if (overflow_->find(item) != overflow_->end()) return true; + // Otherwise, RHS needs to catch up on LHS. + rhs++; } return false; } private: - // Adds the given enum value (as a 32-bit word) to the set. This has no - // effect if the enum value is already in the set. - void AddWord(uint32_t word) { - if (auto new_bits = AsMask(word)) { - mask_ |= new_bits; - } else { - Overflow().insert(word); + // Returns the index of the last bucket in which `value` could be stored. + static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { + return static_cast<size_t>(value) / kBucketSize; + } + + // Returns the smallest enum value that could be contained in the same bucket + // as `value`. + static constexpr inline T ComputeBucketStart(T value) { + return static_cast<T>(kBucketSize * + ComputeLargestPossibleBucketIndexFor(value)); + } + + // Returns the index of the bit that corresponds to `value` in the bucket. + static constexpr inline ElementType ComputeBucketOffset(T value) { + return static_cast<ElementType>(value) % kBucketSize; + } + + // Returns the bitmask used to represent the enum `value` in its bucket. + static constexpr inline BucketType ComputeMaskForValue(T value) { + return 1ULL << ComputeBucketOffset(value); + } + + // Returns the `enum` stored in `bucket` at `offset`. + // `offset` is the bit-offset in the bucket storage. + static constexpr inline T GetValueFromBucket(const Bucket& bucket, + BucketType offset) { + return static_cast<T>(static_cast<ElementType>(bucket.start) + offset); + } + + // For a given enum `value`, finds the bucket index that could contain this + // value. If no such bucket is found, the index at which the new bucket should + // be inserted is returned. + size_t FindBucketForValue(T value) const { + // Set is empty, insert at 0. + if (buckets_.size() == 0) { + return 0; } - } - // Removes the given enum value (as a 32-bit word) from the set. This has no - // effect if the enum value is not in the set. - void RemoveWord(uint32_t word) { - if (auto new_bits = AsMask(word)) { - mask_ &= ~new_bits; - } else { - auto itr = Overflow().find(word); - if (itr != Overflow().end()) Overflow().erase(itr); + const T wanted_start = ComputeBucketStart(value); + assert(buckets_.size() > 0 && + "Size must not be 0 here. Has the code above changed?"); + size_t index = std::min(buckets_.size() - 1, + ComputeLargestPossibleBucketIndexFor(value)); + + // This loops behaves like std::upper_bound with a reverse iterator. + // Buckets are sorted. 3 main cases: + // - The bucket matches + // => returns the bucket index. + // - The found bucket is larger + // => scans left until it finds the correct bucket, or insertion point. + // - The found bucket is smaller + // => We are at the end, so we return past-end index for insertion. + for (; buckets_[index].start >= wanted_start; index--) { + if (index == 0) { + return 0; + } } + + return index + 1; } - // Returns true if the enum represented as a 32-bit word is in the set. - bool ContainsWord(uint32_t word) const { - // We shouldn't call Overflow() since this is a const method. - if (auto bits = AsMask(word)) { - return (mask_ & bits) != 0; - } else if (auto overflow = overflow_.get()) { - return overflow->find(word) != overflow->end(); + // Creates a new bucket to store `value` and inserts it at `index`. + // If the `index` is past the end, the bucket is inserted at the end of the + // vector. + void InsertBucketFor(size_t index, T value) { + const T bucket_start = ComputeBucketStart(value); + Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; + auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); +#if defined(NDEBUG) + (void)it; // Silencing unused variable warning. +#else + assert(std::next(it) == buckets_.end() || + std::next(it)->start > bucket_start); + assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); +#endif + } + + // Returns true if the bucket at `bucketIndex/ stores the enum at + // `bucketOffset`, false otherwise. + bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { + assert(bucketIndex < buckets_.size()); + assert(bucketOffset < kBucketSize); + return buckets_[bucketIndex].data & (1ULL << bucketOffset); + } + + // Returns true if `lhs` and `rhs` hold the exact same values. + friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) { + if (lhs.size_ != rhs.size_) { + return false; } - // The word is large, but the set doesn't have large members, so - // it doesn't have an overflow set. - return false; - } - // Returns the enum value as a uint32_t. - uint32_t ToWord(EnumType value) const { - static_assert(sizeof(EnumType) <= sizeof(uint32_t), - "EnumType must statically castable to uint32_t"); - return static_cast<uint32_t>(value); - } - - // Determines whether the given enum value can be represented - // as a bit in a uint64_t mask. If so, then returns that mask bit. - // Otherwise, returns 0. - uint64_t AsMask(uint32_t word) const { - if (word > 63) return 0; - return uint64_t(1) << word; - } - - // Ensures that overflow_set_ references a set. A new empty set is - // allocated if one doesn't exist yet. Returns overflow_set_. - OverflowSetType& Overflow() { - if (overflow_.get() == nullptr) { - overflow_ = MakeUnique<OverflowSetType>(); + if (lhs.buckets_.size() != rhs.buckets_.size()) { + return false; } - return *overflow_; + return lhs.buckets_ == rhs.buckets_; } - // Enums with values up to 63 are stored as bits in this mask. - uint64_t mask_ = 0; - // Enums with values larger than 63 are stored in this set. - // This set should normally be empty or very small. - std::unique_ptr<OverflowSetType> overflow_ = {}; + // Returns true if `lhs` and `rhs` hold at least 1 different value. + friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) { + return !(lhs == rhs); + } + + // Storage for the buckets. + std::vector<Bucket> buckets_; + // How many enums is this set storing. + size_t size_ = 0; }; -// A set of spv::Capability, optimized for small capability values. +// A set of spv::Capability. using CapabilitySet = EnumSet<spv::Capability>; } // namespace spvtools
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 4e27954..9a5ba84 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp
@@ -30,6 +30,7 @@ #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" @@ -62,6 +63,9 @@ {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), @@ -138,6 +142,9 @@ if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; } + if (!strncmp("NonSemantic.VkspReflection.", name, 27)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION; + } // ensure to add any known non-semantic extended instruction sets // above this point, and update spvExtInstIsNonSemantic() if (!strncmp("NonSemantic.", name, 12)) { @@ -149,7 +156,8 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || - type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { + type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION) { return true; } return false;
diff --git a/source/extensions.cpp b/source/extensions.cpp index ebf6bec..ac987fc 100644 --- a/source/extensions.cpp +++ b/source/extensions.cpp
@@ -40,8 +40,9 @@ std::string ExtensionSetToString(const ExtensionSet& extensions) { std::stringstream ss; - extensions.ForEach( - [&ss](Extension ext) { ss << ExtensionToString(ext) << " "; }); + for (auto extension : extensions) { + ss << ExtensionToString(extension) << " "; + } return ss.str(); }
diff --git a/source/extensions.h b/source/extensions.h index 8023444..cda4924 100644 --- a/source/extensions.h +++ b/source/extensions.h
@@ -15,6 +15,7 @@ #ifndef SOURCE_EXTENSIONS_H_ #define SOURCE_EXTENSIONS_H_ +#include <cstdint> #include <string> #include "source/enum_set.h" @@ -23,7 +24,7 @@ namespace spvtools { // The known SPIR-V extensions. -enum Extension { +enum Extension : uint32_t { #include "extension_enum.inc" };
diff --git a/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp index 07a31e5..87393e9 100644 --- a/source/fuzz/transformation_add_no_contraction_decoration.cpp +++ b/source/fuzz/transformation_add_no_contraction_decoration.cpp
@@ -36,6 +36,11 @@ if (!instr) { return false; } + // |instr| must not be decorated with NoContraction. + if (ir_context->get_decoration_mgr()->HasDecoration( + message_.result_id(), spv::Decoration::NoContraction)) { + return false; + } // The instruction must be arithmetic. return IsArithmetic(instr->opcode()); }
diff --git a/source/fuzz/transformation_add_relaxed_decoration.cpp b/source/fuzz/transformation_add_relaxed_decoration.cpp index 6cd4ecb..601546c 100644 --- a/source/fuzz/transformation_add_relaxed_decoration.cpp +++ b/source/fuzz/transformation_add_relaxed_decoration.cpp
@@ -36,6 +36,11 @@ if (!instr) { return false; } + // |instr| must not be decorated with RelaxedPrecision. + if (ir_context->get_decoration_mgr()->HasDecoration( + message_.result_id(), spv::Decoration::RelaxedPrecision)) { + return false; + } opt::BasicBlock* cur_block = ir_context->get_instr_block(instr); // The instruction must have a block. if (cur_block == nullptr) { @@ -46,6 +51,7 @@ cur_block->id()))) { return false; } + // The instruction must be numeric. return IsNumeric(instr->opcode()); }
diff --git a/source/link/linker.cpp b/source/link/linker.cpp index e50391a..58930e4 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp
@@ -91,7 +91,8 @@ // should be non-null. |max_id_bound| should be strictly greater than 0. spv_result_t GenerateHeader(const MessageConsumer& consumer, const std::vector<opt::Module*>& modules, - uint32_t max_id_bound, opt::ModuleHeader* header); + uint32_t max_id_bound, opt::ModuleHeader* header, + const LinkerOptions& options); // Merge all the modules from |in_modules| into a single module owned by // |linked_context|. @@ -202,7 +203,8 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, const std::vector<opt::Module*>& modules, - uint32_t max_id_bound, opt::ModuleHeader* header) { + uint32_t max_id_bound, opt::ModuleHeader* header, + const LinkerOptions& options) { spv_position_t position = {}; if (modules.empty()) @@ -212,10 +214,12 @@ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of GenerateHeader should not be null."; - const uint32_t linked_version = modules.front()->version(); + uint32_t linked_version = modules.front()->version(); for (std::size_t i = 1; i < modules.size(); ++i) { const uint32_t module_version = modules[i]->version(); - if (module_version != linked_version) + if (options.GetUseHighestVersion()) { + linked_version = std::max(linked_version, module_version); + } else if (module_version != linked_version) { return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL) << "Conflicting SPIR-V versions: " << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "." @@ -224,6 +228,7 @@ << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "." << SPV_SPIRV_VERSION_MINOR_PART(module_version) << " (input module " << (i + 1) << ")."; + } } header->magic_number = spv::MagicNumber; @@ -753,7 +758,7 @@ // Phase 2: Generate the header opt::ModuleHeader header; - res = GenerateHeader(consumer, modules, max_id_bound, &header); + res = GenerateHeader(consumer, modules, max_id_bound, &header, options); if (res != SPV_SUCCESS) return res; IRContext linked_context(c_context->target_env, consumer); linked_context.module()->SetHeader(header);
diff --git a/source/opcode.cpp b/source/opcode.cpp index d26024a..787dbb3 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp
@@ -274,6 +274,7 @@ case spv::Op::OpTypeArray: case spv::Op::OpTypeStruct: case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: return true; default: return false; @@ -294,6 +295,7 @@ case spv::Op::OpPtrAccessChain: case spv::Op::OpLoad: case spv::Op::OpConstantNull: + case spv::Op::OpRawAccessChainNV: return true; default: return false; @@ -308,6 +310,7 @@ case spv::Op::OpFunctionParameter: case spv::Op::OpImageTexelPointer: case spv::Op::OpCopyObject: + case spv::Op::OpRawAccessChainNV: return true; default: return false; @@ -340,6 +343,7 @@ case spv::Op::OpTypeNamedBarrier: case spv::Op::OpTypeAccelerationStructureNV: case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: // case spv::Op::OpTypeAccelerationStructureKHR: covered by // spv::Op::OpTypeAccelerationStructureNV case spv::Op::OpTypeRayQueryKHR: @@ -532,6 +536,8 @@ case spv::Op::OpGroupNonUniformQuadBroadcast: case spv::Op::OpGroupNonUniformQuadSwap: case spv::Op::OpGroupNonUniformRotateKHR: + case spv::Op::OpGroupNonUniformQuadAllKHR: + case spv::Op::OpGroupNonUniformQuadAnyKHR: return true; default: return false; @@ -750,6 +756,7 @@ case spv::Op::OpInBoundsAccessChain: case spv::Op::OpPtrAccessChain: case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpRawAccessChainNV: return true; default: return false;
diff --git a/source/operand.cpp b/source/operand.cpp index 31a6c59..7848846 100644 --- a/source/operand.cpp +++ b/source/operand.cpp
@@ -26,7 +26,6 @@ #include "source/macro.h" #include "source/opcode.h" #include "source/spirv_constant.h" -#include "source/spirv_target_env.h" // For now, assume unified1 contains up to SPIR-V 1.3 and no later // SPIR-V version. @@ -48,7 +47,7 @@ return SPV_SUCCESS; } -spv_result_t spvOperandTableNameLookup(spv_target_env env, +spv_result_t spvOperandTableNameLookup(spv_target_env, const spv_operand_table table, const spv_operand_type_t type, const char* name, @@ -57,31 +56,18 @@ if (!table) return SPV_ERROR_INVALID_TABLE; if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; - const auto version = spvVersionForTargetEnv(env); 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 - // 1. The target environment satisfies the minimal requirement of the - // operand; or - // 2. There is at least one extension enabling this operand; or - // 3. There is at least one capability enabling this operand. - // - // Note that the second rule assumes the extension enabling this operand - // is indeed requested in the SPIR-V code; checking that should be - // validator's work. + // it is in the grammar. It might not be *valid* to use, + // but that should be checked by the validator, not by parsing. if (nameLength == strlen(entry.name) && !strncmp(entry.name, name, nameLength)) { - if ((version >= entry.minVersion && version <= entry.lastVersion) || - entry.numExtensions > 0u || entry.numCapabilities > 0u) { - *pEntry = &entry; - return SPV_SUCCESS; - } else { - // if there is no extension/capability then the version is wrong - return SPV_ERROR_WRONG_VERSION; - } + *pEntry = &entry; + return SPV_SUCCESS; } } } @@ -89,7 +75,7 @@ return SPV_ERROR_INVALID_LOOKUP; } -spv_result_t spvOperandTableValueLookup(spv_target_env env, +spv_result_t spvOperandTableValueLookup(spv_target_env, const spv_operand_table table, const spv_operand_type_t type, const uint32_t value, @@ -110,33 +96,15 @@ const auto beg = group.entries; const auto end = group.entries + group.count; - // We need to loop here because there can exist multiple symbols for the - // same operand value, and they can be introduced in different target - // environments, which means they can have different minimal version - // requirements. For example, SubgroupEqMaskKHR can exist in any SPIR-V - // version as long as the SPV_KHR_shader_ballot extension is there; but - // starting from SPIR-V 1.3, SubgroupEqMask, which has the same numeric - // value as SubgroupEqMaskKHR, is available in core SPIR-V without extension - // 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->value == value; ++it) { - // We consider the current operand as available as long as - // 1. The target environment satisfies the minimal requirement of the - // operand; or - // 2. There is at least one extension enabling this operand; or - // 3. There is at least one capability enabling this operand. - // - // Note that the second rule assumes the extension enabling this operand - // 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; - } + 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; } } @@ -155,6 +123,7 @@ case SPV_OPERAND_TYPE_LITERAL_INTEGER: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_LITERAL_FLOAT: return "literal number"; case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: return "possibly multi-word literal integer"; @@ -236,6 +205,26 @@ case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: return "packed vector format"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: + return "cooperative matrix operands"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT: + return "cooperative matrix layout"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE: + return "cooperative matrix use"; + case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER: + return "initialization mode qualifier"; + case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER: + return "host access qualifier"; + case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL: + return "load cache control"; + case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL: + return "store cache control"; + case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: + return "named maximum number of registers"; + case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: + return "raw access chain operands"; case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: return "image"; @@ -325,6 +314,7 @@ } switch (type) { case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_LITERAL_FLOAT: case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: @@ -369,6 +359,13 @@ case SPV_OPERAND_TYPE_QUANTIZATION_MODES: case SPV_OPERAND_TYPE_OVERFLOW_MODES: case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE: + case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER: + case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL: + case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL: + case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: return true; default: break; @@ -387,6 +384,8 @@ case SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE: 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_RAW_ACCESS_CHAIN_OPERANDS: return true; default: break; @@ -405,7 +404,9 @@ case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: case SPV_OPERAND_TYPE_OPTIONAL_CIV: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: return true; default: break;
diff --git a/source/operand.h b/source/operand.h index a3010d9..f74c933 100644 --- a/source/operand.h +++ b/source/operand.h
@@ -57,12 +57,6 @@ // Gets the name string of the non-variable operand type. const char* spvOperandTypeStr(spv_operand_type_t type); -// Returns true if the given type is concrete. -bool spvOperandIsConcrete(spv_operand_type_t type); - -// Returns true if the given type is concrete and also a mask. -bool spvOperandIsConcreteMask(spv_operand_type_t type); - // Returns true if an operand of the given type is optional. bool spvOperandIsOptional(spv_operand_type_t type);
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index eea3c47..4e7d92d 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt
@@ -71,6 +71,7 @@ instruction_list.h instrument_pass.h interface_var_sroa.h + invocation_interlock_placement_pass.h interp_fixup_pass.h ir_builder.h ir_context.h @@ -93,6 +94,7 @@ loop_unswitch_pass.h mem_pass.h merge_return_pass.h + modify_maximal_reconvergence.h module.h null_pass.h passes.h @@ -121,7 +123,9 @@ strip_debug_info_pass.h strip_nonsemantic_info_pass.h struct_cfg_analysis.h + switch_descriptorset_pass.h tree_iterator.h + trim_capabilities_pass.h type_manager.h types.h unify_const_pass.h @@ -189,6 +193,7 @@ instruction_list.cpp instrument_pass.cpp interface_var_sroa.cpp + invocation_interlock_placement_pass.cpp interp_fixup_pass.cpp ir_context.cpp ir_loader.cpp @@ -210,6 +215,7 @@ loop_unswitch_pass.cpp mem_pass.cpp merge_return_pass.cpp + modify_maximal_reconvergence.cpp module.cpp optimizer.cpp pass.cpp @@ -236,6 +242,8 @@ strip_debug_info_pass.cpp strip_nonsemantic_info_pass.cpp struct_cfg_analysis.cpp + switch_descriptorset_pass.cpp + trim_capabilities_pass.cpp type_manager.cpp types.cpp unify_const_pass.cpp
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 1645638..4737da5 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -438,6 +438,9 @@ const Instruction* inst) { assert(inst->opcode() == spv::Op::OpFunctionCall); std::vector<uint32_t> live_variables; + // NOTE: we should only be checking function call parameters here, not the + // function itself, however, `IsPtr` will trivially return false for + // OpFunction inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) { if (!IsPtr(*operand_id)) return; uint32_t var_id = GetVariableId(*operand_id); @@ -938,6 +941,8 @@ void AggressiveDCEPass::InitExtensions() { extensions_allowlist_.clear(); + + // clang-format off extensions_allowlist_.insert({ "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_trinary_minmax", @@ -980,11 +985,13 @@ "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_vulkan_memory_model", @@ -994,7 +1001,12 @@ "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", "SPV_KHR_fragment_shader_barycentric", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_NV_compute_shader_derivatives" }); + // clang-format on } Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) {
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp index fe23e36..42f695f 100644 --- a/source/opt/block_merge_util.cpp +++ b/source/opt/block_merge_util.cpp
@@ -98,6 +98,17 @@ return false; } + // Note: This means that the instructions in a break block will execute as if + // they were still diverged according to the loop iteration. This restricts + // potential transformations an implementation may perform on the IR to match + // shader author expectations. Similarly, instructions in the loop construct + // cannot be moved into the continue construct unless it can be proven that + // invocations are always converged. + if (succ_is_merge && context->get_feature_mgr()->HasExtension( + kSPV_KHR_maximal_reconvergence)) { + return false; + } + if (pred_is_merge && IsContinue(context, lab_id)) { // Cannot merge a continue target with a merge block. return false;
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index 2610808..17900af 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp
@@ -21,6 +21,59 @@ namespace { constexpr uint32_t kExtractCompositeIdInIdx = 0; +// Returns the value obtained by extracting the |number_of_bits| least +// significant bits from |value|, and sign-extending it to 64-bits. +uint64_t SignExtendValue(uint64_t value, uint32_t number_of_bits) { + if (number_of_bits == 64) return value; + + uint64_t mask_for_sign_bit = 1ull << (number_of_bits - 1); + uint64_t mask_for_significant_bits = (mask_for_sign_bit << 1) - 1ull; + if (value & mask_for_sign_bit) { + // Set upper bits to 1 + value |= ~mask_for_significant_bits; + } else { + // Clear the upper bits + value &= mask_for_significant_bits; + } + return value; +} + +// Returns the value obtained by extracting the |number_of_bits| least +// significant bits from |value|, and zero-extending it to 64-bits. +uint64_t ZeroExtendValue(uint64_t value, uint32_t number_of_bits) { + if (number_of_bits == 64) return value; + + uint64_t mask_for_first_bit_to_clear = 1ull << (number_of_bits); + uint64_t mask_for_bits_to_keep = mask_for_first_bit_to_clear - 1; + value &= mask_for_bits_to_keep; + return value; +} + +// Returns a constant whose value is `value` and type is `type`. This constant +// will be generated by `const_mgr`. The type must be a scalar integer type. +const analysis::Constant* GenerateIntegerConstant( + const analysis::Integer* integer_type, uint64_t result, + analysis::ConstantManager* const_mgr) { + assert(integer_type != nullptr); + + std::vector<uint32_t> words; + if (integer_type->width() == 64) { + // In the 64-bit case, two words are needed to represent the value. + words = {static_cast<uint32_t>(result), + static_cast<uint32_t>(result >> 32)}; + } else { + // In all other cases, only a single word is needed. + assert(integer_type->width() <= 32); + if (integer_type->IsSigned()) { + result = SignExtendValue(result, integer_type->width()); + } else { + result = ZeroExtendValue(result, integer_type->width()); + } + words = {static_cast<uint32_t>(result)}; + } + return const_mgr->GetConstant(integer_type, words); +} + // Returns a constants with the value NaN of the given type. Only works for // 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. const analysis::Constant* GetNan(const analysis::Type* type, @@ -88,6 +141,22 @@ return nullptr; } +// Returns a constants with the value |-val| of the given type. +const analysis::Constant* NegateIntConst(const analysis::Type* result_type, + const analysis::Constant* val, + analysis::ConstantManager* const_mgr) { + const analysis::Integer* int_type = result_type->AsInteger(); + assert(int_type != nullptr); + + if (val->AsNullConstant()) { + return val; + } + + uint64_t new_value = static_cast<uint64_t>(-val->GetSignExtendedValue()); + return const_mgr->GetIntConst(new_value, int_type->width(), + int_type->IsSigned()); +} + // Folds an OpcompositeExtract where input is a composite constant. ConstantFoldingRule FoldExtractWithConstants() { return [](IRContext* context, Instruction* inst, @@ -341,6 +410,69 @@ }; } +// Returns to the constant that results from tranposing |matrix|. The result +// will have type |result_type|, and |matrix| must exist in |context|. The +// result constant will also exist in |context|. +const analysis::Constant* TransposeMatrix(const analysis::Constant* matrix, + analysis::Matrix* result_type, + IRContext* context) { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + if (matrix->AsNullConstant() != nullptr) { + return const_mgr->GetNullCompositeConstant(result_type); + } + + const auto& columns = matrix->AsMatrixConstant()->GetComponents(); + uint32_t number_of_rows = columns[0]->type()->AsVector()->element_count(); + + // Collect the ids of the elements in their new positions. + std::vector<std::vector<uint32_t>> result_elements(number_of_rows); + for (const analysis::Constant* column : columns) { + if (column->AsNullConstant()) { + column = const_mgr->GetNullCompositeConstant(column->type()); + } + const auto& column_components = column->AsVectorConstant()->GetComponents(); + + for (uint32_t row = 0; row < number_of_rows; ++row) { + result_elements[row].push_back( + const_mgr->GetDefiningInstruction(column_components[row]) + ->result_id()); + } + } + + // Create the constant for each row in the result, and collect the ids. + std::vector<uint32_t> result_columns(number_of_rows); + for (uint32_t col = 0; col < number_of_rows; ++col) { + auto* element = const_mgr->GetConstant(result_type->element_type(), + result_elements[col]); + result_columns[col] = + const_mgr->GetDefiningInstruction(element)->result_id(); + } + + // Create the matrix constant from the row ids, and return it. + return const_mgr->GetConstant(result_type, result_columns); +} + +const analysis::Constant* FoldTranspose( + IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == spv::Op::OpTranspose); + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* matrix = constants[0]; + if (matrix == nullptr) { + return nullptr; + } + + auto* result_type = type_mgr->GetType(inst->type_id()); + return TransposeMatrix(matrix, result_type->AsMatrix(), context); +} + ConstantFoldingRule FoldVectorTimesMatrix() { return [](IRContext* context, Instruction* inst, const std::vector<const analysis::Constant*>& constants) @@ -376,13 +508,7 @@ assert(c1->type()->AsVector()->element_type() == element_type && c2->type()->AsMatrix()->element_type() == vector_type); - // Get a float vector that is the result of vector-times-matrix. - std::vector<const analysis::Constant*> c1_components = - c1->GetVectorComponents(const_mgr); - std::vector<const analysis::Constant*> c2_components = - c2->AsMatrixConstant()->GetComponents(); uint32_t resultVectorSize = result_type->AsVector()->element_count(); - std::vector<uint32_t> ids; if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { @@ -395,6 +521,12 @@ return const_mgr->GetConstant(vector_type, ids); } + // Get a float vector that is the result of vector-times-matrix. + std::vector<const analysis::Constant*> c1_components = + c1->GetVectorComponents(const_mgr); + std::vector<const analysis::Constant*> c2_components = + c2->AsMatrixConstant()->GetComponents(); + if (float_type->width() == 32) { for (uint32_t i = 0; i < resultVectorSize; ++i) { float result_scalar = 0.0f; @@ -472,13 +604,7 @@ assert(c1->type()->AsMatrix()->element_type() == vector_type); assert(c2->type()->AsVector()->element_type() == element_type); - // Get a float vector that is the result of matrix-times-vector. - std::vector<const analysis::Constant*> c1_components = - c1->AsMatrixConstant()->GetComponents(); - std::vector<const analysis::Constant*> c2_components = - c2->GetVectorComponents(const_mgr); uint32_t resultVectorSize = result_type->AsVector()->element_count(); - std::vector<uint32_t> ids; if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { @@ -491,6 +617,12 @@ return const_mgr->GetConstant(vector_type, ids); } + // Get a float vector that is the result of matrix-times-vector. + std::vector<const analysis::Constant*> c1_components = + c1->AsMatrixConstant()->GetComponents(); + std::vector<const analysis::Constant*> c2_components = + c2->GetVectorComponents(const_mgr); + if (float_type->width() == 32) { for (uint32_t i = 0; i < resultVectorSize; ++i) { float result_scalar = 0.0f; @@ -587,13 +719,13 @@ const analysis::Type* result_type, const analysis::Constant* a, const analysis::Constant* b, analysis::ConstantManager*)>; -// Returns a |ConstantFoldingRule| that folds unary floating point scalar ops -// using |scalar_rule| and unary float point vectors ops by applying +// Returns a |ConstantFoldingRule| that folds unary scalar ops +// using |scalar_rule| and unary vectors ops by applying // |scalar_rule| to the elements of the vector. The |ConstantFoldingRule| // that is returned assumes that |constants| contains 1 entry. If they are // not |nullptr|, then their type is either |Float| or |Integer| or a |Vector| // whose element type is |Float| or |Integer|. -ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { +ConstantFoldingRule FoldUnaryOp(UnaryScalarFoldingRule scalar_rule) { return [scalar_rule](IRContext* context, Instruction* inst, const std::vector<const analysis::Constant*>& constants) -> const analysis::Constant* { @@ -602,10 +734,6 @@ const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); const analysis::Vector* vector_type = result_type->AsVector(); - if (!inst->IsFloatingPointFoldingAllowed()) { - return nullptr; - } - const analysis::Constant* arg = (inst->opcode() == spv::Op::OpExtInst) ? constants[1] : constants[0]; @@ -640,6 +768,83 @@ }; } +// Returns a |ConstantFoldingRule| that folds binary scalar ops +// using |scalar_rule| and binary vectors ops by applying +// |scalar_rule| to the elements of the vector. The folding rule assumes that op +// has two inputs. For regular instruction, those are in operands 0 and 1. For +// extended instruction, they are in operands 1 and 2. If an element in +// |constants| is not nullprt, then the constant's type is |Float|, |Integer|, +// or |Vector| whose element type is |Float| or |Integer|. +ConstantFoldingRule FoldBinaryOp(BinaryScalarFoldingRule scalar_rule) { + return [scalar_rule](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) + -> const analysis::Constant* { + assert(constants.size() == inst->NumInOperands()); + assert(constants.size() == (inst->opcode() == spv::Op::OpExtInst ? 3 : 2)); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + + const analysis::Constant* arg1 = + (inst->opcode() == spv::Op::OpExtInst) ? constants[1] : constants[0]; + const analysis::Constant* arg2 = + (inst->opcode() == spv::Op::OpExtInst) ? constants[2] : constants[1]; + + if (arg1 == nullptr || arg2 == nullptr) { + return nullptr; + } + + if (vector_type == nullptr) { + return scalar_rule(result_type, arg1, arg2, const_mgr); + } + + std::vector<const analysis::Constant*> a_components; + std::vector<const analysis::Constant*> b_components; + std::vector<const analysis::Constant*> results_components; + + a_components = arg1->GetVectorComponents(const_mgr); + b_components = arg2->GetVectorComponents(const_mgr); + assert(a_components.size() == b_components.size()); + + // Fold each component of the vector. + for (uint32_t i = 0; i < a_components.size(); ++i) { + results_components.push_back(scalar_rule(vector_type->element_type(), + a_components[i], b_components[i], + const_mgr)); + if (results_components[i] == nullptr) { + return nullptr; + } + } + + // 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()); + } + return const_mgr->GetConstant(vector_type, ids); + }; +} + +// Returns a |ConstantFoldingRule| that folds unary floating point scalar ops +// using |scalar_rule| and unary float point vectors ops by applying +// |scalar_rule| to the elements of the vector. The |ConstantFoldingRule| +// that is returned assumes that |constants| contains 1 entry. If they are +// not |nullptr|, then their type is either |Float| or |Integer| or a |Vector| +// whose element type is |Float| or |Integer|. +ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { + auto folding_rule = FoldUnaryOp(scalar_rule); + return [folding_rule](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) + -> const analysis::Constant* { + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + return folding_rule(context, inst, constants); + }; +} + // Returns the result of folding the constants in |constants| according the // |scalar_rule|. If |result_type| is a vector, then |scalar_rule| is applied // per component. @@ -872,6 +1077,11 @@ return FoldFPScalarDivideByZero(result_type, numerator, const_mgr); } + uint32_t width = denominator->type()->AsFloat()->width(); + if (width != 32 && width != 64) { + return nullptr; + } + const analysis::FloatConstant* denominator_float = denominator->AsFloatConstant(); if (denominator_float && denominator->GetValueAsDouble() == -0.0) { @@ -1042,18 +1252,8 @@ }; } -// This function defines a |UnaryScalarFoldingRule| that subtracts the constant -// from zero. -UnaryScalarFoldingRule FoldFNegateOp() { - return [](const analysis::Type* result_type, const analysis::Constant* a, - analysis::ConstantManager* const_mgr) -> const analysis::Constant* { - assert(result_type != nullptr && a != nullptr); - assert(result_type == a->type()); - return NegateFPConst(result_type, a, const_mgr); - }; -} - -ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(FoldFNegateOp()); } +ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(NegateFPConst); } +ConstantFoldingRule FoldSNegate() { return FoldUnaryOp(NegateIntConst); } ConstantFoldingRule FoldFClampFeedingCompare(spv::Op cmp_opcode) { return [cmp_opcode](IRContext* context, Instruction* inst, @@ -1497,6 +1697,74 @@ return nullptr; }; } + +enum Sign { Signed, Unsigned }; + +// Returns a BinaryScalarFoldingRule that applies `op` to the scalars. +// The `signedness` is used to determine if the operands should be interpreted +// as signed or unsigned. If the operands are signed, the value will be sign +// extended before the value is passed to `op`. Otherwise the values will be +// zero extended. +template <Sign signedness> +BinaryScalarFoldingRule FoldBinaryIntegerOperation(uint64_t (*op)(uint64_t, + uint64_t)) { + return + [op](const analysis::Type* result_type, const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr && b != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + assert(integer_type != nullptr); + assert(a->type()->kind() == analysis::Type::kInteger); + assert(b->type()->kind() == analysis::Type::kInteger); + assert(integer_type->width() == a->type()->AsInteger()->width()); + assert(integer_type->width() == b->type()->AsInteger()->width()); + + // In SPIR-V, all operations support unsigned types, but the way they + // are interpreted depends on the opcode. This is why we use the + // template argument to determine how to interpret the operands. + uint64_t ia = (signedness == Signed ? a->GetSignExtendedValue() + : a->GetZeroExtendedValue()); + uint64_t ib = (signedness == Signed ? b->GetSignExtendedValue() + : b->GetZeroExtendedValue()); + uint64_t result = op(ia, ib); + + const analysis::Constant* result_constant = + GenerateIntegerConstant(integer_type, result, const_mgr); + return result_constant; + }; +} + +// A scalar folding rule that folds OpSConvert. +const analysis::Constant* FoldScalarSConvert( + const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) { + assert(result_type != nullptr); + assert(a != nullptr); + assert(const_mgr != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + assert(integer_type && "The result type of an SConvert"); + int64_t value = a->GetSignExtendedValue(); + return GenerateIntegerConstant(integer_type, value, const_mgr); +} + +// A scalar folding rule that folds OpUConvert. +const analysis::Constant* FoldScalarUConvert( + const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) { + assert(result_type != nullptr); + assert(a != nullptr); + assert(const_mgr != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + assert(integer_type && "The result type of an UConvert"); + uint64_t value = a->GetZeroExtendedValue(); + + // If the operand was an unsigned value with less than 32-bit, it would have + // been sign extended earlier, and we need to clear those bits. + auto* operand_type = a->type()->AsInteger(); + value = ZeroExtendValue(value, operand_type->width()); + return GenerateIntegerConstant(integer_type, value, const_mgr); +} } // namespace void ConstantFoldingRules::AddFoldingRules() { @@ -1514,6 +1782,8 @@ rules_[spv::Op::OpConvertFToU].push_back(FoldFToI()); rules_[spv::Op::OpConvertSToF].push_back(FoldIToF()); rules_[spv::Op::OpConvertUToF].push_back(FoldIToF()); + rules_[spv::Op::OpSConvert].push_back(FoldUnaryOp(FoldScalarSConvert)); + rules_[spv::Op::OpUConvert].push_back(FoldUnaryOp(FoldScalarUConvert)); rules_[spv::Op::OpDot].push_back(FoldOpDotWithConstants()); rules_[spv::Op::OpFAdd].push_back(FoldFAdd()); @@ -1566,10 +1836,52 @@ rules_[spv::Op::OpVectorTimesScalar].push_back(FoldVectorTimesScalar()); rules_[spv::Op::OpVectorTimesMatrix].push_back(FoldVectorTimesMatrix()); rules_[spv::Op::OpMatrixTimesVector].push_back(FoldMatrixTimesVector()); + rules_[spv::Op::OpTranspose].push_back(FoldTranspose); rules_[spv::Op::OpFNegate].push_back(FoldFNegate()); + rules_[spv::Op::OpSNegate].push_back(FoldSNegate()); rules_[spv::Op::OpQuantizeToF16].push_back(FoldQuantizeToF16()); + 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::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::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::OpUMod].push_back( + FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>( + [](uint64_t a, uint64_t b) { return (b != 0 ? a % b : 0); }))); + + rules_[spv::Op::OpSRem].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::OpSMod].push_back(FoldBinaryOp( + FoldBinaryIntegerOperation<Signed>([](uint64_t a, uint64_t b) { + if (b == 0) return static_cast<uint64_t>(0ull); + + int64_t signed_a = static_cast<int64_t>(a); + int64_t signed_b = static_cast<int64_t>(b); + int64_t result = signed_a % signed_b; + if ((signed_b < 0) != (result < 0)) result += signed_b; + return static_cast<uint64_t>(result); + }))); + // Add rules for GLSLstd450 FeatureManager* feature_manager = context_->get_feature_mgr(); uint32_t ext_inst_glslstd450_id =
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index 9b4c89a..6eebbb5 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp
@@ -435,6 +435,8 @@ words_per_element = float_type->width() / 32; else if (const auto* int_type = element_type->AsInteger()) words_per_element = int_type->width() / 32; + else if (element_type->AsBool() != nullptr) + words_per_element = 1; if (words_per_element != 1 && words_per_element != 2) return nullptr; @@ -487,6 +489,31 @@ return GetDefiningInstruction(c)->result_id(); } +const Constant* ConstantManager::GetIntConst(uint64_t val, int32_t bitWidth, + bool isSigned) { + Type* int_type = context()->get_type_mgr()->GetIntType(bitWidth, isSigned); + + if (isSigned) { + // Sign extend the value. + int32_t num_of_bit_to_ignore = 64 - bitWidth; + val = static_cast<int64_t>(val << num_of_bit_to_ignore) >> + num_of_bit_to_ignore; + } else if (bitWidth < 64) { + // Clear the upper bit that are not used. + uint64_t mask = ((1ull << bitWidth) - 1); + val &= mask; + } + + if (bitWidth <= 32) { + return GetConstant(int_type, {static_cast<uint32_t>(val)}); + } + + // If the value is more than 32-bit, we need to split the operands into two + // 32-bit integers. + return GetConstant( + int_type, {static_cast<uint32_t>(val), static_cast<uint32_t>(val >> 32)}); +} + uint32_t ConstantManager::GetUIntConstId(uint32_t val) { Type* uint_type = context()->get_type_mgr()->GetUIntType(); const Constant* c = GetConstant(uint_type, {val});
diff --git a/source/opt/constants.h b/source/opt/constants.h index 410304e..ae8dc62 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h
@@ -659,6 +659,12 @@ // Returns the id of a 32-bit signed integer constant with value |val|. uint32_t GetSIntConstId(int32_t val); + // Returns an integer constant with `bitWidth` and value |val|. If `isSigned` + // is true, the constant will be a signed integer. Otherwise it will be + // unsigned. Only the `bitWidth` lower order bits of |val| will be used. The + // rest will be ignored. + const Constant* GetIntConst(uint64_t val, int32_t bitWidth, bool isSigned); + // Returns the id of a 32-bit unsigned integer constant with value |val|. uint32_t GetUIntConstId(uint32_t val);
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index 2c4a631..e243bed 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp
@@ -63,6 +63,10 @@ void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); } +bool ConvertToHalfPass::CanRelaxOpOperands(Instruction* inst) { + return image_ops_.count(inst->opcode()) == 0; +} + analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) { analysis::Float float_ty(width); return context()->get_type_mgr()->GetRegisteredType(&float_ty); @@ -167,6 +171,19 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) { bool modified = false; + // If this is a OpCompositeExtract instruction and has a struct operand, we + // should not relax this instruction. Doing so could cause a mismatch between + // the result type and the struct member type. + bool hasStructOperand = false; + if (inst->opcode() == spv::Op::OpCompositeExtract) { + inst->ForEachInId([&hasStructOperand, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (IsStruct(op_inst)) hasStructOperand = true; + }); + if (hasStructOperand) { + return false; + } + } // Convert all float32 based operands to float16 equivalent and change // instruction type to float16 equivalent. inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { @@ -299,12 +316,19 @@ if (closure_ops_.count(inst->opcode()) == 0) return false; // Can relax if all float operands are relaxed bool relax = true; - inst->ForEachInId([&relax, this](uint32_t* idp) { + bool hasStructOperand = false; + inst->ForEachInId([&relax, &hasStructOperand, this](uint32_t* idp) { Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); - if (IsStruct(op_inst)) relax = false; + if (IsStruct(op_inst)) hasStructOperand = true; if (!IsFloat(op_inst, 32)) return; if (!IsRelaxed(*idp)) relax = false; }); + // If the instruction has a struct operand, we should not relax it, even if + // all its uses are relaxed. Doing so could cause a mismatch between the + // result type and the struct member type. + if (hasStructOperand) { + return false; + } if (relax) { AddRelaxed(inst->result_id()); return true; @@ -313,7 +337,8 @@ relax = true; get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) { if (uinst->result_id() == 0 || !IsFloat(uinst, 32) || - (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) { + (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id())) || + !CanRelaxOpOperands(uinst)) { relax = false; return; }
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h index 24a478f..8e10c4f 100644 --- a/source/opt/convert_to_half_pass.h +++ b/source/opt/convert_to_half_pass.h
@@ -56,6 +56,9 @@ // Add |id| to the relaxed id set void AddRelaxed(uint32_t id); + // Return true if the instruction's operands can be relaxed + bool CanRelaxOpOperands(Instruction* inst); + // Return type id for float with |width| analysis::Type* FloatScalarType(uint32_t width); @@ -133,13 +136,13 @@ // Set of 450 extension operations to be processed std::unordered_set<uint32_t> target_ops_450_; - // Set of sample operations + // Set of all sample operations, including dref and non-dref operations std::unordered_set<spv::Op, hasher> image_ops_; - // Set of dref sample operations + // Set of only dref sample operations std::unordered_set<spv::Op, hasher> dref_image_ops_; - // Set of dref sample operations + // Set of operations that can be marked as relaxed std::unordered_set<spv::Op, hasher> closure_ops_; // Set of ids of all relaxed instructions
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp index 66a268f..c2bea8a 100644 --- a/source/opt/copy_prop_arrays.cpp +++ b/source/opt/copy_prop_arrays.cpp
@@ -35,6 +35,32 @@ dbg_opcode == CommonDebugInfoDebugValue; } +// Returns the number of members in |type|. If |type| is not a composite type +// or the number of components is not known at compile time, the return value +// will be 0. +uint32_t GetNumberOfMembers(const analysis::Type* type, IRContext* context) { + if (const analysis::Struct* struct_type = type->AsStruct()) { + return static_cast<uint32_t>(struct_type->element_types().size()); + } else if (const analysis::Array* array_type = type->AsArray()) { + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant( + array_type->LengthId()); + + if (length_const == nullptr) { + // This can happen if the length is an OpSpecConstant. + return 0; + } + assert(length_const->type()->AsInteger()); + return length_const->GetU32(); + } else if (const analysis::Vector* vector_type = type->AsVector()) { + return vector_type->element_count(); + } else if (const analysis::Matrix* matrix_type = type->AsMatrix()) { + return matrix_type->element_count(); + } else { + return 0; + } +} + } // namespace Pass::Status CopyPropagateArrays::Process() { @@ -357,22 +383,9 @@ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); const analysis::Type* result_type = type_mgr->GetType(insert_inst->type_id()); - uint32_t number_of_elements = 0; - if (const analysis::Struct* struct_type = result_type->AsStruct()) { - number_of_elements = - static_cast<uint32_t>(struct_type->element_types().size()); - } else if (const analysis::Array* array_type = result_type->AsArray()) { - const analysis::Constant* length_const = - const_mgr->FindDeclaredConstant(array_type->LengthId()); - number_of_elements = length_const->GetU32(); - } else if (const analysis::Vector* vector_type = result_type->AsVector()) { - number_of_elements = vector_type->element_count(); - } else if (const analysis::Matrix* matrix_type = result_type->AsMatrix()) { - number_of_elements = matrix_type->element_count(); - } + uint32_t number_of_elements = GetNumberOfMembers(result_type, context()); if (number_of_elements == 0) { return nullptr; @@ -800,23 +813,8 @@ std::vector<uint32_t> access_indices = GetAccessIds(); type = type_mgr->GetMemberType(type, access_indices); - if (const analysis::Struct* struct_type = type->AsStruct()) { - return static_cast<uint32_t>(struct_type->element_types().size()); - } else if (const analysis::Array* array_type = type->AsArray()) { - const analysis::Constant* length_const = - context->get_constant_mgr()->FindDeclaredConstant( - array_type->LengthId()); - assert(length_const->type()->AsInteger()); - return length_const->GetU32(); - } else if (const analysis::Vector* vector_type = type->AsVector()) { - return vector_type->element_count(); - } else if (const analysis::Matrix* matrix_type = type->AsMatrix()) { - return matrix_type->element_count(); - } else { - return 0; - } + return opt::GetNumberOfMembers(type, context); } - template <class iterator> CopyPropagateArrays::MemoryObject::MemoryObject(Instruction* var_inst, iterator begin, iterator end)
diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h index 7486f80..c6ca7d2 100644 --- a/source/opt/copy_prop_arrays.h +++ b/source/opt/copy_prop_arrays.h
@@ -101,7 +101,8 @@ bool IsMember() const { return !access_chain_.empty(); } // Returns the number of members in the object represented by |this|. If - // |this| does not represent a composite type, the return value will be 0. + // |this| does not represent a composite type or the number of components is + // not known at compile time, the return value will be 0. uint32_t GetNumberOfMembers(); // Returns the owning variable that the memory object is contained in. @@ -207,7 +208,7 @@ // Returns the memory object that at some point was equivalent to the result // of |insert_inst|. If a memory object cannot be identified, the return - // value is |nullptr\. The opcode of |insert_inst| must be + // value is |nullptr|. The opcode of |insert_inst| must be // |OpCompositeInsert|. This function looks for a series of // |OpCompositeInsert| instructions that insert the elements one at a time in // order from beginning to end.
diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp index a486903..f985e4c 100644 --- a/source/opt/dead_insert_elim_pass.cpp +++ b/source/opt/dead_insert_elim_pass.cpp
@@ -213,7 +213,8 @@ } break; default: { // Mark inserts in chain for all components - MarkInsertChain(&*ii, nullptr, 0, nullptr); + std::unordered_set<uint32_t> visited_phis; + MarkInsertChain(&*ii, nullptr, 0, &visited_phis); } break; } });
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 1393d48..3e95dbc 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp
@@ -461,7 +461,7 @@ bool DecorationManager::WhileEachDecoration( uint32_t id, uint32_t decoration, - std::function<bool(const Instruction&)> f) { + std::function<bool(const Instruction&)> f) const { for (const Instruction* inst : GetDecorationsFor(id, true)) { switch (inst->opcode()) { case spv::Op::OpMemberDecorate: @@ -485,14 +485,19 @@ void DecorationManager::ForEachDecoration( uint32_t id, uint32_t decoration, - std::function<void(const Instruction&)> f) { + std::function<void(const Instruction&)> f) const { WhileEachDecoration(id, decoration, [&f](const Instruction& inst) { f(inst); return true; }); } -bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) { +bool DecorationManager::HasDecoration(uint32_t id, + spv::Decoration decoration) const { + return HasDecoration(id, static_cast<uint32_t>(decoration)); +} + +bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) const { bool has_decoration = false; ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) { has_decoration = true;
diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h index 1a0d1b1..2be016a 100644 --- a/source/opt/decoration_manager.h +++ b/source/opt/decoration_manager.h
@@ -92,20 +92,21 @@ // Returns whether a decoration instruction for |id| with decoration // |decoration| exists or not. - bool HasDecoration(uint32_t id, uint32_t decoration); + bool HasDecoration(uint32_t id, uint32_t decoration) const; + bool HasDecoration(uint32_t id, spv::Decoration decoration) const; // |f| is run on each decoration instruction for |id| with decoration // |decoration|. Processed are all decorations which target |id| either // directly or indirectly by Decoration Groups. void ForEachDecoration(uint32_t id, uint32_t decoration, - std::function<void(const Instruction&)> f); + std::function<void(const Instruction&)> f) const; // |f| is run on each decoration instruction for |id| with decoration // |decoration|. Processes all decoration which target |id| either directly or // indirectly through decoration groups. If |f| returns false, iteration is // terminated and this function returns false. bool WhileEachDecoration(uint32_t id, uint32_t decoration, - std::function<bool(const Instruction&)> f); + std::function<bool(const Instruction&)> f) const; // |f| is run on each decoration instruction for |id| with decoration // |decoration|. Processes all decoration which target |id| either directly or @@ -141,7 +142,7 @@ uint32_t decoration_value); // Add |decoration, decoration_value| of |inst_id, member| to module. - void AddMemberDecoration(uint32_t member, uint32_t inst_id, + void AddMemberDecoration(uint32_t inst_id, uint32_t member, uint32_t decoration, uint32_t decoration_value); friend bool operator==(const DecorationManager&, const DecorationManager&);
diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index a8dbbc6..13cf9bd 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h
@@ -27,28 +27,6 @@ namespace opt { namespace analysis { -// Class for representing a use of id. Note that: -// * Result type id is a use. -// * Ids referenced in OpSectionMerge & OpLoopMerge are considered as use. -// * Ids referenced in OpPhi's in operands are considered as use. -struct Use { - Instruction* inst; // Instruction using the id. - uint32_t operand_index; // logical operand index of the id use. This can be - // the index of result type id. -}; - -inline bool operator==(const Use& lhs, const Use& rhs) { - return lhs.inst == rhs.inst && lhs.operand_index == rhs.operand_index; -} - -inline bool operator!=(const Use& lhs, const Use& rhs) { return !(lhs == rhs); } - -inline bool operator<(const Use& lhs, const Use& rhs) { - if (lhs.inst < rhs.inst) return true; - if (lhs.inst > rhs.inst) return false; - return lhs.operand_index < rhs.operand_index; -} - // Definition should never be null. User can be null, however, such an entry // should be used only for searching (e.g. all users of a particular definition) // and never stored in a container.
diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index 8da0c86..2c0f482 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp
@@ -54,9 +54,10 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { 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( - var->result_id(), - [this, &access_chain_work_list, &load_work_list](Instruction* use) { + var->result_id(), [this, &access_chain_work_list, &load_work_list, + &entry_point_work_list](Instruction* use) { if (use->opcode() == spv::Op::OpName) { return true; } @@ -73,6 +74,9 @@ case spv::Op::OpLoad: load_work_list.push_back(use); return true; + case spv::Op::OpEntryPoint: + entry_point_work_list.push_back(use); + return true; default: context()->EmitErrorMessage( "Variable cannot be replaced: invalid instruction", use); @@ -95,6 +99,11 @@ return false; } } + for (Instruction* use : entry_point_work_list) { + if (!ReplaceEntryPoint(var, use)) { + return false; + } + } return true; } @@ -147,6 +156,42 @@ return true; } +bool DescriptorScalarReplacement::ReplaceEntryPoint(Instruction* var, + Instruction* use) { + // Build a new |OperandList| for |use| that removes |var| and adds its + // replacement variables. + Instruction::OperandList new_operands; + + // Copy all operands except |var|. + bool found = false; + for (uint32_t idx = 0; idx < use->NumOperands(); idx++) { + Operand& op = use->GetOperand(idx); + if (op.type == SPV_OPERAND_TYPE_ID && op.words[0] == var->result_id()) { + found = true; + } else { + new_operands.emplace_back(op); + } + } + + if (!found) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + + // Add all new replacement variables. + 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)}}); + } + + use->ReplaceOperands(new_operands); + context()->UpdateDefUse(use); + return true; +} + uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, uint32_t idx) { auto replacement_vars = replacement_variables_.find(var);
diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index 6a24fd8..901be3e 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h
@@ -64,6 +64,11 @@ // otherwise. bool ReplaceLoadedValue(Instruction* var, Instruction* value); + // Replaces the given composite variable |var| in the OpEntryPoint with the + // new replacement variables, one for each element of the array |var|. Returns + // |true| if successful, and |false| otherwise. + bool ReplaceEntryPoint(Instruction* var, Instruction* use); + // Replaces the given OpCompositeExtract |extract| and all of its references // with an OpLoad of a replacement variable. |var| is the variable with // composite type whose value is being used by |extract|. Assumes that
diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index 07e053b..5188370 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp
@@ -40,31 +40,33 @@ const std::string name = ext->GetInOperand(0u).AsString(); Extension extension; if (GetExtensionFromString(name.c_str(), &extension)) { - extensions_.Add(extension); + extensions_.insert(extension); } } void FeatureManager::RemoveExtension(Extension ext) { - if (!extensions_.Contains(ext)) return; - extensions_.Remove(ext); + if (!extensions_.contains(ext)) return; + extensions_.erase(ext); } void FeatureManager::AddCapability(spv::Capability cap) { - if (capabilities_.Contains(cap)) return; + if (capabilities_.contains(cap)) return; - capabilities_.Add(cap); + capabilities_.insert(cap); spv_operand_desc desc = {}; if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, uint32_t(cap), &desc)) { - CapabilitySet(desc->numCapabilities, desc->capabilities) - .ForEach([this](spv::Capability c) { AddCapability(c); }); + for (auto capability : + CapabilitySet(desc->numCapabilities, desc->capabilities)) { + AddCapability(capability); + } } } void FeatureManager::RemoveCapability(spv::Capability cap) { - if (!capabilities_.Contains(cap)) return; - capabilities_.Remove(cap); + if (!capabilities_.contains(cap)) return; + capabilities_.erase(cap); } void FeatureManager::AddCapabilities(Module* module) {
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h index b96988d..d150a2f 100644 --- a/source/opt/feature_manager.h +++ b/source/opt/feature_manager.h
@@ -25,27 +25,19 @@ // Tracks features enabled by a module. The IRContext has a FeatureManager. class FeatureManager { public: - explicit FeatureManager(const AssemblyGrammar& grammar) : grammar_(grammar) {} - // Returns true if |ext| is an enabled extension in the module. - bool HasExtension(Extension ext) const { return extensions_.Contains(ext); } - - // Removes the given |extension| from the current FeatureManager. - void RemoveExtension(Extension extension); + bool HasExtension(Extension ext) const { return extensions_.contains(ext); } // Returns true if |cap| is an enabled capability in the module. bool HasCapability(spv::Capability cap) const { - return capabilities_.Contains(cap); + return capabilities_.contains(cap); } - // Removes the given |capability| from the current FeatureManager. - void RemoveCapability(spv::Capability capability); + // Returns the capabilities the module declares. + inline const CapabilitySet& GetCapabilities() const { return capabilities_; } - // Analyzes |module| and records enabled extensions and capabilities. - void Analyze(Module* module); - - CapabilitySet* GetCapabilities() { return &capabilities_; } - const CapabilitySet* GetCapabilities() const { return &capabilities_; } + // Returns the extensions the module imports. + inline const ExtensionSet& GetExtensions() const { return extensions_; } uint32_t GetExtInstImportId_GLSLstd450() const { return extinst_importid_GLSLstd450_; @@ -64,23 +56,34 @@ return !(a == b); } - // Adds the given |capability| and all implied capabilities into the current - // FeatureManager. - void AddCapability(spv::Capability capability); + private: + explicit FeatureManager(const AssemblyGrammar& grammar) : grammar_(grammar) {} + + // Analyzes |module| and records enabled extensions and capabilities. + void Analyze(Module* module); // Add the extension |ext| to the feature manager. void AddExtension(Instruction* ext); - // Analyzes |module| and records imported external instruction sets. - void AddExtInstImportIds(Module* module); - - private: // Analyzes |module| and records enabled extensions. void AddExtensions(Module* module); + // Removes the given |extension| from the current FeatureManager. + void RemoveExtension(Extension extension); + + // Adds the given |capability| and all implied capabilities into the current + // FeatureManager. + void AddCapability(spv::Capability capability); + // Analyzes |module| and records enabled capabilities. void AddCapabilities(Module* module); + // Removes the given |capability| from the current FeatureManager. + void RemoveCapability(spv::Capability capability); + + // Analyzes |module| and records imported external instruction sets. + void AddExtInstImportIds(Module* module); + // Auxiliary object for querying SPIR-V grammar facts. const AssemblyGrammar& grammar_; @@ -100,6 +103,8 @@ // Common NonSemanticShader100DebugInfo external instruction import ids, // cached for performance. uint32_t extinst_importid_Shader100DebugInfo_ = 0; + + friend class IRContext; }; } // namespace opt
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp index 5597e82..564cd1b 100644 --- a/source/opt/fix_storage_class.cpp +++ b/source/opt/fix_storage_class.cpp
@@ -318,7 +318,13 @@ const analysis::Constant* index_const = context()->get_constant_mgr()->FindDeclaredConstant( inst->GetSingleWordInOperand(i)); - uint32_t index = index_const->GetU32(); + // It is highly unlikely that any type would have more fields than could + // be indexed by a 32-bit integer, and GetSingleWordInOperand only takes + // a 32-bit value, so we would not be able to handle it anyway. But the + // specification does allow any scalar integer type, treated as signed, + // so we simply downcast the index to 32-bits. + uint32_t index = + static_cast<uint32_t>(index_const->GetSignExtendedValue()); id = type_inst->GetSingleWordInOperand(index); break; }
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 453756f..942da68 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp
@@ -70,58 +70,6 @@ uint32_t InstructionFolder::BinaryOperate(spv::Op opcode, uint32_t a, uint32_t b) const { switch (opcode) { - // Arthimetics - case spv::Op::OpIAdd: - return a + b; - case spv::Op::OpISub: - return a - b; - case spv::Op::OpIMul: - return a * b; - case spv::Op::OpUDiv: - if (b != 0) { - return a / b; - } else { - // Dividing by 0 is undefined, so we will just pick 0. - return 0; - } - case spv::Op::OpSDiv: - if (b != 0u) { - return (static_cast<int32_t>(a)) / (static_cast<int32_t>(b)); - } else { - // Dividing by 0 is undefined, so we will just pick 0. - return 0; - } - case spv::Op::OpSRem: { - // The sign of non-zero result comes from the first operand: a. This is - // guaranteed by C++11 rules for integer division operator. The division - // result is rounded toward zero, so the result of '%' has the sign of - // the first operand. - if (b != 0u) { - return static_cast<int32_t>(a) % static_cast<int32_t>(b); - } else { - // Remainder when dividing with 0 is undefined, so we will just pick 0. - return 0; - } - } - case spv::Op::OpSMod: { - // The sign of non-zero result comes from the second operand: b - if (b != 0u) { - int32_t rem = BinaryOperate(spv::Op::OpSRem, a, b); - int32_t b_prim = static_cast<int32_t>(b); - return (rem + b_prim) % b_prim; - } else { - // Mod with 0 is undefined, so we will just pick 0. - return 0; - } - } - case spv::Op::OpUMod: - if (b != 0u) { - return (a % b); - } else { - // Mod with 0 is undefined, so we will just pick 0. - return 0; - } - // Shifting case spv::Op::OpShiftRightLogical: if (b >= 32) { @@ -627,7 +575,8 @@ Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const { analysis::ConstantManager* const_mgr = context_->get_constant_mgr(); - if (!inst->IsFoldableByFoldScalar() && !HasConstFoldingRule(inst)) { + if (!inst->IsFoldableByFoldScalar() && !inst->IsFoldableByFoldVector() && + !GetConstantFoldingRules().HasFoldingRule(inst)) { return nullptr; } // Collect the values of the constant parameters. @@ -661,29 +610,58 @@ } } - uint32_t result_val = 0; bool successful = false; + // If all parameters are constant, fold the instruction to a constant. - if (!missing_constants && inst->IsFoldableByFoldScalar()) { - result_val = FoldScalars(inst->opcode(), constants); - successful = true; + if (inst->IsFoldableByFoldScalar()) { + uint32_t result_val = 0; + + if (!missing_constants) { + result_val = FoldScalars(inst->opcode(), constants); + successful = true; + } + + if (!successful) { + successful = FoldIntegerOpToConstant(inst, id_map, &result_val); + } + + if (successful) { + const analysis::Constant* result_const = + const_mgr->GetConstant(const_mgr->GetType(inst), {result_val}); + Instruction* folded_inst = + const_mgr->GetDefiningInstruction(result_const, inst->type_id()); + return folded_inst; + } + } else if (inst->IsFoldableByFoldVector()) { + std::vector<uint32_t> result_val; + + if (!missing_constants) { + if (Instruction* inst_type = + context_->get_def_use_mgr()->GetDef(inst->type_id())) { + result_val = FoldVectors( + inst->opcode(), inst_type->GetSingleWordInOperand(1), constants); + successful = true; + } + } + + if (successful) { + const analysis::Constant* result_const = + const_mgr->GetNumericVectorConstantWithWords( + const_mgr->GetType(inst)->AsVector(), result_val); + Instruction* folded_inst = + const_mgr->GetDefiningInstruction(result_const, inst->type_id()); + return folded_inst; + } } - if (!successful && inst->IsFoldableByFoldScalar()) { - successful = FoldIntegerOpToConstant(inst, id_map, &result_val); - } - - if (successful) { - const analysis::Constant* result_const = - const_mgr->GetConstant(const_mgr->GetType(inst), {result_val}); - Instruction* folded_inst = - const_mgr->GetDefiningInstruction(result_const, inst->type_id()); - return folded_inst; - } return nullptr; } bool InstructionFolder::IsFoldableType(Instruction* type_inst) const { + return IsFoldableScalarType(type_inst) || IsFoldableVectorType(type_inst); +} + +bool InstructionFolder::IsFoldableScalarType(Instruction* type_inst) const { // Support 32-bit integers. if (type_inst->opcode() == spv::Op::OpTypeInt) { return type_inst->GetSingleWordInOperand(0) == 32; @@ -696,6 +674,19 @@ return false; } +bool InstructionFolder::IsFoldableVectorType(Instruction* type_inst) const { + // Support vectors with foldable components + if (type_inst->opcode() == spv::Op::OpTypeVector) { + uint32_t component_type_id = type_inst->GetSingleWordInOperand(0); + Instruction* def_component_type = + context_->get_def_use_mgr()->GetDef(component_type_id); + return def_component_type != nullptr && + IsFoldableScalarType(def_component_type); + } + // Nothing else yet. + return false; +} + bool InstructionFolder::FoldInstruction(Instruction* inst) const { bool modified = false; Instruction* folded_inst(inst);
diff --git a/source/opt/fold.h b/source/opt/fold.h index 9a131d0..42da65e 100644 --- a/source/opt/fold.h +++ b/source/opt/fold.h
@@ -86,6 +86,14 @@ // result type is |type_inst|. bool IsFoldableType(Instruction* type_inst) const; + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableScalarType(Instruction* type_inst) const; + + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableVectorType(Instruction* type_inst) const; + // Tries to fold |inst| to a single constant, when the input ids to |inst| // have been substituted using |id_map|. Returns a pointer to the OpConstant* // instruction if successful. If necessary, a new constant instruction is
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 f6d6155..c568027 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp +++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -115,20 +115,9 @@ "The first in-operand of OpSpecConstantOp instruction must be of " "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); - switch (static_cast<spv::Op>(inst->GetSingleWordInOperand(0))) { - case spv::Op::OpCompositeExtract: - case spv::Op::OpVectorShuffle: - case spv::Op::OpCompositeInsert: - case spv::Op::OpQuantizeToF16: - folded_inst = FoldWithInstructionFolder(pos); - break; - default: - // TODO: This should use the instruction folder as well, but some folding - // rules are missing. - - // Component-wise operations. - folded_inst = DoComponentWiseOperation(pos); - break; + folded_inst = FoldWithInstructionFolder(pos); + if (!folded_inst) { + folded_inst = DoComponentWiseOperation(pos); } if (!folded_inst) return false; @@ -176,8 +165,9 @@ Instruction* new_const_inst = context()->get_instruction_folder().FoldInstructionToConstant( inst.get(), identity_map); - assert(new_const_inst != nullptr && - "Failed to fold instruction that must be folded."); + + // new_const_inst == null indicates we cannot fold this spec constant + if (!new_const_inst) return nullptr; // Get the instruction before |pos| to insert after. |pos| cannot be the // first instruction in the list because its type has to come first.
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 7730ac1..5c68e29 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp
@@ -2067,7 +2067,8 @@ } // Returns the number of elements in the composite type |type|. Returns 0 if -// |type| is a scalar value. +// |type| is a scalar value. Return UINT32_MAX when the size is unknown at +// compile time. uint32_t GetNumberOfElements(const analysis::Type* type) { if (auto* vector_type = type->AsVector()) { return vector_type->element_count(); @@ -2079,21 +2080,27 @@ return static_cast<uint32_t>(struct_type->element_types().size()); } if (auto* array_type = type->AsArray()) { - return array_type->length_info().words[0]; + if (array_type->length_info().words[0] == + analysis::Array::LengthInfo::kConstant && + array_type->length_info().words.size() == 2) { + return array_type->length_info().words[1]; + } + return UINT32_MAX; } return 0; } // Returns a map with the set of values that were inserted into an object by // the chain of OpCompositeInsertInstruction starting with |inst|. -// The map will map the index to the value inserted at that index. +// The map will map the index to the value inserted at that index. An empty map +// will be returned if the map could not be properly generated. std::map<uint32_t, uint32_t> GetInsertedValues(Instruction* inst) { analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr(); std::map<uint32_t, uint32_t> values_inserted; Instruction* current_inst = inst; while (current_inst->opcode() == spv::Op::OpCompositeInsert) { if (current_inst->NumInOperands() > inst->NumInOperands()) { - // This is the catch the case + // This is to catch the case // %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0 // %3 = OpCompositeInsert %m2x2int %int_4 %2 0 0 // %4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1 @@ -2884,8 +2891,12 @@ "Offset and ConstOffset may not be used together"); if (offset_operand_index < inst->NumOperands()) { if (constants[offset_operand_index]) { - image_operands = - image_operands | uint32_t(spv::ImageOperandsMask::ConstOffset); + if (constants[offset_operand_index]->IsZero()) { + inst->RemoveInOperand(offset_operand_index); + } else { + image_operands = image_operands | + uint32_t(spv::ImageOperandsMask::ConstOffset); + } image_operands = image_operands & ~uint32_t(spv::ImageOperandsMask::Offset); inst->SetInOperand(operand_index, {image_operands});
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 8fff8a0..e765c39 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp
@@ -573,9 +573,9 @@ context()->module()->AddExtInstImport(std::move(import_inst)); module_status_.modified = true; context()->AnalyzeDefUse(inst); - // Reanalyze the feature list, since we added an extended instruction - // set improt. - context()->get_feature_mgr()->Analyze(context()->module()); + // Invalidates the feature manager, since we added an extended instruction + // set import. + context()->ResetFeatureManager(); } } return module_status_.glsl_insts_id;
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 3f160b2..3186433 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp
@@ -213,6 +213,19 @@ {(uint32_t)spv::StorageClass::Function}}})); new_vars->push_back(std::move(var_inst)); get_decoration_mgr()->CloneDecorations(calleeFn->result_id(), returnVarId); + + // Decorate the return var with AliasedPointer if the storage class of the + // pointee type is PhysicalStorageBuffer. + auto const pointee_type = + type_mgr->GetType(returnVarTypeId)->AsPointer()->pointee_type(); + if (pointee_type->AsPointer() != nullptr) { + if (pointee_type->AsPointer()->storage_class() == + spv::StorageClass::PhysicalStorageBuffer) { + get_decoration_mgr()->AddDecoration( + returnVarId, uint32_t(spv::Decoration::AliasedPointer)); + } + } + return returnVarId; }
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp index e8c412f..8e7d4f8 100644 --- a/source/opt/inst_bindless_check_pass.cpp +++ b/source/opt/inst_bindless_check_pass.cpp
@@ -31,532 +31,88 @@ constexpr int kSpvAccessChainBaseIdInIdx = 0; constexpr int kSpvAccessChainIndex0IdInIdx = 1; constexpr int kSpvTypeArrayTypeIdInIdx = 0; -constexpr int kSpvTypeArrayLengthIdInIdx = 1; -constexpr int kSpvConstantValueInIdx = 0; constexpr int kSpvVariableStorageClassInIdx = 0; constexpr int kSpvTypePtrTypeIdInIdx = 1; constexpr int kSpvTypeImageDim = 1; constexpr int kSpvTypeImageDepth = 2; constexpr int kSpvTypeImageArrayed = 3; constexpr int kSpvTypeImageMS = 4; -constexpr int kSpvTypeImageSampled = 5; } // namespace -void InstBindlessCheckPass::SetupInputBufferIds() { - if (input_buffer_id_ != 0) { - return; +// This is a stub function for use with Import linkage +// clang-format off +// GLSL: +//bool inst_bindless_check_desc(const uint shader_id, const uint inst_num, const uvec4 stage_info, const uint desc_set, +// const uint binding, const uint desc_index, const uint byte_offset) { +//} +// clang-format on +uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() { + enum { + kShaderId = 0, + kInstructionIndex = 1, + kStageInfo = 2, + kDescSet = 3, + kDescBinding = 4, + kDescIndex = 5, + kByteOffset = 6, + kNumArgs + }; + if (check_desc_func_id_ != 0) { + return check_desc_func_id_; } - AddStorageBufferExt(); - if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) { - context()->AddExtension("SPV_KHR_physical_storage_buffer"); - } - context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses); - Instruction* memory_model = get_module()->GetMemoryModel(); - // TODO should this be just Physical64? - memory_model->SetInOperand( - 0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)}); - analysis::DecorationManager* deco_mgr = get_decoration_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); - constexpr uint32_t width = 32u; + const analysis::Integer* uint_type = GetInteger(32, false); + const analysis::Vector v4uint(uint_type, 4); + const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint); + std::vector<const analysis::Type*> param_types(kNumArgs, uint_type); + param_types[2] = v4uint_type; - // declare the DescriptorSetData struct - analysis::Struct* desc_set_struct = - GetStruct({type_mgr->GetUIntType(), GetUintRuntimeArrayType(width)}); - desc_set_type_id_ = type_mgr->GetTypeInstruction(desc_set_struct); - // By the Vulkan spec, a pre-existing struct containing a RuntimeArray - // must be a block, and will therefore be decorated with Block. Therefore - // the undecorated type returned here will not be pre-existing and can - // safely be decorated. Since this type is now decorated, it is out of - // sync with the TypeManager and therefore the TypeManager must be - // invalidated after this pass. - assert(context()->get_def_use_mgr()->NumUses(desc_set_type_id_) == 0 && - "used struct type returned"); - deco_mgr->AddDecoration(desc_set_type_id_, uint32_t(spv::Decoration::Block)); - deco_mgr->AddMemberDecoration(desc_set_type_id_, 0, - uint32_t(spv::Decoration::Offset), 0); - deco_mgr->AddMemberDecoration(desc_set_type_id_, 1, - uint32_t(spv::Decoration::Offset), 4); - context()->AddDebug2Inst( - NewGlobalName(desc_set_type_id_, "DescriptorSetData")); - context()->AddDebug2Inst(NewMemberName(desc_set_type_id_, 0, "num_bindings")); - context()->AddDebug2Inst(NewMemberName(desc_set_type_id_, 1, "data")); + const uint32_t func_id = TakeNextId(); + std::unique_ptr<Function> func = + StartFunction(func_id, type_mgr->GetBoolType(), param_types); - // declare buffer address reference to DescriptorSetData - desc_set_ptr_id_ = type_mgr->FindPointerToType( - desc_set_type_id_, spv::StorageClass::PhysicalStorageBuffer); - // runtime array of buffer addresses - analysis::Type* rarr_ty = GetArray(type_mgr->GetType(desc_set_ptr_id_), - kDebugInputBindlessMaxDescSets); - deco_mgr->AddDecorationVal(type_mgr->GetId(rarr_ty), - uint32_t(spv::Decoration::ArrayStride), 8u); + func->SetFunctionEnd(EndFunction()); - // declare the InputBuffer type, a struct wrapper around the runtime array - analysis::Struct* input_buffer_struct = GetStruct({rarr_ty}); - input_buffer_struct_id_ = type_mgr->GetTypeInstruction(input_buffer_struct); - deco_mgr->AddDecoration(input_buffer_struct_id_, - uint32_t(spv::Decoration::Block)); - deco_mgr->AddMemberDecoration(input_buffer_struct_id_, 0, - uint32_t(spv::Decoration::Offset), 0); - context()->AddDebug2Inst( - NewGlobalName(input_buffer_struct_id_, "InputBuffer")); - context()->AddDebug2Inst( - NewMemberName(input_buffer_struct_id_, 0, "desc_sets")); - - input_buffer_ptr_id_ = type_mgr->FindPointerToType( - input_buffer_struct_id_, spv::StorageClass::StorageBuffer); - - // declare the input_buffer global variable - input_buffer_id_ = TakeNextId(); - - const std::vector<Operand> var_operands = { + static const std::string func_name{"inst_bindless_check_desc"}; + context()->AddFunctionDeclaration(std::move(func)); + context()->AddDebug2Inst(NewName(func_id, func_name)); + std::vector<Operand> operands{ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}}, {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, - {uint32_t(spv::StorageClass::StorageBuffer)}}, + {uint32_t(spv::Decoration::LinkageAttributes)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING, + utils::MakeVector(func_name.c_str())}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE, + {uint32_t(spv::LinkageType::Import)}}, }; - auto new_var_op = spvtools::MakeUnique<Instruction>( - context(), spv::Op::OpVariable, input_buffer_ptr_id_, input_buffer_id_, - var_operands); + get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands); - context()->AddGlobalValue(std::move(new_var_op)); - context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer")); - deco_mgr->AddDecorationVal( - input_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_); - deco_mgr->AddDecorationVal(input_buffer_id_, - uint32_t(spv::Decoration::Binding), - GetInputBufferBinding()); - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { - // Add the new buffer to all entry points. - for (auto& entry : get_module()->entry_points()) { - entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}}); - context()->AnalyzeUses(&entry); - } - } -} - -// clang-format off -// GLSL: -// uint inst_bindless_read_binding_length(uint desc_set_idx, uint binding_idx) -// { -// if (desc_set_idx >= inst_bindless_input_buffer.desc_sets.length()) { -// return 0; -// } -// -// DescriptorSetData set_data = inst_bindless_input_buffer.desc_sets[desc_set_idx]; -// uvec2 ptr_as_vec = uvec2(set_data); -// if ((ptr_as_vec.x == 0u) && (_ptr_as_vec.y == 0u)) -// { -// return 0u; -// } -// uint num_bindings = set_data.num_bindings; -// if (binding_idx >= num_bindings) { -// return 0; -// } -// return set_data.data[binding_idx]; -// } -// clang-format on -uint32_t InstBindlessCheckPass::GenDebugReadLengthFunctionId() { - if (read_length_func_id_ != 0) { - return read_length_func_id_; - } - SetupInputBufferIds(); - const analysis::Integer* uint_type = GetInteger(32, false); - const std::vector<const analysis::Type*> param_types(2, uint_type); - - const uint32_t func_id = TakeNextId(); - std::unique_ptr<Function> func = - StartFunction(func_id, uint_type, param_types); - - const std::vector<uint32_t> param_ids = AddParameters(*func, param_types); - - // Create block - auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId())); - InstructionBuilder builder( - context(), new_blk_ptr.get(), - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - Instruction* inst; - - inst = builder.AddBinaryOp( - GetBoolId(), spv::Op::OpUGreaterThanEqual, param_ids[0], - builder.GetUintConstantId(kDebugInputBindlessMaxDescSets)); - const uint32_t desc_cmp_id = inst->result_id(); - - uint32_t error_blk_id = TakeNextId(); - uint32_t merge_blk_id = TakeNextId(); - std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); - std::unique_ptr<Instruction> error_label(NewLabel(error_blk_id)); - (void)builder.AddConditionalBranch(desc_cmp_id, error_blk_id, merge_blk_id, - merge_blk_id); - - func->AddBasicBlock(std::move(new_blk_ptr)); - - // error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // check descriptor set table entry is non-null - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - const uint32_t desc_set_ptr_ptr = type_mgr->FindPointerToType( - desc_set_ptr_id_, spv::StorageClass::StorageBuffer); - - inst = builder.AddAccessChain(desc_set_ptr_ptr, input_buffer_id_, - {builder.GetUintConstantId(0), param_ids[0]}); - const uint32_t set_access_chain_id = inst->result_id(); - - inst = builder.AddLoad(desc_set_ptr_id_, set_access_chain_id); - const uint32_t desc_set_ptr_id = inst->result_id(); - - inst = - builder.AddUnaryOp(GetVecUintId(2), spv::Op::OpBitcast, desc_set_ptr_id); - const uint32_t ptr_as_uvec_id = inst->result_id(); - - inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {0}); - const uint32_t uvec_x = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_x, - builder.GetUintConstantId(0)); - const uint32_t x_is_zero_id = inst->result_id(); - - inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {1}); - const uint32_t uvec_y = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_y, - builder.GetUintConstantId(0)); - const uint32_t y_is_zero_id = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpLogicalAnd, x_is_zero_id, - y_is_zero_id); - const uint32_t is_null_id = inst->result_id(); - - error_blk_id = TakeNextId(); - merge_blk_id = TakeNextId(); - merge_label = NewLabel(merge_blk_id); - error_label = NewLabel(error_blk_id); - (void)builder.AddConditionalBranch(is_null_id, error_blk_id, merge_blk_id, - merge_blk_id); - func->AddBasicBlock(std::move(new_blk_ptr)); - // error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // check binding is in range - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - const uint32_t uint_ptr = type_mgr->FindPointerToType( - GetUintId(), spv::StorageClass::PhysicalStorageBuffer); - - inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id, - {builder.GetUintConstantId(0)}); - const uint32_t binding_access_chain_id = inst->result_id(); - - inst = builder.AddLoad(GetUintId(), binding_access_chain_id, 8); - const uint32_t num_bindings_id = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual, - param_ids[1], num_bindings_id); - const uint32_t bindings_cmp_id = inst->result_id(); - - error_blk_id = TakeNextId(); - merge_blk_id = TakeNextId(); - merge_label = NewLabel(merge_blk_id); - error_label = NewLabel(error_blk_id); - (void)builder.AddConditionalBranch(bindings_cmp_id, error_blk_id, - merge_blk_id, merge_blk_id); - func->AddBasicBlock(std::move(new_blk_ptr)); - // error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // read binding length - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id, - {{builder.GetUintConstantId(1), param_ids[1]}}); - const uint32_t length_ac_id = inst->result_id(); - - inst = builder.AddLoad(GetUintId(), length_ac_id, sizeof(uint32_t)); - const uint32_t length_id = inst->result_id(); - - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, length_id); - - func->AddBasicBlock(std::move(new_blk_ptr)); - func->SetFunctionEnd(EndFunction()); - - context()->AddFunction(std::move(func)); - context()->AddDebug2Inst(NewGlobalName(func_id, "read_binding_length")); - - read_length_func_id_ = func_id; - // Make sure this function doesn't get processed by - // InstrumentPass::InstProcessCallTreeFromRoots() - param2output_func_id_[2] = func_id; - return read_length_func_id_; -} - -// clang-format off -// GLSL: -// result = inst_bindless_read_binding_length(desc_set_id, binding_id); -// clang-format on -uint32_t InstBindlessCheckPass::GenDebugReadLength( - uint32_t var_id, InstructionBuilder* builder) { - const uint32_t func_id = GenDebugReadLengthFunctionId(); - - const std::vector<uint32_t> args = { - builder->GetUintConstantId(var2desc_set_[var_id]), - builder->GetUintConstantId(var2binding_[var_id]), - }; - return GenReadFunctionCall(func_id, args, builder); -} - -// clang-format off -// GLSL: -// uint inst_bindless_read_desc_init(uint desc_set_idx, uint binding_idx, uint desc_idx) -// { -// if (desc_set_idx >= uint(inst_bindless_input_buffer.desc_sets.length())) -// { -// return 0u; -// } -// DescriptorSetData set_data = inst_bindless_input_buffer.desc_sets[desc_set_idx]; -// uvec2 ptr_as_vec = uvec2(set_data) -// if ((ptr_as_vec .x == 0u) && (ptr_as_vec.y == 0u)) -// { -// return 0u; -// } -// if (binding_idx >= set_data.num_bindings) -// { -// return 0u; -// } -// if (desc_idx >= set_data.data[binding_idx]) -// { -// return 0u; -// } -// uint desc_records_start = set_data.data[set_data.num_bindings + binding_idx]; -// return set_data.data[desc_records_start + desc_idx]; -// } -// clang-format on -uint32_t InstBindlessCheckPass::GenDebugReadInitFunctionId() { - if (read_init_func_id_ != 0) { - return read_init_func_id_; - } - SetupInputBufferIds(); - const analysis::Integer* uint_type = GetInteger(32, false); - const std::vector<const analysis::Type*> param_types(3, uint_type); - - const uint32_t func_id = TakeNextId(); - std::unique_ptr<Function> func = - StartFunction(func_id, uint_type, param_types); - - const std::vector<uint32_t> param_ids = AddParameters(*func, param_types); - - // Create block - auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId())); - InstructionBuilder builder( - context(), new_blk_ptr.get(), - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - Instruction* inst; - - inst = builder.AddBinaryOp( - GetBoolId(), spv::Op::OpUGreaterThanEqual, param_ids[0], - builder.GetUintConstantId(kDebugInputBindlessMaxDescSets)); - const uint32_t desc_cmp_id = inst->result_id(); - - uint32_t error_blk_id = TakeNextId(); - uint32_t merge_blk_id = TakeNextId(); - std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); - std::unique_ptr<Instruction> error_label(NewLabel(error_blk_id)); - (void)builder.AddConditionalBranch(desc_cmp_id, error_blk_id, merge_blk_id, - merge_blk_id); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // check descriptor set table entry is non-null - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - const uint32_t desc_set_ptr_ptr = type_mgr->FindPointerToType( - desc_set_ptr_id_, spv::StorageClass::StorageBuffer); - - inst = builder.AddAccessChain(desc_set_ptr_ptr, input_buffer_id_, - {builder.GetUintConstantId(0), param_ids[0]}); - const uint32_t set_access_chain_id = inst->result_id(); - - inst = builder.AddLoad(desc_set_ptr_id_, set_access_chain_id); - const uint32_t desc_set_ptr_id = inst->result_id(); - - inst = - builder.AddUnaryOp(GetVecUintId(2), spv::Op::OpBitcast, desc_set_ptr_id); - const uint32_t ptr_as_uvec_id = inst->result_id(); - - inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {0}); - const uint32_t uvec_x = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_x, - builder.GetUintConstantId(0)); - const uint32_t x_is_zero_id = inst->result_id(); - - inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {1}); - const uint32_t uvec_y = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_y, - builder.GetUintConstantId(0)); - const uint32_t y_is_zero_id = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpLogicalAnd, x_is_zero_id, - y_is_zero_id); - const uint32_t is_null_id = inst->result_id(); - - error_blk_id = TakeNextId(); - merge_blk_id = TakeNextId(); - merge_label = NewLabel(merge_blk_id); - error_label = NewLabel(error_blk_id); - (void)builder.AddConditionalBranch(is_null_id, error_blk_id, merge_blk_id, - merge_blk_id); - func->AddBasicBlock(std::move(new_blk_ptr)); - // error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // check binding is in range - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - const uint32_t uint_ptr = type_mgr->FindPointerToType( - GetUintId(), spv::StorageClass::PhysicalStorageBuffer); - - inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id, - {builder.GetUintConstantId(0)}); - const uint32_t binding_access_chain_id = inst->result_id(); - - inst = builder.AddLoad(GetUintId(), binding_access_chain_id, 8); - const uint32_t num_bindings_id = inst->result_id(); - - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual, - param_ids[1], num_bindings_id); - const uint32_t bindings_cmp_id = inst->result_id(); - - error_blk_id = TakeNextId(); - merge_blk_id = TakeNextId(); - merge_label = NewLabel(merge_blk_id); - error_label = NewLabel(error_blk_id); - (void)builder.AddConditionalBranch(bindings_cmp_id, error_blk_id, - merge_blk_id, merge_blk_id); - func->AddBasicBlock(std::move(new_blk_ptr)); - // error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // read binding length - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id, - {{builder.GetUintConstantId(1), param_ids[1]}}); - const uint32_t length_ac_id = inst->result_id(); - - inst = builder.AddLoad(GetUintId(), length_ac_id, sizeof(uint32_t)); - const uint32_t length_id = inst->result_id(); - - // Check descriptor index in bounds - inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual, - param_ids[2], length_id); - const uint32_t desc_idx_range_id = inst->result_id(); - - error_blk_id = TakeNextId(); - merge_blk_id = TakeNextId(); - merge_label = NewLabel(merge_blk_id); - error_label = NewLabel(error_blk_id); - (void)builder.AddConditionalBranch(desc_idx_range_id, error_blk_id, - merge_blk_id, merge_blk_id); - func->AddBasicBlock(std::move(new_blk_ptr)); - // Error return - new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label)); - builder.SetInsertPoint(&*new_blk_ptr); - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - builder.GetUintConstantId(0)); - func->AddBasicBlock(std::move(new_blk_ptr)); - - // Read descriptor init status - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - - inst = builder.AddIAdd(GetUintId(), num_bindings_id, param_ids[1]); - const uint32_t state_offset_id = inst->result_id(); - - inst = - builder.AddAccessChain(uint_ptr, desc_set_ptr_id, - {{builder.GetUintConstantId(1), state_offset_id}}); - const uint32_t state_start_ac_id = inst->result_id(); - - inst = builder.AddLoad(GetUintId(), state_start_ac_id, sizeof(uint32_t)); - const uint32_t state_start_id = inst->result_id(); - - inst = builder.AddIAdd(GetUintId(), state_start_id, param_ids[2]); - const uint32_t state_entry_id = inst->result_id(); - - // Note: length starts from the beginning of the buffer, not the beginning of - // the data array - inst = - builder.AddAccessChain(uint_ptr, desc_set_ptr_id, - {{builder.GetUintConstantId(1), state_entry_id}}); - const uint32_t init_ac_id = inst->result_id(); - - inst = builder.AddLoad(GetUintId(), init_ac_id, sizeof(uint32_t)); - const uint32_t init_status_id = inst->result_id(); - - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, init_status_id); - - func->AddBasicBlock(std::move(new_blk_ptr)); - func->SetFunctionEnd(EndFunction()); - - context()->AddFunction(std::move(func)); - context()->AddDebug2Inst(NewGlobalName(func_id, "read_desc_init")); - - read_init_func_id_ = func_id; + check_desc_func_id_ = func_id; // Make sure function doesn't get processed by // InstrumentPass::InstProcessCallTreeFromRoots() param2output_func_id_[3] = func_id; - return read_init_func_id_; + return check_desc_func_id_; } // clang-format off // GLSL: -// result = inst_bindless_read_desc_init(desc_set_id, binding_id, desc_idx_id); +// result = inst_bindless_check_desc(shader_id, inst_idx, stage_info, desc_set, binding, desc_idx, offset); // // clang-format on -uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id, - uint32_t desc_idx_id, - InstructionBuilder* builder) { - const uint32_t func_id = GenDebugReadInitFunctionId(); +uint32_t InstBindlessCheckPass::GenDescCheckCall( + uint32_t inst_idx, uint32_t stage_idx, uint32_t var_id, + uint32_t desc_idx_id, uint32_t offset_id, InstructionBuilder* builder) { + const uint32_t func_id = GenDescCheckFunctionId(); const std::vector<uint32_t> args = { + builder->GetUintConstantId(shader_id_), + builder->GetUintConstantId(inst_idx), + GenStageInfo(stage_idx, builder), builder->GetUintConstantId(var2desc_set_[var_id]), builder->GetUintConstantId(var2binding_[var_id]), - GenUintCastCode(desc_idx_id, builder)}; - return GenReadFunctionCall(func_id, args, builder); + GenUintCastCode(desc_idx_id, builder), + offset_id}; + return GenReadFunctionCall(GetBoolId(), func_id, args, builder); } uint32_t InstBindlessCheckPass::CloneOriginalImage( @@ -1017,8 +573,7 @@ } void InstBindlessCheckPass::GenCheckCode( - uint32_t check_id, uint32_t error_id, uint32_t offset_id, - uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref, + uint32_t check_id, RefAnalysis* ref, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { BasicBlock* back_blk_ptr = &*new_blocks->back(); InstructionBuilder builder( @@ -1047,30 +602,7 @@ // Gen invalid block new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); builder.SetInsertPoint(&*new_blk_ptr); - const uint32_t u_set_id = builder.GetUintConstantId(ref->set); - const uint32_t u_binding_id = builder.GetUintConstantId(ref->binding); - const uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder); - const uint32_t u_length_id = GenUintCastCode(length_id, &builder); - if (offset_id != 0) { - const uint32_t u_offset_id = GenUintCastCode(offset_id, &builder); - // Buffer OOB - GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx, - {error_id, u_set_id, u_binding_id, u_index_id, - u_offset_id, u_length_id}, - &builder); - } else if (buffer_bounds_enabled_ || texel_buffer_enabled_) { - // Uninitialized Descriptor - Return additional unused zero so all error - // modes will use same debug stream write function - GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx, - {error_id, u_set_id, u_binding_id, u_index_id, - u_length_id, builder.GetUintConstantId(0)}, - &builder); - } else { - // Uninitialized Descriptor - Normal error return - GenDebugStreamWrite( - uid2offset_[ref->ref_inst->unique_id()], stage_idx, - {error_id, u_set_id, u_binding_id, u_index_id, u_length_id}, &builder); - } + // Generate a ConstantNull, converting to uint64 if the type cannot be a null. if (new_ref_id != 0) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -1106,77 +638,42 @@ context()->KillInst(ref->ref_inst); } -void InstBindlessCheckPass::GenDescIdxCheckCode( - BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { - // Look for reference through indexed descriptor. If found, analyze and - // save components. If not, return. - RefAnalysis ref; - if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return; - Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id); - if (ptr_inst->opcode() != spv::Op::OpAccessChain) return; - // If index and bound both compile-time constants and index < bound, - // return without changing - Instruction* var_inst = get_def_use_mgr()->GetDef(ref.var_id); - Instruction* desc_type_inst = GetPointeeTypeInst(var_inst); - uint32_t length_id = 0; - if (desc_type_inst->opcode() == spv::Op::OpTypeArray) { - length_id = - desc_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx); - Instruction* index_inst = get_def_use_mgr()->GetDef(ref.desc_idx_id); - Instruction* length_inst = get_def_use_mgr()->GetDef(length_id); - if (index_inst->opcode() == spv::Op::OpConstant && - length_inst->opcode() == spv::Op::OpConstant && - index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) < - length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx)) - return; - } else if (!desc_idx_enabled_ || - desc_type_inst->opcode() != spv::Op::OpTypeRuntimeArray) { - return; - } - // Move original block's preceding instructions into first new block - std::unique_ptr<BasicBlock> new_blk_ptr; - MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); - InstructionBuilder builder( - context(), &*new_blk_ptr, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - new_blocks->push_back(std::move(new_blk_ptr)); - uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds); - // If length id not yet set, descriptor array is runtime size so - // generate load of length from stage's debug input buffer. - if (length_id == 0) { - assert(desc_type_inst->opcode() == spv::Op::OpTypeRuntimeArray && - "unexpected bindless type"); - length_id = GenDebugReadLength(ref.var_id, &builder); - } - // Generate full runtime bounds test code with true branch - // being full reference and false branch being debug output and zero - // for the referenced value. - uint32_t desc_idx_32b_id = Gen32BitCvtCode(ref.desc_idx_id, &builder); - uint32_t length_32b_id = Gen32BitCvtCode(length_id, &builder); - Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpULessThan, - desc_idx_32b_id, length_32b_id); - ref.desc_idx_id = desc_idx_32b_id; - GenCheckCode(ult_inst->result_id(), error_id, 0u, length_id, stage_idx, &ref, - new_blocks); - // Move original block's remaining code into remainder/merge block and add - // to new blocks - BasicBlock* back_blk_ptr = &*new_blocks->back(); - MovePostludeCode(ref_block_itr, back_blk_ptr); -} - -void InstBindlessCheckPass::GenDescInitCheckCode( +void InstBindlessCheckPass::GenDescCheckCode( BasicBlock::iterator ref_inst_itr, UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { // Look for reference through descriptor. If not, return. RefAnalysis ref; if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return; + std::unique_ptr<BasicBlock> new_blk_ptr; + // Move original block's preceding instructions into first new block + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_blocks->push_back(std::move(new_blk_ptr)); // Determine if we can only do initialization check - bool init_check = false; - if (ref.desc_load_id != 0 || !buffer_bounds_enabled_) { - init_check = true; + uint32_t ref_id = builder.GetUintConstantId(0u); + spv::Op op = ref.ref_inst->opcode(); + if (ref.desc_load_id != 0) { + uint32_t num_in_oprnds = ref.ref_inst->NumInOperands(); + if ((op == spv::Op::OpImageRead && num_in_oprnds == 2) || + (op == spv::Op::OpImageFetch && num_in_oprnds == 2) || + (op == spv::Op::OpImageWrite && num_in_oprnds == 3)) { + Instruction* image_inst = get_def_use_mgr()->GetDef(ref.image_id); + uint32_t image_ty_id = image_inst->type_id(); + Instruction* image_ty_inst = get_def_use_mgr()->GetDef(image_ty_id); + if (spv::Dim(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDim)) == + spv::Dim::Buffer) { + if ((image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDepth) == 0) && + (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageArrayed) == + 0) && + (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageMS) == 0)) { + ref_id = GenUintCastCode(ref.ref_inst->GetSingleWordInOperand(1), + &builder); + } + } + } } else { // For now, only do bounds check for non-aggregate types. Otherwise // just do descriptor initialization check. @@ -1184,106 +681,24 @@ Instruction* ref_ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id); Instruction* pte_type_inst = GetPointeeTypeInst(ref_ptr_inst); spv::Op pte_type_op = pte_type_inst->opcode(); - if (pte_type_op == spv::Op::OpTypeArray || - pte_type_op == spv::Op::OpTypeRuntimeArray || - pte_type_op == spv::Op::OpTypeStruct) - init_check = true; + if (pte_type_op != spv::Op::OpTypeArray && + pte_type_op != spv::Op::OpTypeRuntimeArray && + pte_type_op != spv::Op::OpTypeStruct) { + ref_id = GenLastByteIdx(&ref, &builder); + } } - // If initialization check and not enabled, return - if (init_check && !desc_init_enabled_) return; - // Move original block's preceding instructions into first new block - std::unique_ptr<BasicBlock> new_blk_ptr; - MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); - InstructionBuilder builder( - context(), &*new_blk_ptr, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - new_blocks->push_back(std::move(new_blk_ptr)); - // If initialization check, use reference value of zero. - // Else use the index of the last byte referenced. - uint32_t ref_id = init_check ? builder.GetUintConstantId(0u) - : GenLastByteIdx(&ref, &builder); // Read initialization/bounds from debug input buffer. If index id not yet // set, binding is single descriptor, so set index to constant 0. if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u); - uint32_t init_id = GenDebugReadInit(ref.var_id, ref.desc_idx_id, &builder); - // Generate runtime initialization/bounds test code with true branch - // being full reference and false branch being debug output and zero - // for the referenced value. - Instruction* ult_inst = - builder.AddBinaryOp(GetBoolId(), spv::Op::OpULessThan, ref_id, init_id); - uint32_t error = - init_check - ? kInstErrorBindlessUninit - : (spv::StorageClass(ref.strg_class) == spv::StorageClass::Uniform - ? kInstErrorBuffOOBUniform - : kInstErrorBuffOOBStorage); - uint32_t error_id = builder.GetUintConstantId(error); - GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id, - init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx, - &ref, new_blocks); - // Move original block's remaining code into remainder/merge block and add - // to new blocks - BasicBlock* back_blk_ptr = &*new_blocks->back(); - MovePostludeCode(ref_block_itr, back_blk_ptr); -} + uint32_t check_id = + GenDescCheckCall(ref.ref_inst->unique_id(), stage_idx, ref.var_id, + ref.desc_idx_id, ref_id, &builder); -void InstBindlessCheckPass::GenTexBuffCheckCode( - BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { - // Only process OpImageRead and OpImageWrite with no optional operands - Instruction* ref_inst = &*ref_inst_itr; - spv::Op op = ref_inst->opcode(); - uint32_t num_in_oprnds = ref_inst->NumInOperands(); - if (!((op == spv::Op::OpImageRead && num_in_oprnds == 2) || - (op == spv::Op::OpImageFetch && num_in_oprnds == 2) || - (op == spv::Op::OpImageWrite && num_in_oprnds == 3))) - return; - // Pull components from descriptor reference - RefAnalysis ref; - if (!AnalyzeDescriptorReference(ref_inst, &ref)) return; - // Only process if image is texel buffer - Instruction* image_inst = get_def_use_mgr()->GetDef(ref.image_id); - uint32_t image_ty_id = image_inst->type_id(); - Instruction* image_ty_inst = get_def_use_mgr()->GetDef(image_ty_id); - if (spv::Dim(image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDim)) != - spv::Dim::Buffer) { - return; - } - if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageDepth) != 0) return; - if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageArrayed) != 0) return; - if (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageMS) != 0) return; - // Enable ImageQuery Capability if not yet enabled - context()->AddCapability(spv::Capability::ImageQuery); - // Move original block's preceding instructions into first new block - std::unique_ptr<BasicBlock> new_blk_ptr; - MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); - InstructionBuilder builder( - context(), &*new_blk_ptr, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - new_blocks->push_back(std::move(new_blk_ptr)); - // Get texel coordinate - uint32_t coord_id = - GenUintCastCode(ref_inst->GetSingleWordInOperand(1), &builder); - // If index id not yet set, binding is single descriptor, so set index to - // constant 0. - if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u); - // Get texel buffer size. - Instruction* size_inst = - builder.AddUnaryOp(GetUintId(), spv::Op::OpImageQuerySize, ref.image_id); - uint32_t size_id = size_inst->result_id(); // Generate runtime initialization/bounds test code with true branch - // being full reference and false branch being debug output and zero + // being full reference and false branch being zero // for the referenced value. - Instruction* ult_inst = - builder.AddBinaryOp(GetBoolId(), spv::Op::OpULessThan, coord_id, size_id); - uint32_t error = - (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageSampled) == 2) - ? kInstErrorBuffOOBStorageTexel - : kInstErrorBuffOOBUniformTexel; - uint32_t error_id = builder.GetUintConstantId(error); - GenCheckCode(ult_inst->result_id(), error_id, coord_id, size_id, stage_idx, - &ref, new_blocks); + GenCheckCode(check_id, &ref, new_blocks); + // Move original block's remaining code into remainder/merge block and add // to new blocks BasicBlock* back_blk_ptr = &*new_blocks->back(); @@ -1293,59 +708,48 @@ void InstBindlessCheckPass::InitializeInstBindlessCheck() { // Initialize base class InitializeInstrument(); - // If runtime array length support or buffer bounds checking are enabled, - // create variable mappings. Length support is always enabled if descriptor - // init check is enabled. - if (desc_idx_enabled_ || buffer_bounds_enabled_ || texel_buffer_enabled_) - for (auto& anno : get_module()->annotations()) - if (anno.opcode() == spv::Op::OpDecorate) { - if (spv::Decoration(anno.GetSingleWordInOperand(1u)) == - spv::Decoration::DescriptorSet) { - var2desc_set_[anno.GetSingleWordInOperand(0u)] = - anno.GetSingleWordInOperand(2u); - } else if (spv::Decoration(anno.GetSingleWordInOperand(1u)) == - spv::Decoration::Binding) { - var2binding_[anno.GetSingleWordInOperand(0u)] = - anno.GetSingleWordInOperand(2u); - } + for (auto& anno : get_module()->annotations()) { + if (anno.opcode() == spv::Op::OpDecorate) { + if (spv::Decoration(anno.GetSingleWordInOperand(1u)) == + spv::Decoration::DescriptorSet) { + var2desc_set_[anno.GetSingleWordInOperand(0u)] = + anno.GetSingleWordInOperand(2u); + } else if (spv::Decoration(anno.GetSingleWordInOperand(1u)) == + spv::Decoration::Binding) { + var2binding_[anno.GetSingleWordInOperand(0u)] = + anno.GetSingleWordInOperand(2u); } + } + } } Pass::Status InstBindlessCheckPass::ProcessImpl() { - // Perform bindless bounds check on each entry point function in module + // The memory model and linkage must always be updated for spirv-link to work + // correctly. + AddStorageBufferExt(); + if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) { + context()->AddExtension("SPV_KHR_physical_storage_buffer"); + } + + context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses); + Instruction* memory_model = get_module()->GetMemoryModel(); + memory_model->SetInOperand( + 0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)}); + + context()->AddCapability(spv::Capability::Linkage); + InstProcessFunction pfn = [this](BasicBlock::iterator ref_inst_itr, UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { - return GenDescIdxCheckCode(ref_inst_itr, ref_block_itr, stage_idx, - new_blocks); + return GenDescCheckCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); }; - bool modified = InstProcessEntryPointCallTree(pfn); - if (desc_init_enabled_ || buffer_bounds_enabled_) { - // Perform descriptor initialization and/or buffer bounds check on each - // entry point function in module - pfn = [this](BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, - uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { - return GenDescInitCheckCode(ref_inst_itr, ref_block_itr, stage_idx, - new_blocks); - }; - modified |= InstProcessEntryPointCallTree(pfn); - } - if (texel_buffer_enabled_) { - // Perform texel buffer bounds check on each entry point function in - // module. Generate after descriptor bounds and initialization checks. - pfn = [this](BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, - uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { - return GenTexBuffCheckCode(ref_inst_itr, ref_block_itr, stage_idx, - new_blocks); - }; - modified |= InstProcessEntryPointCallTree(pfn); - } - return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; + + InstProcessEntryPointCallTree(pfn); + // This pass always changes the memory model, so that linking will work + // properly. + return Status::SuccessWithChange; } Pass::Status InstBindlessCheckPass::Process() {
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h index f89af02..243cba7 100644 --- a/source/opt/inst_bindless_check_pass.h +++ b/source/opt/inst_bindless_check_pass.h
@@ -28,16 +28,8 @@ // external design may change as the layer evolves. class InstBindlessCheckPass : public InstrumentPass { public: - InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, - bool desc_idx_enable, bool desc_init_enable, - bool buffer_bounds_enable, bool texel_buffer_enable, - bool opt_direct_reads) - : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, - opt_direct_reads), - desc_idx_enabled_(desc_idx_enable), - desc_init_enabled_(desc_init_enable), - buffer_bounds_enabled_(buffer_bounds_enable), - texel_buffer_enabled_(texel_buffer_enable) {} + InstBindlessCheckPass(uint32_t shader_id) + : InstrumentPass(0, shader_id, true, true) {} ~InstBindlessCheckPass() override = default; @@ -47,82 +39,16 @@ const char* name() const override { return "inst-bindless-check-pass"; } private: - // These functions do bindless checking instrumentation on a single - // instruction which references through a descriptor (ie references into an - // image or buffer). Refer to Vulkan API for further information on - // descriptors. GenDescIdxCheckCode checks that an index into a descriptor - // array (array of images or buffers) is in-bounds. GenDescInitCheckCode - // checks that the referenced descriptor has been initialized, if the - // SPV_EXT_descriptor_indexing extension is enabled, and initialized large - // enough to handle the reference, if RobustBufferAccess is disabled. - // GenDescInitCheckCode checks for uniform and storage buffer overrun. - // GenTexBuffCheckCode checks for texel buffer overrun and should be - // run after GenDescInitCheckCode to first make sure that the descriptor - // is initialized because it uses OpImageQuerySize on the descriptor. - // - // The functions are designed to be passed to - // InstrumentPass::InstProcessEntryPointCallTree(), which applies the - // function to each instruction in a module and replaces the instruction - // if warranted. - // - // If |ref_inst_itr| is a bindless reference, return in |new_blocks| the - // result of instrumenting it with validation code within its block at - // |ref_block_itr|. The validation code first executes a check for the - // specific condition called for. If the check passes, it executes - // the remainder of the reference, otherwise writes a record to the debug - // output buffer stream including |function_idx, instruction_idx, stage_idx| - // and replaces the reference with the null value of the original type. The - // block at |ref_block_itr| can just be replaced with the blocks in - // |new_blocks|, which will contain at least two blocks. The last block will - // comprise all instructions following |ref_inst_itr|, - // preceded by a phi instruction. - // - // These instrumentation functions utilize GenDebugDirectRead() to read data - // from the debug input buffer, specifically the lengths of variable length - // descriptor arrays, and the initialization status of each descriptor. - // The format of the debug input buffer is documented in instrument.hpp. - // - // These instrumentation functions utilize GenDebugStreamWrite() to write its - // error records. The validation-specific part of the error record will - // have the format: - // - // Validation Error Code (=kInstErrorBindlessBounds) - // Descriptor Index - // Descriptor Array Size - // - // The Descriptor Index is the index which has been determined to be - // out-of-bounds. - // - // The Descriptor Array Size is the size of the descriptor array which was - // indexed. - void GenDescIdxCheckCode( - BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks); + void GenDescCheckCode(BasicBlock::iterator ref_inst_itr, + UptrVectorIterator<BasicBlock> ref_block_itr, + uint32_t stage_idx, + std::vector<std::unique_ptr<BasicBlock>>* new_blocks); - void GenDescInitCheckCode( - BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks); + uint32_t GenDescCheckFunctionId(); - void GenTexBuffCheckCode( - BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, - std::vector<std::unique_ptr<BasicBlock>>* new_blocks); - - void SetupInputBufferIds(); - uint32_t GenDebugReadLengthFunctionId(); - - // Generate instructions into |builder| to read length of runtime descriptor - // array |var_id| from debug input buffer and return id of value. - uint32_t GenDebugReadLength(uint32_t var_id, InstructionBuilder* builder); - - uint32_t GenDebugReadInitFunctionId(); - // Generate instructions into |builder| to read initialization status of - // descriptor array |image_id| at |index_id| from debug input buffer and - // return id of value. - uint32_t GenDebugReadInit(uint32_t image_id, uint32_t index_id, - InstructionBuilder* builder); + uint32_t GenDescCheckCall(uint32_t inst_idx, uint32_t stage_idx, + uint32_t var_id, uint32_t index_id, + uint32_t byte_offset, InstructionBuilder* builder); // Analysis data for descriptor reference components, generated by // AnalyzeDescriptorReference. It is necessary and sufficient for further @@ -179,8 +105,7 @@ // writes debug error output utilizing |ref|, |error_id|, |length_id| and // |stage_idx|. Generate merge block for valid and invalid branches. Kill // original reference. - void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t offset_id, - uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref, + void GenCheckCode(uint32_t check_id, RefAnalysis* ref, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); // Initialize state for instrumenting bindless checking @@ -190,30 +115,13 @@ // GenDescInitCheckCode to every instruction in module. Pass::Status ProcessImpl(); - // Enable instrumentation of runtime array length checking - bool desc_idx_enabled_; - - // Enable instrumentation of descriptor initialization checking - bool desc_init_enabled_; - - // Enable instrumentation of uniform and storage buffer overrun checking - bool buffer_bounds_enabled_; - - // Enable instrumentation of texel buffer overrun checking - bool texel_buffer_enabled_; - // Mapping from variable to descriptor set std::unordered_map<uint32_t, uint32_t> var2desc_set_; // Mapping from variable to binding std::unordered_map<uint32_t, uint32_t> var2binding_; - uint32_t read_length_func_id_{0}; - uint32_t read_init_func_id_{0}; - uint32_t desc_set_type_id_{0}; - uint32_t desc_set_ptr_id_{0}; - uint32_t input_buffer_struct_id_{0}; - uint32_t input_buffer_ptr_id_{0}; + uint32_t check_desc_func_id_{0}; }; } // namespace opt
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp index 4954706..e6c5508 100644 --- a/source/opt/inst_buff_addr_check_pass.cpp +++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -19,24 +19,6 @@ namespace spvtools { namespace opt { -bool InstBuffAddrCheckPass::InstrumentFunction(Function* func, - uint32_t stage_idx, - InstProcessFunction& pfn) { - // The bindless instrumentation pass adds functions that use - // BufferDeviceAddress They should not be instrumented by this pass. - Instruction* func_name_inst = - context()->GetNames(func->DefInst().result_id()).begin()->second; - if (func_name_inst) { - static const std::string kPrefix{"inst_bindless_"}; - std::string func_name = func_name_inst->GetOperand(1).AsString(); - if (func_name.size() >= kPrefix.size() && - func_name.compare(0, kPrefix.size(), kPrefix) == 0) { - return false; - } - } - return InstrumentPass::InstrumentFunction(func, stage_idx, pfn); -} - uint32_t InstBuffAddrCheckPass::CloneOriginalReference( Instruction* ref_inst, InstructionBuilder* builder) { // Clone original ref with new result id (if load) @@ -76,8 +58,7 @@ // TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ?? void InstBuffAddrCheckPass::GenCheckCode( - uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id, - uint32_t stage_idx, Instruction* ref_inst, + uint32_t check_id, Instruction* ref_inst, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { BasicBlock* back_blk_ptr = &*new_blocks->back(); InstructionBuilder builder( @@ -104,18 +85,6 @@ // Gen invalid block new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); builder.SetInsertPoint(&*new_blk_ptr); - // Convert uptr from uint64 to 2 uint32 - Instruction* lo_uptr_inst = - builder.AddUnaryOp(GetUintId(), spv::Op::OpUConvert, ref_uptr_id); - Instruction* rshift_uptr_inst = - builder.AddBinaryOp(GetUint64Id(), spv::Op::OpShiftRightLogical, - ref_uptr_id, builder.GetUintConstantId(32)); - Instruction* hi_uptr_inst = builder.AddUnaryOp( - GetUintId(), spv::Op::OpUConvert, rshift_uptr_inst->result_id()); - GenDebugStreamWrite( - uid2offset_[ref_inst->unique_id()], stage_idx, - {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()}, - &builder); // Gen zero for invalid load. If pointer type, need to convert uint64 // zero to pointer; cannot create ConstantNull of pointer type. uint32_t null_id = 0; @@ -150,48 +119,13 @@ context()->KillInst(ref_inst); } -uint32_t InstBuffAddrCheckPass::GetTypeAlignment(uint32_t type_id) { - Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); - switch (type_inst->opcode()) { - case spv::Op::OpTypeFloat: - case spv::Op::OpTypeInt: - case spv::Op::OpTypeVector: - return GetTypeLength(type_id); - case spv::Op::OpTypeMatrix: - return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); - case spv::Op::OpTypeArray: - case spv::Op::OpTypeRuntimeArray: - return GetTypeAlignment(type_inst->GetSingleWordInOperand(0)); - case spv::Op::OpTypeStruct: { - uint32_t max = 0; - type_inst->ForEachInId([&max, this](const uint32_t* iid) { - uint32_t alignment = GetTypeAlignment(*iid); - max = (alignment > max) ? alignment : max; - }); - return max; - } - case spv::Op::OpTypePointer: - assert(spv::StorageClass(type_inst->GetSingleWordInOperand(0)) == - spv::StorageClass::PhysicalStorageBufferEXT && - "unexpected pointer type"); - return 8u; - default: - assert(false && "unexpected type"); - return 0; - } -} - uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case spv::Op::OpTypeFloat: case spv::Op::OpTypeInt: return type_inst->GetSingleWordInOperand(0) / 8u; - case spv::Op::OpTypeVector: { - uint32_t raw_cnt = type_inst->GetSingleWordInOperand(1); - uint32_t adj_cnt = (raw_cnt == 3u) ? 4u : raw_cnt; - return adj_cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); - } + case spv::Op::OpTypeVector: case spv::Op::OpTypeMatrix: return type_inst->GetSingleWordInOperand(1) * GetTypeLength(type_inst->GetSingleWordInOperand(0)); @@ -207,18 +141,19 @@ return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0)); } case spv::Op::OpTypeStruct: { - uint32_t len = 0; - type_inst->ForEachInId([&len, this](const uint32_t* iid) { - // Align struct length - uint32_t alignment = GetTypeAlignment(*iid); - uint32_t mod = len % alignment; - uint32_t diff = (mod != 0) ? alignment - mod : 0; - len += diff; - // Increment struct length by component length - uint32_t comp_len = GetTypeLength(*iid); - len += comp_len; + // Figure out the location of the last byte of the last member of the + // structure. + uint32_t last_offset = 0, last_len = 0; + + get_decoration_mgr()->ForEachDecoration( + type_id, uint32_t(spv::Decoration::Offset), + [&last_offset](const Instruction& deco_inst) { + last_offset = deco_inst.GetSingleWordInOperand(3); + }); + type_inst->ForEachInId([&last_len, this](const uint32_t* iid) { + last_len = GetTypeLength(*iid); }); - return len; + return last_offset + last_len; } case spv::Op::OpTypeRuntimeArray: default: @@ -238,201 +173,86 @@ (*input_func)->AddParameter(std::move(param_inst)); } +// This is a stub function for use with Import linkage +// clang-format off +// GLSL: +//bool inst_bindless_search_and_test(const uint shader_id, const uint inst_num, const uvec4 stage_info, +// const uint64 ref_ptr, const uint length) { +//} +// clang-format on uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() { - if (search_test_func_id_ == 0) { - // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)" - // which searches input buffer for buffer which most likely contains the - // pointer value |ref_ptr| and verifies that the entire reference of - // length |len| bytes is contained in the buffer. - search_test_func_id_ = TakeNextId(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - std::vector<const analysis::Type*> param_types = { - type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())}; - analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types); - analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); - std::unique_ptr<Instruction> func_inst( - new Instruction(get_module()->context(), spv::Op::OpFunction, - GetBoolId(), search_test_func_id_, - {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, - {uint32_t(spv::FunctionControlMask::MaskNone)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {type_mgr->GetTypeInstruction(reg_func_ty)}}})); - get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); - std::unique_ptr<Function> input_func = - MakeUnique<Function>(std::move(func_inst)); - std::vector<uint32_t> param_vec; - // Add ref_ptr and length parameters - AddParam(GetUint64Id(), ¶m_vec, &input_func); - AddParam(GetUintId(), ¶m_vec, &input_func); - // Empty first block. - uint32_t first_blk_id = TakeNextId(); - std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id)); - std::unique_ptr<BasicBlock> first_blk_ptr = - MakeUnique<BasicBlock>(std::move(first_blk_label)); - InstructionBuilder builder( - context(), &*first_blk_ptr, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - uint32_t hdr_blk_id = TakeNextId(); - // Branch to search loop header - std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id)); - (void)builder.AddBranch(hdr_blk_id); - input_func->AddBasicBlock(std::move(first_blk_ptr)); - // Linear search loop header block - // TODO(greg-lunarg): Implement binary search - std::unique_ptr<BasicBlock> hdr_blk_ptr = - MakeUnique<BasicBlock>(std::move(hdr_blk_label)); - builder.SetInsertPoint(&*hdr_blk_ptr); - // Phi for search index. Starts with 1. - uint32_t cont_blk_id = TakeNextId(); - std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id)); - // Deal with def-use cycle caused by search loop index computation. - // Create Add and Phi instructions first, then do Def analysis on Add. - // Add Phi and Add instructions and do Use analysis later. - uint32_t idx_phi_id = TakeNextId(); - uint32_t idx_inc_id = TakeNextId(); - std::unique_ptr<Instruction> idx_inc_inst(new Instruction( - context(), spv::Op::OpIAdd, GetUintId(), idx_inc_id, - {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {builder.GetUintConstantId(1u)}}})); - std::unique_ptr<Instruction> idx_phi_inst(new Instruction( - context(), spv::Op::OpPhi, GetUintId(), idx_phi_id, - {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {builder.GetUintConstantId(1u)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); - get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst); - // Add (previously created) search index phi - (void)builder.AddInstruction(std::move(idx_phi_inst)); - // LoopMerge - uint32_t bound_test_blk_id = TakeNextId(); - std::unique_ptr<Instruction> bound_test_blk_label( - NewLabel(bound_test_blk_id)); - (void)builder.AddLoopMerge(bound_test_blk_id, cont_blk_id, - uint32_t(spv::LoopControlMask::MaskNone)); - // Branch to continue/work block - (void)builder.AddBranch(cont_blk_id); - input_func->AddBasicBlock(std::move(hdr_blk_ptr)); - // Continue/Work Block. Read next buffer pointer and break if greater - // than ref_ptr arg. - std::unique_ptr<BasicBlock> cont_blk_ptr = - MakeUnique<BasicBlock>(std::move(cont_blk_label)); - builder.SetInsertPoint(&*cont_blk_ptr); - // Add (previously created) search index increment now. - (void)builder.AddInstruction(std::move(idx_inc_inst)); - // Load next buffer address from debug input buffer - uint32_t ibuf_id = GetInputBufferId(); - uint32_t ibuf_ptr_id = GetInputBufferPtrId(); - Instruction* uptr_ac_inst = builder.AddTernaryOp( - ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id, - builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id); - uint32_t ibuf_type_id = GetInputBufferTypeId(); - Instruction* uptr_load_inst = builder.AddUnaryOp( - ibuf_type_id, spv::Op::OpLoad, uptr_ac_inst->result_id()); - // If loaded address greater than ref_ptr arg, break, else branch back to - // loop header - Instruction* uptr_test_inst = - builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThan, - uptr_load_inst->result_id(), param_vec[0]); - (void)builder.AddConditionalBranch( - uptr_test_inst->result_id(), bound_test_blk_id, hdr_blk_id, kInvalidId, - uint32_t(spv::SelectionControlMask::MaskNone)); - input_func->AddBasicBlock(std::move(cont_blk_ptr)); - // Bounds test block. Read length of selected buffer and test that - // all len arg bytes are in buffer. - std::unique_ptr<BasicBlock> bound_test_blk_ptr = - MakeUnique<BasicBlock>(std::move(bound_test_blk_label)); - builder.SetInsertPoint(&*bound_test_blk_ptr); - // Decrement index to point to previous/candidate buffer address - Instruction* cand_idx_inst = - builder.AddBinaryOp(GetUintId(), spv::Op::OpISub, idx_inc_id, - builder.GetUintConstantId(1u)); - // Load candidate buffer address - Instruction* cand_ac_inst = - builder.AddTernaryOp(ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id, - builder.GetUintConstantId(kDebugInputDataOffset), - cand_idx_inst->result_id()); - Instruction* cand_load_inst = builder.AddUnaryOp( - ibuf_type_id, spv::Op::OpLoad, cand_ac_inst->result_id()); - // Compute offset of ref_ptr from candidate buffer address - Instruction* offset_inst = - builder.AddBinaryOp(ibuf_type_id, spv::Op::OpISub, param_vec[0], - cand_load_inst->result_id()); - // Convert ref length to uint64 - Instruction* ref_len_64_inst = - builder.AddUnaryOp(ibuf_type_id, spv::Op::OpUConvert, param_vec[1]); - // Add ref length to ref offset to compute end of reference - Instruction* ref_end_inst = builder.AddBinaryOp( - ibuf_type_id, spv::Op::OpIAdd, offset_inst->result_id(), - ref_len_64_inst->result_id()); - // Load starting index of lengths in input buffer and convert to uint32 - Instruction* len_start_ac_inst = - builder.AddTernaryOp(ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id, - builder.GetUintConstantId(kDebugInputDataOffset), - builder.GetUintConstantId(0u)); - Instruction* len_start_load_inst = builder.AddUnaryOp( - ibuf_type_id, spv::Op::OpLoad, len_start_ac_inst->result_id()); - Instruction* len_start_32_inst = builder.AddUnaryOp( - GetUintId(), spv::Op::OpUConvert, len_start_load_inst->result_id()); - // Decrement search index to get candidate buffer length index - Instruction* cand_len_idx_inst = builder.AddBinaryOp( - GetUintId(), spv::Op::OpISub, cand_idx_inst->result_id(), - builder.GetUintConstantId(1u)); - // Add candidate length index to start index - Instruction* len_idx_inst = builder.AddBinaryOp( - GetUintId(), spv::Op::OpIAdd, cand_len_idx_inst->result_id(), - len_start_32_inst->result_id()); - // Load candidate buffer length - Instruction* len_ac_inst = - builder.AddTernaryOp(ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id, - builder.GetUintConstantId(kDebugInputDataOffset), - len_idx_inst->result_id()); - Instruction* len_load_inst = builder.AddUnaryOp( - ibuf_type_id, spv::Op::OpLoad, len_ac_inst->result_id()); - // Test if reference end within candidate buffer length - Instruction* len_test_inst = builder.AddBinaryOp( - GetBoolId(), spv::Op::OpULessThanEqual, ref_end_inst->result_id(), - len_load_inst->result_id()); - // Return test result - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, - len_test_inst->result_id()); - // Close block - input_func->AddBasicBlock(std::move(bound_test_blk_ptr)); - // Close function and add function to module - std::unique_ptr<Instruction> func_end_inst(new Instruction( - get_module()->context(), spv::Op::OpFunctionEnd, 0, 0, {})); - get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); - input_func->SetFunctionEnd(std::move(func_end_inst)); - context()->AddFunction(std::move(input_func)); - context()->AddDebug2Inst( - NewGlobalName(search_test_func_id_, "search_and_test")); + enum { + kShaderId = 0, + kInstructionIndex = 1, + kStageInfo = 2, + kRefPtr = 3, + kLength = 4, + kNumArgs + }; + if (search_test_func_id_ != 0) { + return search_test_func_id_; } + // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)" + // which searches input buffer for buffer which most likely contains the + // pointer value |ref_ptr| and verifies that the entire reference of + // length |len| bytes is contained in the buffer. + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Integer* uint_type = GetInteger(32, false); + const analysis::Vector v4uint(uint_type, 4); + const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint); + + std::vector<const analysis::Type*> param_types = { + uint_type, uint_type, v4uint_type, type_mgr->GetType(GetUint64Id()), + uint_type}; + + const std::string func_name{"inst_buff_addr_search_and_test"}; + const uint32_t func_id = TakeNextId(); + std::unique_ptr<Function> func = + StartFunction(func_id, type_mgr->GetBoolType(), param_types); + func->SetFunctionEnd(EndFunction()); + context()->AddFunctionDeclaration(std::move(func)); + context()->AddDebug2Inst(NewName(func_id, func_name)); + + std::vector<Operand> operands{ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {uint32_t(spv::Decoration::LinkageAttributes)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING, + utils::MakeVector(func_name.c_str())}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE, + {uint32_t(spv::LinkageType::Import)}}, + }; + get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands); + + search_test_func_id_ = func_id; return search_test_func_id_; } uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder, - uint32_t* ref_uptr_id) { + uint32_t* ref_uptr_id, + uint32_t stage_idx) { // Enable Int64 if necessary - context()->AddCapability(spv::Capability::Int64); // Convert reference pointer to uint64 - uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0); + const uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0); Instruction* ref_uptr_inst = builder->AddUnaryOp(GetUint64Id(), spv::Op::OpConvertPtrToU, ref_ptr_id); *ref_uptr_id = ref_uptr_inst->result_id(); // Compute reference length in bytes analysis::DefUseManager* du_mgr = get_def_use_mgr(); Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id); - uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id(); + const uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id(); Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id); - uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1)); - uint32_t ref_len_id = builder->GetUintConstantId(ref_len); + const uint32_t ref_len = + GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1)); // Gen call to search and test function - Instruction* call_inst = builder->AddFunctionCall( - GetBoolId(), GetSearchAndTestFuncId(), {*ref_uptr_id, ref_len_id}); - uint32_t retval = call_inst->result_id(); - return retval; + const uint32_t func_id = GetSearchAndTestFuncId(); + const std::vector<uint32_t> args = { + builder->GetUintConstantId(shader_id_), + builder->GetUintConstantId(ref_inst->unique_id()), + GenStageInfo(stage_idx, builder), *ref_uptr_id, + builder->GetUintConstantId(ref_len)}; + return GenReadFunctionCall(GetBoolId(), func_id, args, builder); } void InstBuffAddrCheckPass::GenBuffAddrCheckCode( @@ -450,16 +270,16 @@ context(), &*new_blk_ptr, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); new_blocks->push_back(std::move(new_blk_ptr)); - uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef); // Generate code to do search and test if all bytes of reference // are within a listed buffer. Return reference pointer converted to uint64. uint32_t ref_uptr_id; - uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id); + uint32_t valid_id = + GenSearchAndTest(ref_inst, &builder, &ref_uptr_id, stage_idx); // Generate test of search results with true branch // being full reference and false branch being debug output and zero // for the referenced value. - GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst, - new_blocks); + GenCheckCode(valid_id, ref_inst, new_blocks); + // Move original block's remaining code into remainder/merge block and add // to new blocks BasicBlock* back_blk_ptr = &*new_blocks->back(); @@ -474,6 +294,20 @@ } Pass::Status InstBuffAddrCheckPass::ProcessImpl() { + // The memory model and linkage must always be updated for spirv-link to work + // correctly. + AddStorageBufferExt(); + if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) { + context()->AddExtension("SPV_KHR_physical_storage_buffer"); + } + + context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses); + Instruction* memory_model = get_module()->GetMemoryModel(); + memory_model->SetInOperand( + 0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)}); + + context()->AddCapability(spv::Capability::Int64); + context()->AddCapability(spv::Capability::Linkage); // Perform bindless bounds check on each entry point function in module InstProcessFunction pfn = [this](BasicBlock::iterator ref_inst_itr, @@ -482,14 +316,13 @@ return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx, new_blocks); }; - bool modified = InstProcessEntryPointCallTree(pfn); - return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; + InstProcessEntryPointCallTree(pfn); + // This pass always changes the memory model, so that linking will work + // properly. + return Status::SuccessWithChange; } Pass::Status InstBuffAddrCheckPass::Process() { - if (!get_feature_mgr()->HasCapability( - spv::Capability::PhysicalStorageBufferAddressesEXT)) - return Status::SuccessWithoutChange; InitInstBuffAddrCheck(); return ProcessImpl(); }
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h index 2ec212b..f07f98a 100644 --- a/source/opt/inst_buff_addr_check_pass.h +++ b/source/opt/inst_buff_addr_check_pass.h
@@ -29,10 +29,10 @@ class InstBuffAddrCheckPass : public InstrumentPass { public: // For test harness only - InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {} + InstBuffAddrCheckPass() : InstrumentPass(0, 23, false, true) {} // For all other interfaces - InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id) - : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {} + InstBuffAddrCheckPass(uint32_t shader_id) + : InstrumentPass(0, shader_id, false, true) {} ~InstBuffAddrCheckPass() override = default; @@ -41,14 +41,7 @@ const char* name() const override { return "inst-buff-addr-check-pass"; } - bool InstrumentFunction(Function* func, uint32_t stage_idx, - InstProcessFunction& pfn) override; - private: - // Return byte alignment of type |type_id|. Must be int, float, vector, - // matrix, struct, array or physical pointer. Uses std430 alignment. - uint32_t GetTypeAlignment(uint32_t type_id); - // Return byte length of type |type_id|. Must be int, float, vector, matrix, // struct, array or physical pointer. Uses std430 alignment and sizes. uint32_t GetTypeLength(uint32_t type_id); @@ -65,7 +58,7 @@ // are within the buffer. Returns id of boolean value which is true if // search and test is successful, false otherwise. uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder, - uint32_t* ref_uptr_id); + uint32_t* ref_uptr_id, uint32_t stage_idx); // This function does checking instrumentation on a single // instruction which references through a physical storage buffer address. @@ -118,8 +111,7 @@ // writes debug error output utilizing |ref_inst|, |error_id| and // |stage_idx|. Generate merge block for valid and invalid reference blocks. // Kill original reference. - void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id, - uint32_t stage_idx, Instruction* ref_inst, + void GenCheckCode(uint32_t check_id, Instruction* ref_inst, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); // Initialize state for instrumenting physical buffer address checking
diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp index 4f97277..abd25e9 100644 --- a/source/opt/inst_debug_printf_pass.cpp +++ b/source/opt/inst_debug_printf_pass.cpp
@@ -16,6 +16,7 @@ #include "inst_debug_printf_pass.h" +#include "source/spirv_constant.h" #include "source/util/string_utils.h" #include "spirv/unified1/NonSemanticDebugPrintf.h" @@ -137,7 +138,7 @@ } void InstDebugPrintfPass::GenOutputCode( - Instruction* printf_inst, uint32_t stage_idx, + Instruction* printf_inst, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { BasicBlock* back_blk_ptr = &*new_blocks->back(); InstructionBuilder builder( @@ -165,14 +166,16 @@ GenOutputValues(opnd_inst, &val_ids, &builder); } }); - GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids, - &builder); + GenDebugStreamWrite( + builder.GetUintConstantId(shader_id_), + builder.GetUintConstantId(uid2offset_[printf_inst->unique_id()]), val_ids, + &builder); context()->KillInst(printf_inst); } void InstDebugPrintfPass::GenDebugPrintfCode( BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, + UptrVectorIterator<BasicBlock> ref_block_itr, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { // If not DebugPrintf OpExtInst, return. Instruction* printf_inst = &*ref_inst_itr; @@ -188,7 +191,7 @@ MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); new_blocks->push_back(std::move(new_blk_ptr)); // Generate instructions to output printf args to printf buffer - GenOutputCode(printf_inst, stage_idx, new_blocks); + GenOutputCode(printf_inst, new_blocks); // Caller expects at least two blocks with last block containing remaining // code, so end block after instrumentation, create remainder block, and // branch to it @@ -208,19 +211,243 @@ new_blocks->push_back(std::move(new_blk_ptr)); } +// Return id for output buffer +uint32_t InstDebugPrintfPass::GetOutputBufferId() { + if (output_buffer_id_ == 0) { + // If not created yet, create one + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::RuntimeArray* reg_uint_rarr_ty = GetUintRuntimeArrayType(32); + analysis::Integer* reg_uint_ty = GetInteger(32, false); + analysis::Type* reg_buf_ty = + GetStruct({reg_uint_ty, reg_uint_ty, reg_uint_rarr_ty}); + uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); + // By the Vulkan spec, a pre-existing struct containing a RuntimeArray + // must be a block, and will therefore be decorated with Block. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && + "used struct type returned"); + deco_mgr->AddDecoration(obufTyId, uint32_t(spv::Decoration::Block)); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputFlagsOffset, + uint32_t(spv::Decoration::Offset), 0); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, + uint32_t(spv::Decoration::Offset), 4); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, + uint32_t(spv::Decoration::Offset), 8); + uint32_t obufTyPtrId_ = + type_mgr->FindPointerToType(obufTyId, spv::StorageClass::StorageBuffer); + output_buffer_id_ = TakeNextId(); + std::unique_ptr<Instruction> newVarOp(new Instruction( + context(), spv::Op::OpVariable, obufTyPtrId_, output_buffer_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {uint32_t(spv::StorageClass::StorageBuffer)}}})); + context()->AddGlobalValue(std::move(newVarOp)); + context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer")); + context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "flags")); + context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "written_count")); + context()->AddDebug2Inst(NewMemberName(obufTyId, 2, "data")); + context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer")); + deco_mgr->AddDecorationVal( + output_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_); + deco_mgr->AddDecorationVal(output_buffer_id_, + uint32_t(spv::Decoration::Binding), + GetOutputBufferBinding()); + AddStorageBufferExt(); + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // Add the new buffer to all entry points. + for (auto& entry : get_module()->entry_points()) { + entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}}); + context()->AnalyzeUses(&entry); + } + } + } + return output_buffer_id_; +} + +uint32_t InstDebugPrintfPass::GetOutputBufferPtrId() { + if (output_buffer_ptr_id_ == 0) { + output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( + GetUintId(), spv::StorageClass::StorageBuffer); + } + return output_buffer_ptr_id_; +} + +uint32_t InstDebugPrintfPass::GetOutputBufferBinding() { + return kDebugOutputPrintfStream; +} + +void InstDebugPrintfPass::GenDebugOutputFieldCode(uint32_t base_offset_id, + uint32_t field_offset, + uint32_t field_value_id, + InstructionBuilder* builder) { + // Cast value to 32-bit unsigned if necessary + uint32_t val_id = GenUintCastCode(field_value_id, builder); + // Store value + Instruction* data_idx_inst = builder->AddIAdd( + GetUintId(), base_offset_id, builder->GetUintConstantId(field_offset)); + uint32_t buf_id = GetOutputBufferId(); + uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); + Instruction* achain_inst = builder->AddAccessChain( + buf_uint_ptr_id, buf_id, + {builder->GetUintConstantId(kDebugOutputDataOffset), + data_idx_inst->result_id()}); + (void)builder->AddStore(achain_inst->result_id(), val_id); +} + +uint32_t InstDebugPrintfPass::GetStreamWriteFunctionId(uint32_t param_cnt) { + enum { + kShaderId = 0, + kInstructionIndex = 1, + kFirstParam = 2, + }; + // Total param count is common params plus validation-specific + // params + if (param2output_func_id_[param_cnt] == 0) { + // Create function + param2output_func_id_[param_cnt] = TakeNextId(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + + const analysis::Type* uint_type = GetInteger(32, false); + + std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt, + uint_type); + std::unique_ptr<Function> output_func = StartFunction( + param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types); + + std::vector<uint32_t> param_ids = AddParameters(*output_func, param_types); + + // Create first block + auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId())); + + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen test if debug output buffer size will not be exceeded. + const uint32_t first_param_offset = kInstCommonOutInstructionIdx + 1; + const uint32_t obuf_record_sz = first_param_offset + param_cnt; + const uint32_t buf_id = GetOutputBufferId(); + const uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); + Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain( + buf_uint_ptr_id, buf_id, + {builder.GetUintConstantId(kDebugOutputSizeOffset)}); + // Fetch the current debug buffer written size atomically, adding the + // size of the record to be written. + uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); + uint32_t mask_none_id = + builder.GetUintConstantId(uint32_t(spv::MemoryAccessMask::MaskNone)); + uint32_t scope_invok_id = + builder.GetUintConstantId(uint32_t(spv::Scope::Invocation)); + Instruction* obuf_curr_sz_inst = builder.AddQuadOp( + GetUintId(), spv::Op::OpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), + scope_invok_id, mask_none_id, obuf_record_sz_id); + uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); + // Compute new written size + Instruction* obuf_new_sz_inst = + builder.AddIAdd(GetUintId(), obuf_curr_sz_id, + builder.GetUintConstantId(obuf_record_sz)); + // Fetch the data bound + Instruction* obuf_bnd_inst = + builder.AddIdLiteralOp(GetUintId(), spv::Op::OpArrayLength, + GetOutputBufferId(), kDebugOutputDataOffset); + // Test that new written size is less than or equal to debug output + // data bound + Instruction* obuf_safe_inst = builder.AddBinaryOp( + GetBoolId(), spv::Op::OpULessThanEqual, obuf_new_sz_inst->result_id(), + obuf_bnd_inst->result_id()); + uint32_t merge_blk_id = TakeNextId(); + uint32_t write_blk_id = TakeNextId(); + std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); + std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id)); + (void)builder.AddConditionalBranch( + obuf_safe_inst->result_id(), write_blk_id, merge_blk_id, merge_blk_id, + uint32_t(spv::SelectionControlMask::MaskNone)); + // Close safety test block and gen write block + output_func->AddBasicBlock(std::move(new_blk_ptr)); + new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label)); + builder.SetInsertPoint(&*new_blk_ptr); + // Generate common and stage-specific debug record members + GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutSize, + builder.GetUintConstantId(obuf_record_sz), + &builder); + // Store Shader Id + GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutShaderId, + param_ids[kShaderId], &builder); + // Store Instruction Idx + GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutInstructionIdx, + param_ids[kInstructionIndex], &builder); + // Gen writes of validation specific data + for (uint32_t i = 0; i < param_cnt; ++i) { + GenDebugOutputFieldCode(obuf_curr_sz_id, first_param_offset + i, + param_ids[kFirstParam + i], &builder); + } + // Close write block and gen merge block + (void)builder.AddBranch(merge_blk_id); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); + builder.SetInsertPoint(&*new_blk_ptr); + // Close merge block and function and add function to module + (void)builder.AddNullaryOp(0, spv::Op::OpReturn); + + output_func->AddBasicBlock(std::move(new_blk_ptr)); + output_func->SetFunctionEnd(EndFunction()); + context()->AddFunction(std::move(output_func)); + + std::string name("stream_write_"); + name += std::to_string(param_cnt); + + context()->AddDebug2Inst( + NewGlobalName(param2output_func_id_[param_cnt], name)); + } + return param2output_func_id_[param_cnt]; +} + +void InstDebugPrintfPass::GenDebugStreamWrite( + uint32_t shader_id, uint32_t instruction_idx_id, + const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) { + // Call debug output function. Pass func_idx, instruction_idx and + // validation ids as args. + uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size()); + std::vector<uint32_t> args = {shader_id, instruction_idx_id}; + (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); + (void)builder->AddFunctionCall(GetVoidId(), + GetStreamWriteFunctionId(val_id_cnt), args); +} + +std::unique_ptr<Instruction> InstDebugPrintfPass::NewGlobalName( + uint32_t id, const std::string& name_str) { + std::string prefixed_name{"inst_printf_"}; + prefixed_name += name_str; + return NewName(id, prefixed_name); +} + +std::unique_ptr<Instruction> InstDebugPrintfPass::NewMemberName( + uint32_t id, uint32_t member_index, const std::string& name_str) { + return MakeUnique<Instruction>( + context(), spv::Op::OpMemberName, 0, 0, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}); +} + void InstDebugPrintfPass::InitializeInstDebugPrintf() { // Initialize base class InitializeInstrument(); + output_buffer_id_ = 0; + output_buffer_ptr_id_ = 0; } Pass::Status InstDebugPrintfPass::ProcessImpl() { // Perform printf instrumentation on each entry point function in module InstProcessFunction pfn = [this](BasicBlock::iterator ref_inst_itr, - UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx, + UptrVectorIterator<BasicBlock> ref_block_itr, + [[maybe_unused]] uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { - return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx, - new_blocks); + return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, new_blocks); }; (void)InstProcessEntryPointCallTree(pfn); // Remove DebugPrintf OpExtInstImport instruction @@ -239,15 +466,7 @@ } } if (!non_sem_set_seen) { - for (auto c_itr = context()->module()->extension_begin(); - c_itr != context()->module()->extension_end(); ++c_itr) { - const std::string ext_name = c_itr->GetInOperand(0).AsString(); - if (ext_name == "SPV_KHR_non_semantic_info") { - context()->KillInst(&*c_itr); - break; - } - } - context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info); + context()->RemoveExtension(kSPV_KHR_non_semantic_info); } return Status::SuccessWithChange; }
diff --git a/source/opt/inst_debug_printf_pass.h b/source/opt/inst_debug_printf_pass.h index 70b0a72..5688d38 100644 --- a/source/opt/inst_debug_printf_pass.h +++ b/source/opt/inst_debug_printf_pass.h
@@ -28,10 +28,10 @@ class InstDebugPrintfPass : public InstrumentPass { public: // For test harness only - InstDebugPrintfPass() : InstrumentPass(7, 23, kInstValidationIdDebugPrintf) {} + InstDebugPrintfPass() : InstrumentPass(7, 23, false, false) {} // For all other interfaces InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id) - : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf) {} + : InstrumentPass(desc_set, shader_id, false, false) {} ~InstDebugPrintfPass() override = default; @@ -41,12 +41,92 @@ const char* name() const override { return "inst-printf-pass"; } private: + // Gen code into |builder| to write |field_value_id| into debug output + // buffer at |base_offset_id| + |field_offset|. + void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset, + uint32_t field_value_id, + InstructionBuilder* builder); + + // Generate instructions in |builder| which will atomically fetch and + // increment the size of the debug output buffer stream of the current + // validation and write a record to the end of the stream, if enough space + // in the buffer remains. The record will contain the index of the function + // and instruction within that function |func_idx, instruction_idx| which + // generated the record. Finally, the record will contain validation-specific + // data contained in |validation_ids| which will identify the validation + // error as well as the values involved in the error. + // + // The output buffer binding written to by the code generated by the function + // is determined by the validation id specified when each specific + // instrumentation pass is created. + // + // The output buffer is a sequence of 32-bit values with the following + // format (where all elements are unsigned 32-bit unless otherwise noted): + // + // Size + // Record0 + // Record1 + // Record2 + // ... + // + // Size is the number of 32-bit values that have been written or + // attempted to be written to the output buffer, excluding the Size. It is + // initialized to 0. If the size of attempts to write the buffer exceeds + // the actual size of the buffer, it is possible that this field can exceed + // the actual size of the buffer. + // + // Each Record* is a variable-length sequence of 32-bit values with the + // following format defined using static const offsets in the .cpp file: + // + // Record Size + // Shader ID + // Instruction Index + // ... + // Validation Error Code + // Validation-specific Word 0 + // Validation-specific Word 1 + // Validation-specific Word 2 + // ... + // + // Each record consists of two subsections: members common across all + // validation and members specific to a + // validation. + // + // The Record Size is the number of 32-bit words in the record, including + // the Record Size word. + // + // Shader ID is a value that identifies which shader has generated the + // validation error. It is passed when the instrumentation pass is created. + // + // The Instruction Index is the position of the instruction within the + // SPIR-V file which is in error. + // + // The Validation Error Code specifies the exact error which has occurred. + // These are enumerated with the kInstError* static consts. This allows + // multiple validation layers to use the same, single output buffer. + // + // The Validation-specific Words are a validation-specific number of 32-bit + // words which give further information on the validation error that + // occurred. These are documented further in each file containing the + // validation-specific class which derives from this base class. + // + // Because the code that is generated checks against the size of the buffer + // before writing, the size of the debug out buffer can be used by the + // validation layer to control the number of error records that are written. + void GenDebugStreamWrite(uint32_t shader_id, uint32_t instruction_idx_id, + const std::vector<uint32_t>& validation_ids, + InstructionBuilder* builder); + + // Return id for output function. Define if it doesn't exist with + // |val_spec_param_cnt| validation-specific uint32 parameters. + uint32_t GetStreamWriteFunctionId(uint32_t val_spec_param_cnt); + // Generate instructions for OpDebugPrintf. // // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result // of replacing it with buffer write instructions within its block at // |ref_block_itr|. The instructions write a record to the printf - // output buffer stream including |function_idx, instruction_idx, stage_idx| + // output buffer stream including |function_idx, instruction_idx| // and removes the OpDebugPrintf. The block at |ref_block_itr| can just be // replaced with the block in |new_blocks|. Besides the buffer writes, this // block will comprise all instructions preceding and following @@ -64,7 +144,6 @@ // DebugPrintf. void GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr, UptrVectorIterator<BasicBlock> ref_block_itr, - uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); // Generate a sequence of uint32 instructions in |builder| (if necessary) @@ -77,16 +156,40 @@ // Generate instructions to write a record containing the operands of // |printf_inst| arguments to printf buffer, adding new code to the end of // the last block in |new_blocks|. Kill OpDebugPrintf instruction. - void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx, + void GenOutputCode(Instruction* printf_inst, std::vector<std::unique_ptr<BasicBlock>>* new_blocks); + // Set the name for a function or global variable, names will be + // prefixed to identify which instrumentation pass generated them. + std::unique_ptr<Instruction> NewGlobalName(uint32_t id, + const std::string& name_str); + + // Set the name for a structure member + std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index, + const std::string& name_str); + + // Return id for debug output buffer + uint32_t GetOutputBufferId(); + + // Return id for buffer uint type + uint32_t GetOutputBufferPtrId(); + + // Return binding for output buffer for current validation. + uint32_t GetOutputBufferBinding(); + // Initialize state for instrumenting bindless checking void InitializeInstDebugPrintf(); // Apply GenDebugPrintfCode to every instruction in module. Pass::Status ProcessImpl(); - uint32_t ext_inst_printf_id_; + uint32_t ext_inst_printf_id_{0}; + + // id for output buffer variable + uint32_t output_buffer_id_{0}; + + // ptr type id for output buffer element + uint32_t output_buffer_ptr_id_{0}; }; } // namespace opt
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index ece6baf..aa4ae26 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp
@@ -751,7 +751,7 @@ } bool Instruction::IsFoldable() const { - return IsFoldableByFoldScalar() || + return IsFoldableByFoldScalar() || IsFoldableByFoldVector() || context()->get_instruction_folder().HasConstFoldingRule(this); } @@ -762,7 +762,7 @@ } Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); - if (!folder.IsFoldableType(type)) { + if (!folder.IsFoldableScalarType(type)) { return false; } @@ -773,7 +773,29 @@ 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()); - return folder.IsFoldableType(def_inst_type); + return folder.IsFoldableScalarType(def_inst_type); + }); +} + +bool Instruction::IsFoldableByFoldVector() const { + const InstructionFolder& folder = context()->get_instruction_folder(); + if (!folder.IsFoldableOpcode(opcode())) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); + if (!folder.IsFoldableVectorType(type)) { + return false; + } + + // 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) { + 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()); + return folder.IsFoldableVectorType(def_inst_type); }); }
diff --git a/source/opt/instruction.h b/source/opt/instruction.h index d50e625..c2617fb 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h
@@ -294,6 +294,8 @@ // It is the responsibility of the caller to make sure // that the instruction remains valid. inline void AddOperand(Operand&& operand); + // Adds a copy of |operand| to the list of operands of this instruction. + inline void AddOperand(const Operand& operand); // Gets the |index|-th logical operand as a single SPIR-V word. This method is // not expected to be used with logical operands consisting of multiple SPIR-V // words. @@ -522,6 +524,10 @@ // constant value by |FoldScalar|. bool IsFoldableByFoldScalar() const; + // Returns true if |this| is an instruction which could be folded into a + // constant value by |FoldVector|. + bool IsFoldableByFoldVector() const; + // Returns true if we are allowed to fold or otherwise manipulate the // instruction that defines |id| in the given context. This includes not // handling NaN values. @@ -676,6 +682,10 @@ operands_.push_back(std::move(operand)); } +inline void Instruction::AddOperand(const Operand& operand) { + operands_.push_back(operand); +} + inline void Instruction::SetInOperand(uint32_t index, Operand::OperandData&& data) { SetOperand(index + TypeResultIdCount(), std::move(data));
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp index 9233ffd..b6845a5 100644 --- a/source/opt/instrument_pass.cpp +++ b/source/opt/instrument_pass.cpp
@@ -22,9 +22,6 @@ namespace spvtools { namespace opt { namespace { -// Common Parameter Positions -constexpr int kInstCommonParamInstIdx = 0; -constexpr int kInstCommonParamCnt = 1; // Indices of operands in SPIR-V instructions constexpr int kEntryPointFunctionIdInIdx = 1; } // namespace @@ -134,38 +131,6 @@ {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}); } -std::unique_ptr<Instruction> InstrumentPass::NewGlobalName( - uint32_t id, const std::string& name_str) { - std::string prefixed_name; - switch (validation_id_) { - case kInstValidationIdBindless: - prefixed_name = "inst_bindless_"; - break; - case kInstValidationIdBuffAddr: - prefixed_name = "inst_buff_addr_"; - break; - case kInstValidationIdDebugPrintf: - prefixed_name = "inst_printf_"; - break; - default: - assert(false); // add new instrumentation pass here - prefixed_name = "inst_pass_"; - break; - } - prefixed_name += name_str; - return NewName(id, prefixed_name); -} - -std::unique_ptr<Instruction> InstrumentPass::NewMemberName( - uint32_t id, uint32_t member_index, const std::string& name_str) { - return MakeUnique<Instruction>( - context(), spv::Op::OpMemberName, 0, 0, - std::initializer_list<Operand>{ - {SPV_OPERAND_TYPE_ID, {id}}, - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}}, - {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}); -} - uint32_t InstrumentPass::Gen32BitCvtCode(uint32_t val_id, InstructionBuilder* builder) { // Convert integer value to 32-bit if necessary @@ -198,52 +163,6 @@ ->result_id(); } -void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, - uint32_t field_offset, - uint32_t field_value_id, - InstructionBuilder* builder) { - // Cast value to 32-bit unsigned if necessary - uint32_t val_id = GenUintCastCode(field_value_id, builder); - // Store value - Instruction* data_idx_inst = builder->AddIAdd( - GetUintId(), base_offset_id, builder->GetUintConstantId(field_offset)); - uint32_t buf_id = GetOutputBufferId(); - uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); - Instruction* achain_inst = builder->AddAccessChain( - buf_uint_ptr_id, buf_id, - {builder->GetUintConstantId(kDebugOutputDataOffset), - data_idx_inst->result_id()}); - (void)builder->AddStore(achain_inst->result_id(), val_id); -} - -void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz, - uint32_t inst_id, - uint32_t stage_idx, - uint32_t base_offset_id, - InstructionBuilder* builder) { - // Store record size - GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize, - builder->GetUintConstantId(record_sz), builder); - // Store Shader Id - GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId, - builder->GetUintConstantId(shader_id_), builder); - // Store Instruction Idx - GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id, - builder); - // Store Stage Idx - GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx, - builder->GetUintConstantId(stage_idx), builder); -} - -void InstrumentPass::GenFragCoordEltDebugOutputCode( - uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element, - InstructionBuilder* builder) { - Instruction* element_val_inst = - builder->AddCompositeExtract(GetUintId(), uint_frag_coord_id, {element}); - GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element, - element_val_inst->result_id(), builder); -} - uint32_t InstrumentPass::GenVarLoad(uint32_t var_id, InstructionBuilder* builder) { Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); @@ -252,28 +171,24 @@ return load_inst->result_id(); } -void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id, - uint32_t builtin_off, - uint32_t base_offset_id, - InstructionBuilder* builder) { - // Load and store builtin - uint32_t load_id = GenVarLoad(builtin_id, builder); - GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder); -} - -void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, - uint32_t base_offset_id, - InstructionBuilder* builder) { +uint32_t InstrumentPass::GenStageInfo(uint32_t stage_idx, + InstructionBuilder* builder) { + std::vector<uint32_t> ids(4, builder->GetUintConstantId(0)); + ids[0] = builder->GetUintConstantId(stage_idx); + // %289 = OpCompositeConstruct %v4uint %uint_0 %285 %288 %uint_0 // TODO(greg-lunarg): Add support for all stages switch (spv::ExecutionModel(stage_idx)) { case spv::ExecutionModel::Vertex: { // Load and store VertexId and InstanceId - GenBuiltinOutputCode( + uint32_t load_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::VertexIndex)), - kInstVertOutVertexIndex, base_offset_id, builder); - GenBuiltinOutputCode(context()->GetBuiltinInputVarId( + builder); + ids[1] = GenUintCastCode(load_id, builder); + + load_id = GenVarLoad(context()->GetBuiltinInputVarId( uint32_t(spv::BuiltIn::InstanceIndex)), - kInstVertOutInstanceIndex, base_offset_id, builder); + builder); + ids[2] = GenUintCastCode(load_id, builder); } break; case spv::ExecutionModel::GLCompute: case spv::ExecutionModel::TaskNV: @@ -284,56 +199,50 @@ uint32_t load_id = GenVarLoad(context()->GetBuiltinInputVarId(uint32_t( spv::BuiltIn::GlobalInvocationId)), builder); - Instruction* x_inst = - builder->AddCompositeExtract(GetUintId(), load_id, {0}); - Instruction* y_inst = - builder->AddCompositeExtract(GetUintId(), load_id, {1}); - Instruction* z_inst = - builder->AddCompositeExtract(GetUintId(), load_id, {2}); - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, - x_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, - y_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, - z_inst->result_id(), builder); + for (uint32_t u = 0; u < 3u; ++u) { + ids[u + 1] = builder->AddCompositeExtract(GetUintId(), load_id, {u}) + ->result_id(); + } } break; case spv::ExecutionModel::Geometry: { // Load and store PrimitiveId and InvocationId. - GenBuiltinOutputCode( + uint32_t load_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)), - kInstGeomOutPrimitiveId, base_offset_id, builder); - GenBuiltinOutputCode( + builder); + ids[1] = load_id; + load_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::InvocationId)), - kInstGeomOutInvocationId, base_offset_id, builder); + builder); + ids[2] = GenUintCastCode(load_id, builder); } break; case spv::ExecutionModel::TessellationControl: { // Load and store InvocationId and PrimitiveId - GenBuiltinOutputCode( + uint32_t load_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::InvocationId)), - kInstTessCtlOutInvocationId, base_offset_id, builder); - GenBuiltinOutputCode( + builder); + ids[1] = GenUintCastCode(load_id, builder); + load_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)), - kInstTessCtlOutPrimitiveId, base_offset_id, builder); + builder); + ids[2] = load_id; } break; case spv::ExecutionModel::TessellationEvaluation: { // Load and store PrimitiveId and TessCoord.uv - GenBuiltinOutputCode( - context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)), - kInstTessEvalOutPrimitiveId, base_offset_id, builder); uint32_t load_id = GenVarLoad( + context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::PrimitiveId)), + builder); + ids[1] = load_id; + load_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::TessCoord)), builder); Instruction* uvec3_cast_inst = builder->AddUnaryOp(GetVec3UintId(), spv::Op::OpBitcast, load_id); uint32_t uvec3_cast_id = uvec3_cast_inst->result_id(); - Instruction* u_inst = - builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {0}); - Instruction* v_inst = - builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {1}); - GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, - u_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, - v_inst->result_id(), builder); + for (uint32_t u = 0; u < 2u; ++u) { + ids[u + 2] = + builder->AddCompositeExtract(GetUintId(), uvec3_cast_id, {u}) + ->result_id(); + } } break; case spv::ExecutionModel::Fragment: { // Load FragCoord and convert to Uint @@ -342,9 +251,13 @@ context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::FragCoord))); Instruction* uint_frag_coord_inst = builder->AddUnaryOp( GetVec4UintId(), spv::Op::OpBitcast, frag_coord_inst->result_id()); - for (uint32_t u = 0; u < 2u; ++u) - GenFragCoordEltDebugOutputCode( - base_offset_id, uint_frag_coord_inst->result_id(), u, builder); + for (uint32_t u = 0; u < 2u; ++u) { + ids[u + 1] = + builder + ->AddCompositeExtract(GetUintId(), + uint_frag_coord_inst->result_id(), {u}) + ->result_id(); + } } break; case spv::ExecutionModel::RayGenerationNV: case spv::ExecutionModel::IntersectionNV: @@ -356,33 +269,14 @@ uint32_t launch_id = GenVarLoad( context()->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::LaunchIdNV)), builder); - Instruction* x_launch_inst = - builder->AddCompositeExtract(GetUintId(), launch_id, {0}); - Instruction* y_launch_inst = - builder->AddCompositeExtract(GetUintId(), launch_id, {1}); - Instruction* z_launch_inst = - builder->AddCompositeExtract(GetUintId(), launch_id, {2}); - GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX, - x_launch_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY, - y_launch_inst->result_id(), builder); - GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ, - z_launch_inst->result_id(), builder); + for (uint32_t u = 0; u < 3u; ++u) { + ids[u + 1] = builder->AddCompositeExtract(GetUintId(), launch_id, {u}) + ->result_id(); + } } break; default: { assert(false && "unsupported stage"); } break; } -} - -void InstrumentPass::GenDebugStreamWrite( - uint32_t instruction_idx, uint32_t stage_idx, - const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) { - // Call debug output function. Pass func_idx, instruction_idx and - // validation ids as args. - uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size()); - std::vector<uint32_t> args = {builder->GetUintConstantId(instruction_idx)}; - (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); - (void)builder->AddFunctionCall( - GetVoidId(), GetStreamWriteFunctionId(stage_idx, val_id_cnt), args); + return builder->AddCompositeConstruct(GetVec4UintId(), ids)->result_id(); } bool InstrumentPass::AllConstant(const std::vector<uint32_t>& ids) { @@ -393,16 +287,9 @@ return true; } -uint32_t InstrumentPass::GenDebugDirectRead( - const std::vector<uint32_t>& offset_ids, InstructionBuilder* builder) { - // Call debug input function. Pass func_idx and offset ids as args. - const uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size()); - const uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt); - return GenReadFunctionCall(input_func_id, offset_ids, builder); -} - uint32_t InstrumentPass::GenReadFunctionCall( - uint32_t func_id, const std::vector<uint32_t>& func_call_args, + uint32_t return_id, uint32_t func_id, + const std::vector<uint32_t>& func_call_args, InstructionBuilder* ref_builder) { // If optimizing direct reads and the call has already been generated, // use its result @@ -423,8 +310,7 @@ builder.SetInsertPoint(insert_before); } uint32_t res_id = - builder.AddFunctionCall(GetUintId(), func_id, func_call_args) - ->result_id(); + builder.AddFunctionCall(return_id, func_id, func_call_args)->result_id(); if (insert_in_first_block) call2id_[func_call_args] = res_id; return res_id; } @@ -494,53 +380,6 @@ }); } -uint32_t InstrumentPass::GetOutputBufferPtrId() { - if (output_buffer_ptr_id_ == 0) { - output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( - GetUintId(), spv::StorageClass::StorageBuffer); - } - return output_buffer_ptr_id_; -} - -uint32_t InstrumentPass::GetInputBufferTypeId() { - return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id() - : GetUintId(); -} - -uint32_t InstrumentPass::GetInputBufferPtrId() { - if (input_buffer_ptr_id_ == 0) { - input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( - GetInputBufferTypeId(), spv::StorageClass::StorageBuffer); - } - return input_buffer_ptr_id_; -} - -uint32_t InstrumentPass::GetOutputBufferBinding() { - switch (validation_id_) { - case kInstValidationIdBindless: - return kDebugOutputBindingStream; - case kInstValidationIdBuffAddr: - return kDebugOutputBindingStream; - case kInstValidationIdDebugPrintf: - return kDebugOutputPrintfStream; - default: - assert(false && "unexpected validation id"); - } - return 0; -} - -uint32_t InstrumentPass::GetInputBufferBinding() { - switch (validation_id_) { - case kInstValidationIdBindless: - return kDebugInputBindingBindless; - case kInstValidationIdBuffAddr: - return kDebugInputBindingBuffAddr; - default: - assert(false && "unexpected validation id"); - } - return 0; -} - analysis::Integer* InstrumentPass::GetInteger(uint32_t width, bool is_signed) { analysis::Integer i(width, is_signed); analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&i); @@ -621,110 +460,6 @@ storage_buffer_ext_defined_ = true; } -// Return id for output buffer -uint32_t InstrumentPass::GetOutputBufferId() { - if (output_buffer_id_ == 0) { - // If not created yet, create one - analysis::DecorationManager* deco_mgr = get_decoration_mgr(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::RuntimeArray* reg_uint_rarr_ty = GetUintRuntimeArrayType(32); - analysis::Integer* reg_uint_ty = GetInteger(32, false); - analysis::Type* reg_buf_ty = - GetStruct({reg_uint_ty, reg_uint_ty, reg_uint_rarr_ty}); - uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); - // By the Vulkan spec, a pre-existing struct containing a RuntimeArray - // must be a block, and will therefore be decorated with Block. Therefore - // the undecorated type returned here will not be pre-existing and can - // safely be decorated. Since this type is now decorated, it is out of - // sync with the TypeManager and therefore the TypeManager must be - // invalidated after this pass. - assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && - "used struct type returned"); - deco_mgr->AddDecoration(obufTyId, uint32_t(spv::Decoration::Block)); - deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputFlagsOffset, - uint32_t(spv::Decoration::Offset), 0); - deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, - uint32_t(spv::Decoration::Offset), 4); - deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, - uint32_t(spv::Decoration::Offset), 8); - uint32_t obufTyPtrId_ = - type_mgr->FindPointerToType(obufTyId, spv::StorageClass::StorageBuffer); - output_buffer_id_ = TakeNextId(); - std::unique_ptr<Instruction> newVarOp(new Instruction( - context(), spv::Op::OpVariable, obufTyPtrId_, output_buffer_id_, - {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, - {uint32_t(spv::StorageClass::StorageBuffer)}}})); - context()->AddGlobalValue(std::move(newVarOp)); - context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer")); - context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "flags")); - context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "written_count")); - context()->AddDebug2Inst(NewMemberName(obufTyId, 2, "data")); - context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer")); - deco_mgr->AddDecorationVal( - output_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_); - deco_mgr->AddDecorationVal(output_buffer_id_, - uint32_t(spv::Decoration::Binding), - GetOutputBufferBinding()); - AddStorageBufferExt(); - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { - // Add the new buffer to all entry points. - for (auto& entry : get_module()->entry_points()) { - entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}}); - context()->AnalyzeUses(&entry); - } - } - } - return output_buffer_id_; -} - -uint32_t InstrumentPass::GetInputBufferId() { - if (input_buffer_id_ == 0) { - // If not created yet, create one - analysis::DecorationManager* deco_mgr = get_decoration_mgr(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u; - analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width); - analysis::Struct* reg_buf_ty = GetStruct({reg_uint_rarr_ty}); - uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); - // By the Vulkan spec, a pre-existing struct containing a RuntimeArray - // must be a block, and will therefore be decorated with Block. Therefore - // the undecorated type returned here will not be pre-existing and can - // safely be decorated. Since this type is now decorated, it is out of - // sync with the TypeManager and therefore the TypeManager must be - // invalidated after this pass. - assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 && - "used struct type returned"); - deco_mgr->AddDecoration(ibufTyId, uint32_t(spv::Decoration::Block)); - deco_mgr->AddMemberDecoration(ibufTyId, 0, - uint32_t(spv::Decoration::Offset), 0); - uint32_t ibufTyPtrId_ = - type_mgr->FindPointerToType(ibufTyId, spv::StorageClass::StorageBuffer); - input_buffer_id_ = TakeNextId(); - std::unique_ptr<Instruction> newVarOp(new Instruction( - context(), spv::Op::OpVariable, ibufTyPtrId_, input_buffer_id_, - {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, - {uint32_t(spv::StorageClass::StorageBuffer)}}})); - context()->AddGlobalValue(std::move(newVarOp)); - context()->AddDebug2Inst(NewGlobalName(ibufTyId, "InputBuffer")); - context()->AddDebug2Inst(NewMemberName(ibufTyId, 0, "data")); - context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer")); - deco_mgr->AddDecorationVal( - input_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_); - deco_mgr->AddDecorationVal(input_buffer_id_, - uint32_t(spv::Decoration::Binding), - GetInputBufferBinding()); - AddStorageBufferExt(); - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { - // Add the new buffer to all entry points. - for (auto& entry : get_module()->entry_points()) { - entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}}); - context()->AnalyzeUses(&entry); - } - } - } - return input_buffer_id_; -} - uint32_t InstrumentPass::GetFloatId() { if (float_id_ == 0) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -817,159 +552,6 @@ return void_id_; } -uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, - uint32_t val_spec_param_cnt) { - // Total param count is common params plus validation-specific - // params - uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; - if (param2output_func_id_[param_cnt] == 0) { - // Create function - param2output_func_id_[param_cnt] = TakeNextId(); - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - - const std::vector<const analysis::Type*> param_types(param_cnt, - GetInteger(32, false)); - std::unique_ptr<Function> output_func = StartFunction( - param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types); - - std::vector<uint32_t> param_ids = AddParameters(*output_func, param_types); - - // Create first block - auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId())); - - InstructionBuilder builder( - context(), &*new_blk_ptr, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - // Gen test if debug output buffer size will not be exceeded. - uint32_t val_spec_offset = kInstStageOutCnt; - uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; - uint32_t buf_id = GetOutputBufferId(); - uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); - Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain( - buf_uint_ptr_id, buf_id, - {builder.GetUintConstantId(kDebugOutputSizeOffset)}); - // Fetch the current debug buffer written size atomically, adding the - // size of the record to be written. - uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); - uint32_t mask_none_id = - builder.GetUintConstantId(uint32_t(spv::MemoryAccessMask::MaskNone)); - uint32_t scope_invok_id = - builder.GetUintConstantId(uint32_t(spv::Scope::Invocation)); - Instruction* obuf_curr_sz_inst = builder.AddQuadOp( - GetUintId(), spv::Op::OpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), - scope_invok_id, mask_none_id, obuf_record_sz_id); - uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); - // Compute new written size - Instruction* obuf_new_sz_inst = - builder.AddIAdd(GetUintId(), obuf_curr_sz_id, - builder.GetUintConstantId(obuf_record_sz)); - // Fetch the data bound - Instruction* obuf_bnd_inst = - builder.AddIdLiteralOp(GetUintId(), spv::Op::OpArrayLength, - GetOutputBufferId(), kDebugOutputDataOffset); - // Test that new written size is less than or equal to debug output - // data bound - Instruction* obuf_safe_inst = builder.AddBinaryOp( - GetBoolId(), spv::Op::OpULessThanEqual, obuf_new_sz_inst->result_id(), - obuf_bnd_inst->result_id()); - uint32_t merge_blk_id = TakeNextId(); - uint32_t write_blk_id = TakeNextId(); - std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id)); - std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id)); - (void)builder.AddConditionalBranch( - obuf_safe_inst->result_id(), write_blk_id, merge_blk_id, merge_blk_id, - uint32_t(spv::SelectionControlMask::MaskNone)); - // Close safety test block and gen write block - output_func->AddBasicBlock(std::move(new_blk_ptr)); - new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label)); - builder.SetInsertPoint(&*new_blk_ptr); - // Generate common and stage-specific debug record members - GenCommonStreamWriteCode(obuf_record_sz, param_ids[kInstCommonParamInstIdx], - stage_idx, obuf_curr_sz_id, &builder); - GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder); - // Gen writes of validation specific data - for (uint32_t i = 0; i < val_spec_param_cnt; ++i) { - GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i, - param_ids[kInstCommonParamCnt + i], &builder); - } - // Close write block and gen merge block - (void)builder.AddBranch(merge_blk_id); - output_func->AddBasicBlock(std::move(new_blk_ptr)); - new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label)); - builder.SetInsertPoint(&*new_blk_ptr); - // Close merge block and function and add function to module - (void)builder.AddNullaryOp(0, spv::Op::OpReturn); - - output_func->AddBasicBlock(std::move(new_blk_ptr)); - output_func->SetFunctionEnd(EndFunction()); - context()->AddFunction(std::move(output_func)); - - std::string name("stream_write_"); - name += std::to_string(param_cnt); - - context()->AddDebug2Inst( - NewGlobalName(param2output_func_id_[param_cnt], name)); - } - return param2output_func_id_[param_cnt]; -} - -uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { - uint32_t func_id = param2input_func_id_[param_cnt]; - if (func_id != 0) return func_id; - // Create input function for param_cnt. - func_id = TakeNextId(); - analysis::Integer* uint_type = GetInteger(32, false); - std::vector<const analysis::Type*> param_types(param_cnt, uint_type); - - std::unique_ptr<Function> input_func = - StartFunction(func_id, uint_type, param_types); - std::vector<uint32_t> param_ids = AddParameters(*input_func, param_types); - - // Create block - auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId())); - InstructionBuilder builder( - context(), &*new_blk_ptr, - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - // For each offset parameter, generate new offset with parameter, adding last - // loaded value if it exists, and load value from input buffer at new offset. - // Return last loaded value. - uint32_t ibuf_type_id = GetInputBufferTypeId(); - uint32_t buf_id = GetInputBufferId(); - uint32_t buf_ptr_id = GetInputBufferPtrId(); - uint32_t last_value_id = 0; - for (uint32_t p = 0; p < param_cnt; ++p) { - uint32_t offset_id; - if (p == 0) { - offset_id = param_ids[0]; - } else { - if (ibuf_type_id != GetUintId()) { - last_value_id = - builder.AddUnaryOp(GetUintId(), spv::Op::OpUConvert, last_value_id) - ->result_id(); - } - offset_id = builder.AddIAdd(GetUintId(), last_value_id, param_ids[p]) - ->result_id(); - } - Instruction* ac_inst = builder.AddAccessChain( - buf_ptr_id, buf_id, - {builder.GetUintConstantId(kDebugInputDataOffset), offset_id}); - last_value_id = - builder.AddLoad(ibuf_type_id, ac_inst->result_id())->result_id(); - } - (void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, last_value_id); - // Close block and function and add function to module - input_func->AddBasicBlock(std::move(new_blk_ptr)); - input_func->SetFunctionEnd(EndFunction()); - context()->AddFunction(std::move(input_func)); - - std::string name("direct_read_"); - name += std::to_string(param_cnt); - context()->AddDebug2Inst(NewGlobalName(func_id, name)); - - param2input_func_id_[param_cnt] = func_id; - return func_id; -} - void InstrumentPass::SplitBlock( BasicBlock::iterator inst_itr, UptrVectorIterator<BasicBlock> block_itr, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { @@ -1071,52 +653,54 @@ } bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { - // Make sure all entry points have the same execution model. Do not - // instrument if they do not. - // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module - // can contain entry points with different execution models, although - // such modules will likely be rare as GLSL and HLSL are geared toward - // one model per module. In such cases we will need - // to clone any functions which are in the call trees of entrypoints - // with differing execution models. - spv::ExecutionModel stage = context()->GetStage(); - // Check for supported stages - if (stage != spv::ExecutionModel::Vertex && - stage != spv::ExecutionModel::Fragment && - stage != spv::ExecutionModel::Geometry && - stage != spv::ExecutionModel::GLCompute && - stage != spv::ExecutionModel::TessellationControl && - stage != spv::ExecutionModel::TessellationEvaluation && - stage != spv::ExecutionModel::TaskNV && - stage != spv::ExecutionModel::MeshNV && - stage != spv::ExecutionModel::RayGenerationNV && - stage != spv::ExecutionModel::IntersectionNV && - stage != spv::ExecutionModel::AnyHitNV && - stage != spv::ExecutionModel::ClosestHitNV && - stage != spv::ExecutionModel::MissNV && - stage != spv::ExecutionModel::CallableNV && - stage != spv::ExecutionModel::TaskEXT && - stage != spv::ExecutionModel::MeshEXT) { - if (consumer()) { - std::string message = "Stage not supported by instrumentation"; - consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + uint32_t stage_id; + if (use_stage_info_) { + // Make sure all entry points have the same execution model. Do not + // instrument if they do not. + // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module + // can contain entry points with different execution models, although + // such modules will likely be rare as GLSL and HLSL are geared toward + // one model per module. In such cases we will need + // to clone any functions which are in the call trees of entrypoints + // with differing execution models. + spv::ExecutionModel stage = context()->GetStage(); + // Check for supported stages + if (stage != spv::ExecutionModel::Vertex && + stage != spv::ExecutionModel::Fragment && + stage != spv::ExecutionModel::Geometry && + stage != spv::ExecutionModel::GLCompute && + stage != spv::ExecutionModel::TessellationControl && + stage != spv::ExecutionModel::TessellationEvaluation && + stage != spv::ExecutionModel::TaskNV && + stage != spv::ExecutionModel::MeshNV && + stage != spv::ExecutionModel::RayGenerationNV && + stage != spv::ExecutionModel::IntersectionNV && + stage != spv::ExecutionModel::AnyHitNV && + stage != spv::ExecutionModel::ClosestHitNV && + stage != spv::ExecutionModel::MissNV && + stage != spv::ExecutionModel::CallableNV && + stage != spv::ExecutionModel::TaskEXT && + stage != spv::ExecutionModel::MeshEXT) { + if (consumer()) { + std::string message = "Stage not supported by instrumentation"; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return false; } - return false; + stage_id = static_cast<uint32_t>(stage); + } else { + stage_id = 0; } // Add together the roots of all entry points std::queue<uint32_t> roots; for (auto& e : get_module()->entry_points()) { roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); } - bool modified = InstProcessCallTreeFromRoots(pfn, &roots, uint32_t(stage)); + bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage_id); return modified; } void InstrumentPass::InitializeInstrument() { - output_buffer_id_ = 0; - output_buffer_ptr_id_ = 0; - input_buffer_ptr_id_ = 0; - input_buffer_id_ = 0; float_id_ = 0; v4float_id_ = 0; uint_id_ = 0;
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h index 4bbbb09..e4408c9 100644 --- a/source/opt/instrument_pass.h +++ b/source/opt/instrument_pass.h
@@ -55,14 +55,6 @@ namespace spvtools { namespace opt { -namespace { -// Validation Ids -// These are used to identify the general validation being done and map to -// its output buffers. -constexpr uint32_t kInstValidationIdBindless = 0; -constexpr uint32_t kInstValidationIdBuffAddr = 1; -constexpr uint32_t kInstValidationIdDebugPrintf = 2; -} // namespace class InstrumentPass : public Pass { using cbb_ptr = const BasicBlock*; @@ -85,13 +77,13 @@ // set |desc_set| for debug input and output buffers and writes |shader_id| // into debug output records. |opt_direct_reads| indicates that the pass // will see direct input buffer reads and should prepare to optimize them. - InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id, - bool opt_direct_reads = false) + InstrumentPass(uint32_t desc_set, uint32_t shader_id, bool opt_direct_reads, + bool use_stage_info) : Pass(), desc_set_(desc_set), shader_id_(shader_id), - validation_id_(validation_id), - opt_direct_reads_(opt_direct_reads) {} + opt_direct_reads_(opt_direct_reads), + use_stage_info_(use_stage_info) {} // Initialize state for instrumentation of module. void InitializeInstrument(); @@ -113,108 +105,10 @@ void MovePostludeCode(UptrVectorIterator<BasicBlock> ref_block_itr, BasicBlock* new_blk_ptr); - // Generate instructions in |builder| which will atomically fetch and - // increment the size of the debug output buffer stream of the current - // validation and write a record to the end of the stream, if enough space - // in the buffer remains. The record will contain the index of the function - // and instruction within that function |func_idx, instruction_idx| which - // generated the record. It will also contain additional information to - // identify the instance of the shader, depending on the stage |stage_idx| - // of the shader. Finally, the record will contain validation-specific - // data contained in |validation_ids| which will identify the validation - // error as well as the values involved in the error. - // - // The output buffer binding written to by the code generated by the function - // is determined by the validation id specified when each specific - // instrumentation pass is created. - // - // The output buffer is a sequence of 32-bit values with the following - // format (where all elements are unsigned 32-bit unless otherwise noted): - // - // Size - // Record0 - // Record1 - // Record2 - // ... - // - // Size is the number of 32-bit values that have been written or - // attempted to be written to the output buffer, excluding the Size. It is - // initialized to 0. If the size of attempts to write the buffer exceeds - // the actual size of the buffer, it is possible that this field can exceed - // the actual size of the buffer. - // - // Each Record* is a variable-length sequence of 32-bit values with the - // following format defined using static const offsets in the .cpp file: - // - // Record Size - // Shader ID - // Instruction Index - // Stage - // Stage-specific Word 0 - // Stage-specific Word 1 - // ... - // Validation Error Code - // Validation-specific Word 0 - // Validation-specific Word 1 - // Validation-specific Word 2 - // ... - // - // Each record consists of three subsections: members common across all - // validation, members specific to the stage, and members specific to a - // validation. - // - // The Record Size is the number of 32-bit words in the record, including - // the Record Size word. - // - // Shader ID is a value that identifies which shader has generated the - // validation error. It is passed when the instrumentation pass is created. - // - // The Instruction Index is the position of the instruction within the - // SPIR-V file which is in error. - // - // The Stage is the pipeline stage which has generated the error as defined - // by the SpvExecutionModel_ enumeration. This is used to interpret the - // following Stage-specific words. - // - // The Stage-specific Words identify which invocation of the shader generated - // the error. Every stage will write a fixed number of words. Vertex shaders - // will write the Vertex and Instance ID. Fragment shaders will write - // FragCoord.xy. Compute shaders will write the GlobalInvocation ID. - // The tessellation eval shader will write the Primitive ID and TessCoords.uv. - // The tessellation control shader and geometry shader will write the - // Primitive ID and Invocation ID. - // - // The Validation Error Code specifies the exact error which has occurred. - // These are enumerated with the kInstError* static consts. This allows - // multiple validation layers to use the same, single output buffer. - // - // The Validation-specific Words are a validation-specific number of 32-bit - // words which give further information on the validation error that - // occurred. These are documented further in each file containing the - // validation-specific class which derives from this base class. - // - // Because the code that is generated checks against the size of the buffer - // before writing, the size of the debug out buffer can be used by the - // validation layer to control the number of error records that are written. - void GenDebugStreamWrite(uint32_t instruction_idx, uint32_t stage_idx, - const std::vector<uint32_t>& validation_ids, - InstructionBuilder* builder); - // Return true if all instructions in |ids| are constants or spec constants. bool AllConstant(const std::vector<uint32_t>& ids); - // Generate in |builder| instructions to read the unsigned integer from the - // input buffer specified by the offsets in |offset_ids|. Given offsets - // o0, o1, ... oN, and input buffer ibuf, return the id for the value: - // - // ibuf[...ibuf[ibuf[o0]+o1]...+oN] - // - // The binding and the format of the input buffer is determined by each - // specific validation, which is specified at the creation of the pass. - uint32_t GenDebugDirectRead(const std::vector<uint32_t>& offset_ids, - InstructionBuilder* builder); - - uint32_t GenReadFunctionCall(uint32_t func_id, + uint32_t GenReadFunctionCall(uint32_t return_id, uint32_t func_id, const std::vector<uint32_t>& args, InstructionBuilder* builder); @@ -242,15 +136,6 @@ std::unique_ptr<Instruction> NewName(uint32_t id, const std::string& name_str); - // Set the name for a function or global variable, names will be - // prefixed to identify which instrumentation pass generated them. - std::unique_ptr<Instruction> NewGlobalName(uint32_t id, - const std::string& name_str); - - // Set the name for a structure member - std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index, - const std::string& name_str); - // Return id for 32-bit unsigned type uint32_t GetUintId(); @@ -282,30 +167,9 @@ // Return pointer to type for runtime array of uint analysis::RuntimeArray* GetUintRuntimeArrayType(uint32_t width); - // Return id for buffer uint type - uint32_t GetOutputBufferPtrId(); - - // Return id for buffer uint type - uint32_t GetInputBufferTypeId(); - - // Return id for buffer uint type - uint32_t GetInputBufferPtrId(); - - // Return binding for output buffer for current validation. - uint32_t GetOutputBufferBinding(); - - // Return binding for input buffer for current validation. - uint32_t GetInputBufferBinding(); - // Add storage buffer extension if needed void AddStorageBufferExt(); - // Return id for debug output buffer - uint32_t GetOutputBufferId(); - - // Return id for debug input buffer - uint32_t GetInputBufferId(); - // Return id for 32-bit float type uint32_t GetFloatId(); @@ -321,15 +185,6 @@ // Return id for v3uint type uint32_t GetVec3UintId(); - // Return id for output function. Define if it doesn't exist with - // |val_spec_param_cnt| validation-specific uint32 parameters. - uint32_t GetStreamWriteFunctionId(uint32_t stage_idx, - uint32_t val_spec_param_cnt); - - // Return id for input function taking |param_cnt| uint32 parameters. Define - // if it doesn't exist. - uint32_t GetDirectReadFunctionId(uint32_t param_cnt); - // Split block |block_itr| into two new blocks where the second block // contains |inst_itr| and place in |new_blocks|. void SplitBlock(BasicBlock::iterator inst_itr, @@ -349,40 +204,11 @@ std::queue<uint32_t>* roots, uint32_t stage_idx); - // Gen code into |builder| to write |field_value_id| into debug output - // buffer at |base_offset_id| + |field_offset|. - void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset, - uint32_t field_value_id, - InstructionBuilder* builder); - - // Generate instructions into |builder| which will write the members - // of the debug output record common for all stages and validations at - // |base_off|. - void GenCommonStreamWriteCode(uint32_t record_sz, uint32_t instruction_idx, - uint32_t stage_idx, uint32_t base_off, - InstructionBuilder* builder); - - // Generate instructions into |builder| which will write - // |uint_frag_coord_id| at |component| of the record at |base_offset_id| of - // the debug output buffer . - void GenFragCoordEltDebugOutputCode(uint32_t base_offset_id, - uint32_t uint_frag_coord_id, - uint32_t component, - InstructionBuilder* builder); - // Generate instructions into |builder| which will load |var_id| and return // its result id. uint32_t GenVarLoad(uint32_t var_id, InstructionBuilder* builder); - // Generate instructions into |builder| which will load the uint |builtin_id| - // and write it into the debug output buffer at |base_off| + |builtin_off|. - void GenBuiltinOutputCode(uint32_t builtin_id, uint32_t builtin_off, - uint32_t base_off, InstructionBuilder* builder); - - // Generate instructions into |builder| which will write the |stage_idx|- - // specific members of the debug output stream at |base_off|. - void GenStageStreamWriteCode(uint32_t stage_idx, uint32_t base_off, - InstructionBuilder* builder); + uint32_t GenStageInfo(uint32_t stage_idx, InstructionBuilder* builder); // Return true if instruction must be in the same block that its result // is used. @@ -418,62 +244,47 @@ // Map from instruction's unique id to offset in original file. std::unordered_map<uint32_t, uint32_t> uid2offset_; - // result id for OpConstantFalse - uint32_t validation_id_; - - // id for output buffer variable - uint32_t output_buffer_id_; - - // ptr type id for output buffer element - uint32_t output_buffer_ptr_id_; - - // ptr type id for input buffer element - uint32_t input_buffer_ptr_id_; - // id for debug output function std::unordered_map<uint32_t, uint32_t> param2output_func_id_; // ids for debug input functions std::unordered_map<uint32_t, uint32_t> param2input_func_id_; - // id for input buffer variable - uint32_t input_buffer_id_; - // id for 32-bit float type - uint32_t float_id_; + uint32_t float_id_{0}; // id for v4float type - uint32_t v4float_id_; + uint32_t v4float_id_{0}; // id for v4uint type - uint32_t v4uint_id_; + uint32_t v4uint_id_{0}; // id for v3uint type - uint32_t v3uint_id_; + uint32_t v3uint_id_{0}; // id for 32-bit unsigned type - uint32_t uint_id_; + uint32_t uint_id_{0}; // id for 64-bit unsigned type - uint32_t uint64_id_; + uint32_t uint64_id_{0}; // id for 8-bit unsigned type - uint32_t uint8_id_; + uint32_t uint8_id_{0}; // id for bool type - uint32_t bool_id_; + uint32_t bool_id_{0}; // id for void type - uint32_t void_id_; + uint32_t void_id_{0}; // boolean to remember storage buffer extension - bool storage_buffer_ext_defined_; + bool storage_buffer_ext_defined_{false}; // runtime array of uint type - analysis::RuntimeArray* uint64_rarr_ty_; + analysis::RuntimeArray* uint64_rarr_ty_{nullptr}; // runtime array of uint type - analysis::RuntimeArray* uint32_rarr_ty_; + analysis::RuntimeArray* uint32_rarr_ty_{nullptr}; // Pre-instrumentation same-block insts std::unordered_map<uint32_t, Instruction*> same_block_pre_; @@ -498,11 +309,15 @@ std::unordered_map<std::vector<uint32_t>, uint32_t, vector_hash_> call2id_; // Function currently being instrumented - Function* curr_func_; + Function* curr_func_{nullptr}; // Optimize direct debug input buffer reads. Specifically, move all such // reads with constant args to first block and reuse them. - bool opt_direct_reads_; + const bool opt_direct_reads_; + + // Set true if the instrumentation needs to know the current stage. + // Note that this does not work with multi-stage modules. + const bool use_stage_info_; }; } // namespace opt
diff --git a/source/opt/invocation_interlock_placement_pass.cpp b/source/opt/invocation_interlock_placement_pass.cpp new file mode 100644 index 0000000..642e2d2 --- /dev/null +++ b/source/opt/invocation_interlock_placement_pass.cpp
@@ -0,0 +1,493 @@ +// Copyright (c) 2023 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/opt/invocation_interlock_placement_pass.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <functional> +#include <optional> +#include <queue> +#include <stack> +#include <unordered_map> +#include <unordered_set> +#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/util/string_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +constexpr uint32_t kEntryPointExecutionModelInIdx = 0; +constexpr uint32_t kEntryPointFunctionIdInIdx = 1; +constexpr uint32_t kFunctionCallFunctionIdInIdx = 0; +} // namespace + +bool InvocationInterlockPlacementPass::hasSingleNextBlock(uint32_t block_id, + bool reverse_cfg) { + if (reverse_cfg) { + // We are traversing forward, so check whether there is a single successor. + BasicBlock* block = cfg()->block(block_id); + + switch (block->tail()->opcode()) { + case spv::Op::OpBranchConditional: + return false; + case spv::Op::OpSwitch: + return block->tail()->NumInOperandWords() == 1; + default: + return !block->tail()->IsReturnOrAbort(); + } + } else { + // We are traversing backward, so check whether there is a single + // predecessor. + return cfg()->preds(block_id).size() == 1; + } +} + +void InvocationInterlockPlacementPass::forEachNext( + uint32_t block_id, bool reverse_cfg, std::function<void(uint32_t)> f) { + if (reverse_cfg) { + BasicBlock* block = cfg()->block(block_id); + + block->ForEachSuccessorLabel([f](uint32_t succ_id) { f(succ_id); }); + } else { + for (uint32_t pred_id : cfg()->preds(block_id)) { + f(pred_id); + } + } +} + +void InvocationInterlockPlacementPass::addInstructionAtBlockBoundary( + BasicBlock* block, spv::Op opcode, bool at_end) { + if (at_end) { + assert(block->begin()->opcode() != spv::Op::OpPhi && + "addInstructionAtBlockBoundary expects to be called with at_end == " + "true only if there is a single successor to block"); + // Insert a begin instruction at the end of the block. + Instruction* begin_inst = new Instruction(context(), opcode); + begin_inst->InsertAfter(&*--block->tail()); + } else { + assert(block->begin()->opcode() != spv::Op::OpPhi && + "addInstructionAtBlockBoundary expects to be called with at_end == " + "false only if there is a single predecessor to block"); + // Insert an end instruction at the beginning of the block. + Instruction* end_inst = new Instruction(context(), opcode); + end_inst->InsertBefore(&*block->begin()); + } +} + +bool InvocationInterlockPlacementPass::killDuplicateBegin(BasicBlock* block) { + bool found = false; + + return context()->KillInstructionIf( + block->begin(), block->end(), [&found](Instruction* inst) { + if (inst->opcode() == spv::Op::OpBeginInvocationInterlockEXT) { + if (found) { + return true; + } + found = true; + } + return false; + }); +} + +bool InvocationInterlockPlacementPass::killDuplicateEnd(BasicBlock* block) { + std::vector<Instruction*> to_kill; + block->ForEachInst([&to_kill](Instruction* inst) { + if (inst->opcode() == spv::Op::OpEndInvocationInterlockEXT) { + to_kill.push_back(inst); + } + }); + + if (to_kill.size() <= 1) { + return false; + } + + to_kill.pop_back(); + + for (Instruction* inst : to_kill) { + context()->KillInst(inst); + } + + return true; +} + +void InvocationInterlockPlacementPass::recordBeginOrEndInFunction( + Function* func) { + if (extracted_functions_.count(func)) { + return; + } + + bool had_begin = false; + bool had_end = false; + + func->ForEachInst([this, &had_begin, &had_end](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + had_begin = true; + break; + case spv::Op::OpEndInvocationInterlockEXT: + had_end = true; + break; + case spv::Op::OpFunctionCall: { + uint32_t function_id = + inst->GetSingleWordInOperand(kFunctionCallFunctionIdInIdx); + Function* inner_func = context()->GetFunction(function_id); + recordBeginOrEndInFunction(inner_func); + ExtractionResult result = extracted_functions_[inner_func]; + had_begin = had_begin || result.had_begin; + had_end = had_end || result.had_end; + break; + } + default: + break; + } + }); + + ExtractionResult result = {had_begin, had_end}; + extracted_functions_[func] = result; +} + +bool InvocationInterlockPlacementPass:: + removeBeginAndEndInstructionsFromFunction(Function* func) { + bool modified = false; + func->ForEachInst([this, &modified](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + context()->KillInst(inst); + modified = true; + break; + case spv::Op::OpEndInvocationInterlockEXT: + context()->KillInst(inst); + modified = true; + break; + default: + break; + } + }); + return modified; +} + +bool InvocationInterlockPlacementPass::extractInstructionsFromCalls( + std::vector<BasicBlock*> blocks) { + bool modified = false; + + for (BasicBlock* block : blocks) { + block->ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionCall) { + uint32_t function_id = + inst->GetSingleWordInOperand(kFunctionCallFunctionIdInIdx); + Function* func = context()->GetFunction(function_id); + ExtractionResult result = extracted_functions_[func]; + + if (result.had_begin) { + Instruction* new_inst = new Instruction( + context(), spv::Op::OpBeginInvocationInterlockEXT); + new_inst->InsertBefore(inst); + modified = true; + } + if (result.had_end) { + Instruction* new_inst = + new Instruction(context(), spv::Op::OpEndInvocationInterlockEXT); + new_inst->InsertAfter(inst); + modified = true; + } + } + }); + } + return modified; +} + +void InvocationInterlockPlacementPass::recordExistingBeginAndEndBlock( + std::vector<BasicBlock*> blocks) { + for (BasicBlock* block : blocks) { + block->ForEachInst([this, block](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + begin_.insert(block->id()); + break; + case spv::Op::OpEndInvocationInterlockEXT: + end_.insert(block->id()); + break; + default: + break; + } + }); + } +} + +InvocationInterlockPlacementPass::BlockSet +InvocationInterlockPlacementPass::computeReachableBlocks( + BlockSet& previous_inside, const BlockSet& starting_nodes, + bool reverse_cfg) { + BlockSet inside = starting_nodes; + + std::deque<uint32_t> worklist; + worklist.insert(worklist.begin(), starting_nodes.begin(), + starting_nodes.end()); + + while (!worklist.empty()) { + uint32_t block_id = worklist.front(); + worklist.pop_front(); + + forEachNext(block_id, reverse_cfg, + [&inside, &previous_inside, &worklist](uint32_t next_id) { + previous_inside.insert(next_id); + if (inside.insert(next_id).second) { + worklist.push_back(next_id); + } + }); + } + + return inside; +} + +bool InvocationInterlockPlacementPass::removeUnneededInstructions( + BasicBlock* block) { + bool modified = false; + if (!predecessors_after_begin_.count(block->id()) && + after_begin_.count(block->id())) { + // None of the previous blocks are in the critical section, but this block + // is. This can only happen if this block already has at least one begin + // instruction. Leave the first begin instruction, and remove any others. + modified |= killDuplicateBegin(block); + } else if (predecessors_after_begin_.count(block->id())) { + // At least one previous block is in the critical section; remove all + // begin instructions in this block. + modified |= context()->KillInstructionIf( + block->begin(), block->end(), [](Instruction* inst) { + return inst->opcode() == spv::Op::OpBeginInvocationInterlockEXT; + }); + } + + if (!successors_before_end_.count(block->id()) && + before_end_.count(block->id())) { + // Same as above + modified |= killDuplicateEnd(block); + } else if (successors_before_end_.count(block->id())) { + modified |= context()->KillInstructionIf( + block->begin(), block->end(), [](Instruction* inst) { + return inst->opcode() == spv::Op::OpEndInvocationInterlockEXT; + }); + } + return modified; +} + +BasicBlock* InvocationInterlockPlacementPass::splitEdge(BasicBlock* block, + uint32_t succ_id) { + // Create a new block to replace the critical edge. + auto new_succ_temp = MakeUnique<BasicBlock>( + MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, TakeNextId(), + std::initializer_list<Operand>{})); + auto* new_succ = new_succ_temp.get(); + + // Insert the new block into the function. + block->GetParent()->InsertBasicBlockAfter(std::move(new_succ_temp), block); + + new_succ->AddInstruction(MakeUnique<Instruction>( + context(), spv::Op::OpBranch, 0, 0, + std::initializer_list<Operand>{ + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {succ_id})})); + + assert(block->tail()->opcode() == spv::Op::OpBranchConditional || + block->tail()->opcode() == spv::Op::OpSwitch); + + // Update the first branch to successor to instead branch to + // the new successor. If there are multiple edges, we arbitrarily choose the + // first time it appears in the list. The other edges to `succ_id` will have + // to be split by another call to `splitEdge`. + block->tail()->WhileEachInId([new_succ, succ_id](uint32_t* branch_id) { + if (*branch_id == succ_id) { + *branch_id = new_succ->id(); + return false; + } + return true; + }); + + return new_succ; +} + +bool InvocationInterlockPlacementPass::placeInstructionsForEdge( + BasicBlock* block, uint32_t next_id, BlockSet& inside, + BlockSet& previous_inside, spv::Op opcode, bool reverse_cfg) { + bool modified = false; + + if (previous_inside.count(next_id) && !inside.count(block->id())) { + // This block is not in the critical section but the next has at least one + // other previous block that is, so this block should be enter it as well. + // We need to add begin or end instructions to the edge. + + modified = true; + + if (hasSingleNextBlock(block->id(), reverse_cfg)) { + // This is the only next block. + + // Additionally, because `next_id` is in `previous_inside`, we know that + // `next_id` has at least one previous block in `inside`. And because + // 'block` is not in `inside`, that means the `next_id` has to have at + // least one other previous block in `inside`. + + // This is solely for a debug assertion. It is essentially recomputing the + // value of `previous_inside` to verify that it was computed correctly + // such that the above statement is true. + bool next_has_previous_inside = false; + // By passing !reverse_cfg to forEachNext, we are actually iterating over + // the previous blocks. + forEachNext(next_id, !reverse_cfg, + [&next_has_previous_inside, inside](uint32_t previous_id) { + if (inside.count(previous_id)) { + next_has_previous_inside = true; + } + }); + assert(next_has_previous_inside && + "`previous_inside` must be the set of blocks with at least one " + "previous block in `inside`"); + + addInstructionAtBlockBoundary(block, opcode, reverse_cfg); + } else { + // This block has multiple next blocks. Split the edge and insert the + // instruction in the new next block. + BasicBlock* new_branch; + if (reverse_cfg) { + new_branch = splitEdge(block, next_id); + } else { + new_branch = splitEdge(cfg()->block(next_id), block->id()); + } + + auto inst = new Instruction(context(), opcode); + inst->InsertBefore(&*new_branch->tail()); + } + } + + return modified; +} + +bool InvocationInterlockPlacementPass::placeInstructions(BasicBlock* block) { + bool modified = false; + + block->ForEachSuccessorLabel([this, block, &modified](uint32_t succ_id) { + modified |= 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); + }); + + return modified; +} + +bool InvocationInterlockPlacementPass::processFragmentShaderEntry( + Function* entry_func) { + bool modified = false; + + // Save the original order of blocks in the function, so we don't iterate over + // newly-added blocks. + std::vector<BasicBlock*> original_blocks; + for (auto bi = entry_func->begin(); bi != entry_func->end(); ++bi) { + original_blocks.push_back(&*bi); + } + + modified |= extractInstructionsFromCalls(original_blocks); + recordExistingBeginAndEndBlock(original_blocks); + + after_begin_ = computeReachableBlocks(predecessors_after_begin_, begin_, + /* reverse_cfg= */ true); + before_end_ = computeReachableBlocks(successors_before_end_, end_, + /* reverse_cfg= */ false); + + for (BasicBlock* block : original_blocks) { + modified |= removeUnneededInstructions(block); + modified |= placeInstructions(block); + } + return modified; +} + +bool InvocationInterlockPlacementPass::isFragmentShaderInterlockEnabled() { + if (!context()->get_feature_mgr()->HasExtension( + kSPV_EXT_fragment_shader_interlock)) { + return false; + } + + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::FragmentShaderSampleInterlockEXT)) { + return true; + } + + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::FragmentShaderPixelInterlockEXT)) { + return true; + } + + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::FragmentShaderShadingRateInterlockEXT)) { + return true; + } + + return false; +} + +Pass::Status InvocationInterlockPlacementPass::Process() { + // Skip this pass if the necessary extension or capability is missing + if (!isFragmentShaderInterlockEnabled()) { + return Status::SuccessWithoutChange; + } + + bool modified = false; + + std::unordered_set<Function*> entry_points; + for (Instruction& entry_inst : context()->module()->entry_points()) { + uint32_t entry_id = + entry_inst.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + entry_points.insert(context()->GetFunction(entry_id)); + } + + for (auto fi = context()->module()->begin(); fi != context()->module()->end(); + ++fi) { + Function* func = &*fi; + recordBeginOrEndInFunction(func); + if (!entry_points.count(func) && extracted_functions_.count(func)) { + modified |= removeBeginAndEndInstructionsFromFunction(func); + } + } + + for (Instruction& entry_inst : context()->module()->entry_points()) { + uint32_t entry_id = + entry_inst.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + Function* entry_func = context()->GetFunction(entry_id); + + auto execution_model = spv::ExecutionModel( + entry_inst.GetSingleWordInOperand(kEntryPointExecutionModelInIdx)); + + if (execution_model != spv::ExecutionModel::Fragment) { + continue; + } + + modified |= processFragmentShaderEntry(entry_func); + } + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/invocation_interlock_placement_pass.h b/source/opt/invocation_interlock_placement_pass.h new file mode 100644 index 0000000..4e85be8 --- /dev/null +++ b/source/opt/invocation_interlock_placement_pass.h
@@ -0,0 +1,158 @@ +// Copyright (c) 2023 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_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_ +#define SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_ + +#include <algorithm> +#include <array> +#include <functional> +#include <optional> +#include <unordered_map> +#include <unordered_set> + +#include "source/enum_set.h" +#include "source/extensions.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace opt { + +// This pass will ensure that an entry point will only have at most one +// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that +// order +class InvocationInterlockPlacementPass : public Pass { + public: + InvocationInterlockPlacementPass() {} + InvocationInterlockPlacementPass(const InvocationInterlockPlacementPass&) = + delete; + InvocationInterlockPlacementPass(InvocationInterlockPlacementPass&&) = delete; + + const char* name() const override { return "dedupe-interlock-invocation"; } + Status Process() override; + + private: + using BlockSet = std::unordered_set<uint32_t>; + + // Specifies whether a function originally had a begin or end instruction. + struct ExtractionResult { + bool had_begin : 1; + bool had_end : 2; + }; + + // Check if a block has only a single next block, depending on the directing + // that we are traversing the CFG. If reverse_cfg is true, we are walking + // forward through the CFG, and will return if the block has only one + // successor. Otherwise, we are walking backward through the CFG, and will + // return if the block has only one predecessor. + bool hasSingleNextBlock(uint32_t block_id, bool reverse_cfg); + + // Iterate over each of a block's predecessors or successors, depending on + // direction. If reverse_cfg is true, we are walking forward through the CFG, + // and need to iterate over the successors. Otherwise, we are walking backward + // through the CFG, and need to iterate over the predecessors. + void forEachNext(uint32_t block_id, bool reverse_cfg, + std::function<void(uint32_t)> f); + + // Add either a begin or end instruction to the edge of the basic block. If + // at_end is true, add the instruction to the end of the block; otherwise add + // the instruction to the beginning of the basic block. + void addInstructionAtBlockBoundary(BasicBlock* block, spv::Op opcode, + bool at_end); + + // Remove every OpBeginInvocationInterlockEXT instruction in block after the + // first. Returns whether any instructions were removed. + bool killDuplicateBegin(BasicBlock* block); + // Remove every OpBeginInvocationInterlockEXT instruction in block before the + // last. Returns whether any instructions were removed. + bool killDuplicateEnd(BasicBlock* block); + + // Records whether a function will potentially execute a begin or end + // instruction. + void recordBeginOrEndInFunction(Function* func); + + // Recursively removes any begin or end instructions from func and any + // function func calls. Returns whether any instructions were removed. + bool removeBeginAndEndInstructionsFromFunction(Function* func); + + // For every function call in any of the passed blocks, move any begin or end + // instructions outside of the function call. Returns whether any extractions + // occurred. + bool extractInstructionsFromCalls(std::vector<BasicBlock*> blocks); + + // Finds the sets of blocks that contain OpBeginInvocationInterlockEXT and + // OpEndInvocationInterlockEXT, storing them in the member variables begin_ + // and end_ respectively. + void recordExistingBeginAndEndBlock(std::vector<BasicBlock*> blocks); + + // Compute the set of blocks including or after the barrier instruction, and + // the set of blocks with any previous blocks inside the barrier instruction. + // If reverse_cfg is true, move forward through the CFG, computing + // after_begin_ and predecessors_after_begin_computing after_begin_ and + // predecessors_after_begin_, otherwise, move backward through the CFG, + // computing before_end_ and successors_before_end_. + BlockSet computeReachableBlocks(BlockSet& in_set, + const BlockSet& starting_nodes, + bool reverse_cfg); + + // Remove unneeded begin and end instructions in block. + bool removeUnneededInstructions(BasicBlock* block); + + // Given a block which branches to multiple successors, and a specific + // successor, creates a new empty block, and update the branch instruction to + // branch to the new block instead. + BasicBlock* splitEdge(BasicBlock* block, uint32_t succ_id); + + // 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); + // Calls placeInstructionsForEdge for each edge in block. + bool placeInstructions(BasicBlock* block); + + // Processes a single fragment shader entry function. + bool processFragmentShaderEntry(Function* entry_func); + + // Returns whether the module has the SPV_EXT_fragment_shader_interlock + // extension and one of the FragmentShader*InterlockEXT capabilities. + bool isFragmentShaderInterlockEnabled(); + + // Maps a function to whether that function originally held a begin or end + // instruction. + std::unordered_map<Function*, ExtractionResult> extracted_functions_; + + // The set of blocks which have an OpBeginInvocationInterlockEXT instruction. + BlockSet begin_; + // The set of blocks which have an OpEndInvocationInterlockEXT instruction. + BlockSet end_; + // The set of blocks which either have a begin instruction, or have a + // predecessor which has a begin instruction. + BlockSet after_begin_; + // The set of blocks which either have an end instruction, or have a successor + // which have an end instruction. + BlockSet before_end_; + // The set of blocks which have a predecessor in after_begin_. + BlockSet predecessors_after_begin_; + // The set of blocks which have a successor in before_end_. + BlockSet successors_before_end_; +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h index 48e08ee..f3e0afc 100644 --- a/source/opt/ir_builder.h +++ b/source/opt/ir_builder.h
@@ -440,6 +440,22 @@ return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); } + Instruction* GetBoolConstant(bool value) { + analysis::Bool type; + uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&type); + analysis::Type* rebuilt_type = + GetContext()->get_type_mgr()->GetType(type_id); + uint32_t word = value; + const analysis::Constant* constant = + GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); + return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); + } + + uint32_t GetBoolConstantId(bool value) { + Instruction* inst = GetBoolConstant(value); + return (inst != nullptr ? inst->result_id() : 0); + } + Instruction* AddCompositeExtract(uint32_t type, uint32_t id_of_composite, const std::vector<uint32_t>& index_list) { std::vector<Operand> operands;
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 26501c2..d864b7c 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp
@@ -88,6 +88,9 @@ if (set & kAnalysisDebugInfo) { BuildDebugInfoManager(); } + if (set & kAnalysisLiveness) { + BuildLivenessManager(); + } } void IRContext::InvalidateAnalysesExceptFor( @@ -220,6 +223,28 @@ return next_instruction; } +bool IRContext::KillInstructionIf(Module::inst_iterator begin, + Module::inst_iterator end, + std::function<bool(Instruction*)> condition) { + bool removed = false; + for (auto it = begin; it != end;) { + if (!condition(&*it)) { + ++it; + continue; + } + + removed = true; + // `it` is an iterator on an intrusive list. Next is invalidated on the + // current node when an instruction is killed. The iterator must be moved + // forward before deleting the node. + auto instruction = &*it; + ++it; + KillInst(instruction); + } + + return removed; +} + void IRContext::CollectNonSemanticTree( Instruction* inst, std::unordered_set<Instruction*>* to_kill) { if (!inst->HasResultId()) return; @@ -251,6 +276,36 @@ return false; } +bool IRContext::RemoveCapability(spv::Capability capability) { + const bool removed = KillInstructionIf( + module()->capability_begin(), module()->capability_end(), + [capability](Instruction* inst) { + return static_cast<spv::Capability>(inst->GetSingleWordOperand(0)) == + capability; + }); + + if (removed && feature_mgr_ != nullptr) { + feature_mgr_->RemoveCapability(capability); + } + + return removed; +} + +bool IRContext::RemoveExtension(Extension extension) { + const std::string_view extensionName = ExtensionToString(extension); + const bool removed = KillInstructionIf( + module()->extension_begin(), module()->extension_end(), + [&extensionName](Instruction* inst) { + return inst->GetOperand(0).AsString() == extensionName; + }); + + if (removed && feature_mgr_ != nullptr) { + feature_mgr_->RemoveExtension(extension); + } + + return removed; +} + bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) { return ReplaceAllUsesWithPredicate(before, after, [](Instruction*) { return true; }); @@ -718,9 +773,9 @@ } void IRContext::InitializeCombinators() { - get_feature_mgr()->GetCapabilities()->ForEach([this](spv::Capability cap) { - AddCombinatorsForCapability(uint32_t(cap)); - }); + for (auto capability : get_feature_mgr()->GetCapabilities()) { + AddCombinatorsForCapability(uint32_t(capability)); + } for (auto& extension : module()->ext_inst_imports()) { AddCombinatorsForExtension(&extension);
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 8419ee7..ef7c458 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h
@@ -27,6 +27,7 @@ #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" @@ -83,7 +84,7 @@ kAnalysisTypes = 1 << 15, kAnalysisDebugInfo = 1 << 16, kAnalysisLiveness = 1 << 17, - kAnalysisEnd = 1 << 17 + kAnalysisEnd = 1 << 18 }; using ProcessFunction = std::function<bool(Function*)>; @@ -153,13 +154,19 @@ inline IteratorRange<Module::inst_iterator> capabilities(); inline IteratorRange<Module::const_inst_iterator> capabilities() const; + // Iterators for extensions instructions contained in this module. + inline Module::inst_iterator extension_begin(); + inline Module::inst_iterator extension_end(); + inline IteratorRange<Module::inst_iterator> extensions(); + inline IteratorRange<Module::const_inst_iterator> extensions() const; + // Iterators for types, constants and global variables instructions. inline Module::inst_iterator types_values_begin(); inline Module::inst_iterator types_values_end(); inline IteratorRange<Module::inst_iterator> types_values(); inline IteratorRange<Module::const_inst_iterator> types_values() const; - // Iterators for extension instructions contained in this module. + // Iterators for ext_inst import instructions contained in this module. inline Module::inst_iterator ext_inst_import_begin(); inline Module::inst_iterator ext_inst_import_end(); inline IteratorRange<Module::inst_iterator> ext_inst_imports(); @@ -204,17 +211,26 @@ // Add |capability| to the module, if it is not already enabled. inline void AddCapability(spv::Capability capability); - // Appends a capability instruction to this module. inline void AddCapability(std::unique_ptr<Instruction>&& c); + // Removes instruction declaring `capability` from this module. + // Returns true if the capability was removed, false otherwise. + bool RemoveCapability(spv::Capability capability); + // Appends an extension instruction to this module. inline void AddExtension(const std::string& ext_name); inline void AddExtension(std::unique_ptr<Instruction>&& e); + // Removes instruction declaring `extension` from this module. + // Returns true if the extension was removed, false otherwise. + bool RemoveExtension(Extension extension); + // Appends an extended instruction set instruction to this module. inline void AddExtInstImport(const std::string& name); inline void AddExtInstImport(std::unique_ptr<Instruction>&& e); // Set the memory model for this module. inline void SetMemoryModel(std::unique_ptr<Instruction>&& m); + // Get the memory model for this module. + inline const Instruction* GetMemoryModel() const; // Appends an entry point instruction to this module. inline void AddEntryPoint(std::unique_ptr<Instruction>&& e); // Appends an execution mode instruction to this module. @@ -238,6 +254,8 @@ inline void AddType(std::unique_ptr<Instruction>&& t); // Appends a constant, global variable, or OpUndef instruction to this module. inline void AddGlobalValue(std::unique_ptr<Instruction>&& v); + // Prepends a function declaration to this module. + inline void AddFunctionDeclaration(std::unique_ptr<Function>&& f); // Appends a function to this module. inline void AddFunction(std::unique_ptr<Function>&& f); @@ -422,6 +440,15 @@ // instruction exists. Instruction* KillInst(Instruction* inst); + // Deletes all the instruction in the range [`begin`; `end`[, for which the + // unary predicate `condition` returned true. + // Returns true if at least one instruction was removed, false otherwise. + // + // Pointer and iterator pointing to the deleted instructions become invalid. + // However other pointers and iterators are still valid. + bool KillInstructionIf(Module::inst_iterator begin, Module::inst_iterator end, + std::function<bool(Instruction*)> condition); + // Collects the non-semantic instruction tree that uses |inst|'s result id // to be killed later. void CollectNonSemanticTree(Instruction* inst, @@ -772,7 +799,8 @@ // Analyzes the features in the owned module. Builds the manager if required. void AnalyzeFeatures() { - feature_mgr_ = MakeUnique<FeatureManager>(grammar_); + feature_mgr_ = + std::unique_ptr<FeatureManager>(new FeatureManager(grammar_)); feature_mgr_->Analyze(module()); } @@ -964,6 +992,22 @@ return ((const Module*)module())->capabilities(); } +Module::inst_iterator IRContext::extension_begin() { + return module()->extension_begin(); +} + +Module::inst_iterator IRContext::extension_end() { + return module()->extension_end(); +} + +IteratorRange<Module::inst_iterator> IRContext::extensions() { + return module()->extensions(); +} + +IteratorRange<Module::const_inst_iterator> IRContext::extensions() const { + return ((const Module*)module())->extensions(); +} + Module::inst_iterator IRContext::types_values_begin() { return module()->types_values_begin(); } @@ -1114,6 +1158,10 @@ module()->SetMemoryModel(std::move(m)); } +const Instruction* IRContext::GetMemoryModel() const { + return module()->GetMemoryModel(); +} + void IRContext::AddEntryPoint(std::unique_ptr<Instruction>&& e) { module()->AddEntryPoint(std::move(e)); } @@ -1173,6 +1221,10 @@ module()->AddGlobalValue(std::move(v)); } +void IRContext::AddFunctionDeclaration(std::unique_ptr<Function>&& f) { + module()->AddFunctionDeclaration(std::move(f)); +} + void IRContext::AddFunction(std::unique_ptr<Function>&& f) { module()->AddFunction(std::move(f)); }
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 6ec0c2d..7ba75cb 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -420,13 +420,16 @@ "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_NV_ray_tracing", - "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", + "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_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_NV_compute_shader_derivatives"}); } bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 063d1b9..d7a9295 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp
@@ -273,11 +273,13 @@ "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", @@ -285,7 +287,11 @@ "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", "SPV_KHR_fragment_shader_barycentric", - "SPV_KHR_vulkan_memory_model"}); + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_NV_compute_shader_derivatives"}); } } // namespace opt
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index a0de44c..7cd6b0e 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp
@@ -124,10 +124,12 @@ "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", @@ -135,7 +137,11 @@ "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", "SPV_KHR_fragment_shader_barycentric", - "SPV_KHR_vulkan_memory_model"}); + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_NV_compute_shader_derivatives"}); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { std::vector<Instruction*> users;
diff --git a/source/opt/log.h b/source/opt/log.h index 6805100..4fb66fd 100644 --- a/source/opt/log.h +++ b/source/opt/log.h
@@ -23,7 +23,7 @@ #include "spirv-tools/libspirv.hpp" // Asserts the given condition is true. Otherwise, sends a message to the -// consumer and exits the problem with failure code. Accepts the following +// consumer and exits the program with failure code. Accepts the following // formats: // // SPIRV_ASSERT(<message-consumer>, <condition-expression>); @@ -36,7 +36,9 @@ #if !defined(NDEBUG) #define SPIRV_ASSERT(consumer, ...) SPIRV_ASSERT_IMPL(consumer, __VA_ARGS__) #else -#define SPIRV_ASSERT(consumer, ...) +// Adding a use to avoid errors in the release build related to unused +// consumers. +#define SPIRV_ASSERT(consumer, ...) (void)(consumer) #endif // Logs a debug message to the consumer. Accepts the following formats: @@ -49,26 +51,11 @@ #if !defined(NDEBUG) && defined(SPIRV_LOG_DEBUG) #define SPIRV_DEBUG(consumer, ...) SPIRV_DEBUG_IMPL(consumer, __VA_ARGS__) #else -#define SPIRV_DEBUG(consumer, ...) +// Adding a use to avoid errors in the release build related to unused +// consumers. +#define SPIRV_DEBUG(consumer, ...) (void)(consumer) #endif -// Logs an error message to the consumer saying the given feature is -// unimplemented. -#define SPIRV_UNIMPLEMENTED(consumer, feature) \ - do { \ - spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ - {static_cast<size_t>(__LINE__), 0, 0}, \ - "unimplemented: " feature); \ - } while (0) - -// Logs an error message to the consumer saying the code location -// should be unreachable. -#define SPIRV_UNREACHABLE(consumer) \ - do { \ - spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ - {static_cast<size_t>(__LINE__), 0, 0}, "unreachable"); \ - } while (0) - // Helper macros for concatenating arguments. #define SPIRV_CONCATENATE(a, b) SPIRV_CONCATENATE_(a, b) #define SPIRV_CONCATENATE_(a, b) a##b
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp index 9f95785..9972c4f 100644 --- a/source/opt/mem_pass.cpp +++ b/source/opt/mem_pass.cpp
@@ -76,6 +76,11 @@ bool MemPass::IsPtr(uint32_t ptrId) { uint32_t varId = ptrId; Instruction* ptrInst = get_def_use_mgr()->GetDef(varId); + if (ptrInst->opcode() == spv::Op::OpFunction) { + // A function is not a pointer, but it's return type could be, which will + // erroneously lead to this function returning true later on + return false; + } while (ptrInst->opcode() == spv::Op::OpCopyObject) { varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx); ptrInst = get_def_use_mgr()->GetDef(varId);
diff --git a/source/opt/modify_maximal_reconvergence.cpp b/source/opt/modify_maximal_reconvergence.cpp new file mode 100644 index 0000000..dd79b62 --- /dev/null +++ b/source/opt/modify_maximal_reconvergence.cpp
@@ -0,0 +1,103 @@ +// Copyright (c) 2024 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 "modify_maximal_reconvergence.h" + +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status ModifyMaximalReconvergence::Process() { + bool changed = false; + if (add_) { + changed = AddMaximalReconvergence(); + } else { + changed = RemoveMaximalReconvergence(); + } + return changed ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +bool ModifyMaximalReconvergence::AddMaximalReconvergence() { + bool changed = false; + bool has_extension = false; + bool has_shader = + context()->get_feature_mgr()->HasCapability(spv::Capability::Shader); + for (auto extension : context()->extensions()) { + if (extension.GetOperand(0).AsString() == "SPV_KHR_maximal_reconvergence") { + has_extension = true; + break; + } + } + + std::unordered_set<uint32_t> entry_points_with_mode; + for (auto mode : get_module()->execution_modes()) { + if (spv::ExecutionMode(mode.GetSingleWordInOperand(1)) == + spv::ExecutionMode::MaximallyReconvergesKHR) { + entry_points_with_mode.insert(mode.GetSingleWordInOperand(0)); + } + } + + for (auto entry_point : get_module()->entry_points()) { + const uint32_t id = entry_point.GetSingleWordInOperand(1); + if (!entry_points_with_mode.count(id)) { + changed = true; + if (!has_extension) { + context()->AddExtension("SPV_KHR_maximal_reconvergence"); + has_extension = true; + } + if (!has_shader) { + context()->AddCapability(spv::Capability::Shader); + has_shader = true; + } + context()->AddExecutionMode(MakeUnique<Instruction>( + context(), spv::Op::OpExecutionMode, 0, 0, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_EXECUTION_MODE, + {static_cast<uint32_t>( + spv::ExecutionMode::MaximallyReconvergesKHR)}}})); + entry_points_with_mode.insert(id); + } + } + + return changed; +} + +bool ModifyMaximalReconvergence::RemoveMaximalReconvergence() { + bool changed = false; + std::vector<Instruction*> to_remove; + Instruction* mode = &*get_module()->execution_mode_begin(); + while (mode) { + if (mode->opcode() != spv::Op::OpExecutionMode && + mode->opcode() != spv::Op::OpExecutionModeId) { + break; + } + if (spv::ExecutionMode(mode->GetSingleWordInOperand(1)) == + spv::ExecutionMode::MaximallyReconvergesKHR) { + mode = context()->KillInst(mode); + changed = true; + } else { + mode = mode->NextNode(); + } + } + + changed |= + context()->RemoveExtension(Extension::kSPV_KHR_maximal_reconvergence); + return changed; +} +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/modify_maximal_reconvergence.h b/source/opt/modify_maximal_reconvergence.h new file mode 100644 index 0000000..8d9a698 --- /dev/null +++ b/source/opt/modify_maximal_reconvergence.h
@@ -0,0 +1,53 @@ +// Copyright (c) 2024 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_MODIFY_MAXIMAL_RECONVERGENCE_H_ +#define LIBSPIRV_OPT_MODIFY_MAXIMAL_RECONVERGENCE_H_ + +#include "pass.h" + +namespace spvtools { +namespace opt { + +// Modifies entry points to either add or remove MaximallyReconvergesKHR +// +// This pass will either add or remove MaximallyReconvergesKHR to all entry +// points in the module. When adding the execution mode, it does not attempt to +// determine whether any ray tracing invocation repack instructions might be +// executed because it is a runtime restriction. That is left to the user. +class ModifyMaximalReconvergence : public Pass { + public: + const char* name() const override { return "modify-maximal-reconvergence"; } + Status Process() override; + + explicit ModifyMaximalReconvergence(bool add = true) : Pass(), add_(add) {} + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + bool AddMaximalReconvergence(); + bool RemoveMaximalReconvergence(); + + bool add_; +}; +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_MODIFY_MAXIMAL_RECONVERGENCE_H_
diff --git a/source/opt/module.h b/source/opt/module.h index ed2f345..98c16dc 100644 --- a/source/opt/module.h +++ b/source/opt/module.h
@@ -17,6 +17,7 @@ #include <functional> #include <memory> +#include <string_view> #include <unordered_map> #include <utility> #include <vector> @@ -119,6 +120,9 @@ // Appends a constant, global variable, or OpUndef instruction to this module. inline void AddGlobalValue(std::unique_ptr<Instruction> v); + // Prepends a function declaration to this module. + inline void AddFunctionDeclaration(std::unique_ptr<Function> f); + // Appends a function to this module. inline void AddFunction(std::unique_ptr<Function> f); @@ -379,6 +383,11 @@ types_values_.push_back(std::move(v)); } +inline void Module::AddFunctionDeclaration(std::unique_ptr<Function> f) { + // function declarations must come before function definitions. + functions_.emplace(functions_.begin(), std::move(f)); +} + inline void Module::AddFunction(std::unique_ptr<Function> f) { functions_.emplace_back(std::move(f)); }
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 46a92dd..c4c2b0f 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp
@@ -15,6 +15,7 @@ #include "spirv-tools/optimizer.hpp" #include <cassert> +#include <charconv> #include <memory> #include <string> #include <unordered_map> @@ -32,6 +33,15 @@ namespace spvtools { +std::vector<std::string> GetVectorOfStrings(const char** strings, + const size_t string_count) { + std::vector<std::string> result; + for (uint32_t i = 0; i < string_count; i++) { + result.emplace_back(strings[i]); + } + return result; +} + struct Optimizer::PassToken::Impl { Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {} @@ -109,7 +119,7 @@ // The legalization problem is essentially a very general copy propagation // problem. The optimization we use are all used to either do copy propagation // or enable more copy propagation. -Optimizer& Optimizer::RegisterLegalizationPasses() { +Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) { return // Wrap OpKill instructions so all other code can be inlined. RegisterPass(CreateWrapOpKillPass()) @@ -129,16 +139,16 @@ // Propagate the value stored to the loads in very simple cases. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) // Split up aggregates so they are easier to deal with. .RegisterPass(CreateScalarReplacementPass(0)) // Remove loads and stores so everything is in intermediate values. // Takes care of copy propagation of non-members. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateLocalMultiStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) // Propagate constants to get as many constant conditions on branches // as possible. .RegisterPass(CreateCCPPass()) @@ -147,7 +157,7 @@ // Copy propagate members. Cleans up code sequences generated by // scalar replacement. Also important for removing OpPhi nodes. .RegisterPass(CreateSimplificationPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateCopyPropagateArraysPass()) // May need loop unrolling here see // https://github.com/Microsoft/DirectXShaderCompiler/pull/930 @@ -156,30 +166,36 @@ .RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateDeadInsertElimPass()) .RegisterPass(CreateReduceLoadSizePass()) - .RegisterPass(CreateAggressiveDCEPass()) - .RegisterPass(CreateInterpolateFixupPass()); + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()) + .RegisterPass(CreateInterpolateFixupPass()) + .RegisterPass(CreateInvocationInterlockPlacementPass()); } -Optimizer& Optimizer::RegisterPerformancePasses() { +Optimizer& Optimizer::RegisterLegalizationPasses() { + return RegisterLegalizationPasses(false); +} + +Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) { return RegisterPass(CreateWrapOpKillPass()) .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) .RegisterPass(CreateInlineExhaustivePass()) .RegisterPass(CreateEliminateDeadFunctionsPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreatePrivateToLocalPass()) .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateScalarReplacementPass()) .RegisterPass(CreateLocalAccessChainConvertPass()) .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateLocalMultiStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateCCPPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateLoopUnrollPass(true)) .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateRedundancyEliminationPass()) @@ -189,9 +205,9 @@ .RegisterPass(CreateLocalAccessChainConvertPass()) .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) .RegisterPass(CreateLocalSingleStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateSSARewritePass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateDeadInsertElimPass()) .RegisterPass(CreateDeadBranchElimPass()) @@ -199,7 +215,7 @@ .RegisterPass(CreateIfConversionPass()) .RegisterPass(CreateCopyPropagateArraysPass()) .RegisterPass(CreateReduceLoadSizePass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateBlockMergePass()) .RegisterPass(CreateRedundancyEliminationPass()) .RegisterPass(CreateDeadBranchElimPass()) @@ -207,7 +223,11 @@ .RegisterPass(CreateSimplificationPass()); } -Optimizer& Optimizer::RegisterSizePasses() { +Optimizer& Optimizer::RegisterPerformancePasses() { + return RegisterPerformancePasses(false); +} + +Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) { return RegisterPass(CreateWrapOpKillPass()) .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateMergeReturnPass()) @@ -224,12 +244,12 @@ .RegisterPass(CreateLocalSingleStoreElimPass()) .RegisterPass(CreateIfConversionPass()) .RegisterPass(CreateSimplificationPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateDeadBranchElimPass()) .RegisterPass(CreateBlockMergePass()) .RegisterPass(CreateLocalAccessChainConvertPass()) .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateCopyPropagateArraysPass()) .RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateDeadInsertElimPass()) @@ -239,13 +259,20 @@ .RegisterPass(CreateLocalMultiStoreElimPass()) .RegisterPass(CreateRedundancyEliminationPass()) .RegisterPass(CreateSimplificationPass()) - .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) .RegisterPass(CreateCFGCleanupPass()); } +Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); } + bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) { + return RegisterPassesFromFlags(flags, false); +} + +bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags, + bool preserve_interface) { for (const auto& flag : flags) { - if (!RegisterPassFromFlag(flag)) { + if (!RegisterPassFromFlag(flag, preserve_interface)) { return false; } } @@ -269,6 +296,11 @@ } bool Optimizer::RegisterPassFromFlag(const std::string& flag) { + return RegisterPassFromFlag(flag, false); +} + +bool Optimizer::RegisterPassFromFlag(const std::string& flag, + bool preserve_interface) { if (!FlagHasValidForm(flag)) { return false; } @@ -330,7 +362,7 @@ } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { - RegisterPass(CreateAggressiveDCEPass()); + RegisterPass(CreateAggressiveDCEPass(preserve_interface)); } else if (pass_name == "eliminate-insert-extract") { RegisterPass(CreateInsertExtractElimPass()); } else if (pass_name == "eliminate-local-single-block") { @@ -419,32 +451,26 @@ RegisterPass(CreateWorkaround1209Pass()); } else if (pass_name == "replace-invalid-opcode") { RegisterPass(CreateReplaceInvalidOpcodePass()); - } else if (pass_name == "inst-bindless-check") { - RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false)); + } else if (pass_name == "inst-bindless-check" || + pass_name == "inst-desc-idx-check" || + pass_name == "inst-buff-oob-check") { + // preserve legacy names + RegisterPass(CreateInstBindlessCheckPass(23)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass(true)); - } else if (pass_name == "inst-desc-idx-check") { - RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true)); - RegisterPass(CreateSimplificationPass()); - RegisterPass(CreateDeadBranchElimPass()); - RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass(true)); - } else if (pass_name == "inst-buff-oob-check") { - RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true)); - RegisterPass(CreateSimplificationPass()); - RegisterPass(CreateDeadBranchElimPass()); - RegisterPass(CreateBlockMergePass()); - RegisterPass(CreateAggressiveDCEPass(true)); } else if (pass_name == "inst-buff-addr-check") { - RegisterPass(CreateInstBuffAddrCheckPass(7, 23)); - RegisterPass(CreateAggressiveDCEPass(true)); + RegisterPass(CreateInstBuffAddrCheckPass(23)); } else if (pass_name == "convert-relaxed-to-half") { RegisterPass(CreateConvertRelaxedToHalfPass()); } else if (pass_name == "relax-float-ops") { RegisterPass(CreateRelaxFloatOpsPass()); } else if (pass_name == "inst-debug-printf") { + // This private option is not for user consumption. + // It is here to assist in debugging and fixing the debug printf + // instrumentation pass. + // For users who wish to utilize debug printf, see the white paper at + // https://www.lunarg.com/wp-content/uploads/2021/08/Using-Debug-Printf-02August2021.pdf RegisterPass(CreateInstDebugPrintfPass(7, 23)); } else if (pass_name == "simplify-instructions") { RegisterPass(CreateSimplificationPass()); @@ -507,11 +533,11 @@ } else if (pass_name == "fix-storage-class") { RegisterPass(CreateFixStorageClassPass()); } else if (pass_name == "O") { - RegisterPerformancePasses(); + RegisterPerformancePasses(preserve_interface); } else if (pass_name == "Os") { - RegisterSizePasses(); + RegisterSizePasses(preserve_interface); } else if (pass_name == "legalize-hlsl") { - RegisterLegalizationPasses(); + RegisterLegalizationPasses(preserve_interface); } else if (pass_name == "remove-unused-interface-variables") { RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()); } else if (pass_name == "graphics-robust-access") { @@ -548,6 +574,58 @@ pass_args.c_str()); return false; } + } else if (pass_name == "switch-descriptorset") { + if (pass_args.size() == 0) { + Error(consumer(), nullptr, {}, + "--switch-descriptorset requires a from:to argument."); + return false; + } + uint32_t from_set = 0, to_set = 0; + const char* start = pass_args.data(); + const char* end = pass_args.data() + pass_args.size(); + + auto result = std::from_chars(start, end, from_set); + if (result.ec != std::errc()) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --switch-descriptorset: %s", + pass_args.c_str()); + return false; + } + start = result.ptr; + if (start[0] != ':') { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --switch-descriptorset: %s", + pass_args.c_str()); + return false; + } + start++; + result = std::from_chars(start, end, to_set); + if (result.ec != std::errc() || result.ptr != end) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --switch-descriptorset: %s", + pass_args.c_str()); + return false; + } + RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set)); + } else if (pass_name == "modify-maximal-reconvergence") { + if (pass_args.size() == 0) { + Error(consumer(), nullptr, {}, + "--modify-maximal-reconvergence requires an argument"); + return false; + } + if (pass_args == "add") { + RegisterPass(CreateModifyMaximalReconvergencePass(true)); + } else if (pass_args == "remove") { + RegisterPass(CreateModifyMaximalReconvergencePass(false)); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --modify-maximal-reconvergence: %s (must be " + "'add' or 'remove')", + pass_args.c_str()); + return false; + } + } else if (pass_name == "trim-capabilities") { + RegisterPass(CreateTrimCapabilitiesPass()); } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -945,14 +1023,9 @@ MakeUnique<opt::UpgradeMemoryModel>()); } -Optimizer::PassToken CreateInstBindlessCheckPass( - uint32_t desc_set, uint32_t shader_id, bool desc_length_enable, - bool desc_init_enable, bool buff_oob_enable, bool texbuff_oob_enable) { +Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id) { return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::InstBindlessCheckPass>( - desc_set, shader_id, desc_length_enable, desc_init_enable, - buff_oob_enable, texbuff_oob_enable, - desc_length_enable || desc_init_enable || buff_oob_enable)); + MakeUnique<opt::InstBindlessCheckPass>(shader_id)); } Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set, @@ -961,10 +1034,9 @@ MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id)); } -Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, - uint32_t shader_id) { +Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id) { return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id)); + MakeUnique<opt::InstBuffAddrCheckPass>(shader_id)); } Optimizer::PassToken CreateConvertRelaxedToHalfPass() { @@ -1074,6 +1146,26 @@ return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::FixFuncCallArgumentsPass>()); } + +Optimizer::PassToken CreateTrimCapabilitiesPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::TrimCapabilitiesPass>()); +} + +Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::SwitchDescriptorSetPass>(from, to)); +} + +Optimizer::PassToken CreateInvocationInterlockPlacementPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::InvocationInterlockPlacementPass>()); +} + +Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::ModifyMaximalReconvergence>(add)); +} } // namespace spvtools extern "C" { @@ -1122,13 +1214,19 @@ SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags( spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) { - std::vector<std::string> opt_flags; - for (uint32_t i = 0; i < flag_count; i++) { - opt_flags.emplace_back(flags[i]); - } + std::vector<std::string> opt_flags = + spvtools::GetVectorOfStrings(flags, flag_count); + return reinterpret_cast<spvtools::Optimizer*>(optimizer) + ->RegisterPassesFromFlags(opt_flags, false); +} - return reinterpret_cast<spvtools::Optimizer*>(optimizer)-> - RegisterPassesFromFlags(opt_flags); +SPIRV_TOOLS_EXPORT bool +spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) { + std::vector<std::string> opt_flags = + spvtools::GetVectorOfStrings(flags, flag_count); + return reinterpret_cast<spvtools::Optimizer*>(optimizer) + ->RegisterPassesFromFlags(opt_flags, true); } SPIRV_TOOLS_EXPORT
diff --git a/source/opt/passes.h b/source/opt/passes.h index eb3b1e5..9d027fb 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h
@@ -53,6 +53,7 @@ #include "source/opt/inst_debug_printf_pass.h" #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/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" #include "source/opt/local_redundancy_elimination.h" @@ -64,6 +65,7 @@ #include "source/opt/loop_unroller.h" #include "source/opt/loop_unswitch_pass.h" #include "source/opt/merge_return_pass.h" +#include "source/opt/modify_maximal_reconvergence.h" #include "source/opt/null_pass.h" #include "source/opt/private_to_local_pass.h" #include "source/opt/reduce_load_size.h" @@ -82,6 +84,8 @@ #include "source/opt/strength_reduction_pass.h" #include "source/opt/strip_debug_info_pass.h" #include "source/opt/strip_nonsemantic_info_pass.h" +#include "source/opt/switch_descriptorset_pass.h" +#include "source/opt/trim_capabilities_pass.h" #include "source/opt/unify_const_pass.h" #include "source/opt/upgrade_memory_model.h" #include "source/opt/vector_dce.h"
diff --git a/source/opt/switch_descriptorset_pass.cpp b/source/opt/switch_descriptorset_pass.cpp new file mode 100644 index 0000000..f07c917 --- /dev/null +++ b/source/opt/switch_descriptorset_pass.cpp
@@ -0,0 +1,46 @@ +// Copyright (c) 2023 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/switch_descriptorset_pass.h" + +#include "source/opt/ir_builder.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status SwitchDescriptorSetPass::Process() { + Status status = Status::SuccessWithoutChange; + auto* deco_mgr = context()->get_decoration_mgr(); + + for (Instruction& var : context()->types_values()) { + if (var.opcode() != spv::Op::OpVariable) { + continue; + } + auto decos = deco_mgr->GetDecorationsFor(var.result_id(), false); + for (const auto& deco : decos) { + spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u)); + if (d == spv::Decoration::DescriptorSet && + deco->GetSingleWordInOperand(2u) == ds_from_) { + deco->SetInOperand(2u, {ds_to_}); + status = Status::SuccessWithChange; + break; + } + } + } + return status; +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/switch_descriptorset_pass.h b/source/opt/switch_descriptorset_pass.h new file mode 100644 index 0000000..2084e9c --- /dev/null +++ b/source/opt/switch_descriptorset_pass.h
@@ -0,0 +1,52 @@ +// Copyright (c) 2023 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. + +#pragma once + +#include <cstdio> +#include <memory> +#include <queue> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SwitchDescriptorSetPass : public Pass { + public: + SwitchDescriptorSetPass(uint32_t ds_from, uint32_t ds_to) + : ds_from_(ds_from), ds_to_(ds_to) {} + + const char* name() const override { return "switch-descriptorset"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + // this pass preserves everything except decorations + uint32_t mask = ((IRContext::kAnalysisEnd << 1) - 1); + mask &= ~static_cast<uint32_t>(IRContext::kAnalysisDecorations); + return static_cast<IRContext::Analysis>(mask); + } + + private: + uint32_t ds_from_; + uint32_t ds_to_; +}; + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/trim_capabilities_pass.cpp b/source/opt/trim_capabilities_pass.cpp new file mode 100644 index 0000000..24f9e46 --- /dev/null +++ b/source/opt/trim_capabilities_pass.cpp
@@ -0,0 +1,649 @@ +// Copyright (c) 2023 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/opt/trim_capabilities_pass.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <functional> +#include <optional> +#include <queue> +#include <stack> +#include <unordered_map> +#include <unordered_set> +#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/util/string_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +constexpr uint32_t kOpTypeFloatSizeIndex = 0; +constexpr uint32_t kOpTypePointerStorageClassIndex = 0; +constexpr uint32_t kTypeArrayTypeIndex = 0; +constexpr uint32_t kOpTypeScalarBitWidthIndex = 0; +constexpr uint32_t kTypePointerTypeIdInIndex = 1; +constexpr uint32_t kOpTypeIntSizeIndex = 0; +constexpr uint32_t kOpTypeImageDimIndex = 1; +constexpr uint32_t kOpTypeImageArrayedIndex = kOpTypeImageDimIndex + 2; +constexpr uint32_t kOpTypeImageMSIndex = kOpTypeImageArrayedIndex + 1; +constexpr uint32_t kOpTypeImageSampledIndex = kOpTypeImageMSIndex + 1; +constexpr uint32_t kOpTypeImageFormatIndex = kOpTypeImageSampledIndex + 1; +constexpr uint32_t kOpImageReadImageIndex = 0; +constexpr uint32_t kOpImageSparseReadImageIndex = 0; + +// DFS visit of the type defined by `instruction`. +// If `condition` is true, children of the current node are visited. +// If `condition` is false, the children of the current node are ignored. +template <class UnaryPredicate> +static void DFSWhile(const Instruction* instruction, UnaryPredicate condition) { + std::stack<uint32_t> instructions_to_visit; + instructions_to_visit.push(instruction->result_id()); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + while (!instructions_to_visit.empty()) { + const Instruction* item = def_use_mgr->GetDef(instructions_to_visit.top()); + instructions_to_visit.pop(); + + if (!condition(item)) { + continue; + } + + if (item->opcode() == spv::Op::OpTypePointer) { + instructions_to_visit.push( + item->GetSingleWordInOperand(kTypePointerTypeIdInIndex)); + continue; + } + + if (item->opcode() == spv::Op::OpTypeMatrix || + item->opcode() == spv::Op::OpTypeVector || + item->opcode() == spv::Op::OpTypeArray || + item->opcode() == spv::Op::OpTypeRuntimeArray) { + instructions_to_visit.push( + item->GetSingleWordInOperand(kTypeArrayTypeIndex)); + continue; + } + + if (item->opcode() == spv::Op::OpTypeStruct) { + item->ForEachInOperand([&instructions_to_visit](const uint32_t* op_id) { + instructions_to_visit.push(*op_id); + }); + continue; + } + } +} + +// Walks the type defined by `instruction` (OpType* only). +// Returns `true` if any call to `predicate` with the type/subtype returns true. +template <class UnaryPredicate> +static bool AnyTypeOf(const Instruction* instruction, + UnaryPredicate predicate) { + assert(IsTypeInst(instruction->opcode()) && + "AnyTypeOf called with a non-type instruction."); + + bool found_one = false; + DFSWhile(instruction, [&found_one, predicate](const Instruction* node) { + if (found_one || predicate(node)) { + found_one = true; + return false; + } + + return true; + }); + return found_one; +} + +static bool is16bitType(const Instruction* instruction) { + if (instruction->opcode() != spv::Op::OpTypeInt && + instruction->opcode() != spv::Op::OpTypeFloat) { + return false; + } + + return instruction->GetSingleWordInOperand(kOpTypeScalarBitWidthIndex) == 16; +} + +static bool Has16BitCapability(const FeatureManager* feature_manager) { + const CapabilitySet& capabilities = feature_manager->GetCapabilities(); + return capabilities.contains(spv::Capability::Float16) || + capabilities.contains(spv::Capability::Int16); +} + +} // namespace + +// ============== Begin opcode handler implementations. ======================= +// +// Adding support for a new capability should only require adding a new handler, +// and updating the +// kSupportedCapabilities/kUntouchableCapabilities/kFordiddenCapabilities lists. +// +// Handler names follow the following convention: +// Handler_<Opcode>_<Capability>() + +static std::optional<spv::Capability> Handler_OpTypeFloat_Float16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeFloat && + "This handler only support OpTypeFloat opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeFloatSizeIndex); + return size == 16 ? std::optional(spv::Capability::Float16) : std::nullopt; +} + +static std::optional<spv::Capability> Handler_OpTypeFloat_Float64( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeFloat && + "This handler only support OpTypeFloat opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeFloatSizeIndex); + return size == 64 ? std::optional(spv::Capability::Float64) : std::nullopt; +} + +static std::optional<spv::Capability> +Handler_OpTypePointer_StorageInputOutput16(const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has an Input/Output + // storage class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return std::nullopt; + } + + if (!Has16BitCapability(instruction->context()->get_feature_mgr())) { + return std::nullopt; + } + + return AnyTypeOf(instruction, is16bitType) + ? std::optional(spv::Capability::StorageInputOutput16) + : std::nullopt; +} + +static std::optional<spv::Capability> +Handler_OpTypePointer_StoragePushConstant16(const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has a PushConstant storage + // class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::PushConstant) { + return std::nullopt; + } + + if (!Has16BitCapability(instruction->context()->get_feature_mgr())) { + return std::nullopt; + } + + return AnyTypeOf(instruction, is16bitType) + ? std::optional(spv::Capability::StoragePushConstant16) + : std::nullopt; +} + +static std::optional<spv::Capability> +Handler_OpTypePointer_StorageUniformBufferBlock16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has a Uniform storage + // class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::Uniform) { + return std::nullopt; + } + + if (!Has16BitCapability(instruction->context()->get_feature_mgr())) { + 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::BufferBlock)) { + return false; + } + + return AnyTypeOf(item, is16bitType); + }); + + return matchesCondition + ? std::optional(spv::Capability::StorageUniformBufferBlock16) + : std::nullopt; +} + +static std::optional<spv::Capability> Handler_OpTypePointer_StorageUniform16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has a Uniform storage + // class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::Uniform) { + return std::nullopt; + } + + const auto* feature_manager = instruction->context()->get_feature_mgr(); + if (!Has16BitCapability(feature_manager)) { + return std::nullopt; + } + + const bool hasBufferBlockCapability = + feature_manager->GetCapabilities().contains( + spv::Capability::StorageUniformBufferBlock16); + const auto* decoration_mgr = instruction->context()->get_decoration_mgr(); + bool found16bitType = false; + + DFSWhile(instruction, [decoration_mgr, hasBufferBlockCapability, + &found16bitType](const Instruction* item) { + if (found16bitType) { + return false; + } + + if (hasBufferBlockCapability && + decoration_mgr->HasDecoration(item->result_id(), + spv::Decoration::BufferBlock)) { + return false; + } + + if (is16bitType(item)) { + found16bitType = true; + return false; + } + + return true; + }); + + return found16bitType ? std::optional(spv::Capability::StorageUniform16) + : std::nullopt; +} + +static std::optional<spv::Capability> Handler_OpTypeInt_Int16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeInt && + "This handler only support OpTypeInt opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeIntSizeIndex); + return size == 16 ? std::optional(spv::Capability::Int16) : std::nullopt; +} + +static std::optional<spv::Capability> Handler_OpTypeInt_Int64( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeInt && + "This handler only support OpTypeInt opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeIntSizeIndex); + return size == 64 ? std::optional(spv::Capability::Int64) : std::nullopt; +} + +static std::optional<spv::Capability> Handler_OpTypeImage_ImageMSArray( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeImage && + "This handler only support OpTypeImage opcodes."); + + const uint32_t arrayed = + instruction->GetSingleWordInOperand(kOpTypeImageArrayedIndex); + const uint32_t ms = instruction->GetSingleWordInOperand(kOpTypeImageMSIndex); + const uint32_t sampled = + instruction->GetSingleWordInOperand(kOpTypeImageSampledIndex); + + return arrayed == 1 && sampled == 2 && ms == 1 + ? std::optional(spv::Capability::ImageMSArray) + : std::nullopt; +} + +static std::optional<spv::Capability> +Handler_OpImageRead_StorageImageReadWithoutFormat( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpImageRead && + "This handler only support OpImageRead opcodes."); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + const uint32_t image_index = + instruction->GetSingleWordInOperand(kOpImageReadImageIndex); + const uint32_t type_index = def_use_mgr->GetDef(image_index)->type_id(); + const Instruction* type = def_use_mgr->GetDef(type_index); + const uint32_t dim = type->GetSingleWordInOperand(kOpTypeImageDimIndex); + const uint32_t format = type->GetSingleWordInOperand(kOpTypeImageFormatIndex); + + const bool is_unknown = spv::ImageFormat(format) == spv::ImageFormat::Unknown; + const bool requires_capability_for_unknown = + spv::Dim(dim) != spv::Dim::SubpassData; + return is_unknown && requires_capability_for_unknown + ? std::optional(spv::Capability::StorageImageReadWithoutFormat) + : std::nullopt; +} + +static std::optional<spv::Capability> +Handler_OpImageSparseRead_StorageImageReadWithoutFormat( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpImageSparseRead && + "This handler only support OpImageSparseRead opcodes."); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + const uint32_t image_index = + instruction->GetSingleWordInOperand(kOpImageSparseReadImageIndex); + const uint32_t type_index = def_use_mgr->GetDef(image_index)->type_id(); + const Instruction* type = def_use_mgr->GetDef(type_index); + const uint32_t format = type->GetSingleWordInOperand(kOpTypeImageFormatIndex); + + return spv::ImageFormat(format) == spv::ImageFormat::Unknown + ? std::optional(spv::Capability::StorageImageReadWithoutFormat) + : std::nullopt; +} + +// Opcode of interest to determine capabilities requirements. +constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 12> kOpcodeHandlers{{ + // clang-format off + {spv::Op::OpImageRead, Handler_OpImageRead_StorageImageReadWithoutFormat}, + {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}, + // clang-format on +}}; + +// ============== End opcode handler implementations. ======================= + +namespace { +ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities, + const AssemblyGrammar& grammar) { + ExtensionSet output; + const spv_operand_desc_t* desc = nullptr; + for (auto capability : capabilities) { + if (SPV_SUCCESS != grammar.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]); + } + } + + return output; +} +} // namespace + +TrimCapabilitiesPass::TrimCapabilitiesPass() + : supportedCapabilities_( + TrimCapabilitiesPass::kSupportedCapabilities.cbegin(), + TrimCapabilitiesPass::kSupportedCapabilities.cend()), + forbiddenCapabilities_( + TrimCapabilitiesPass::kForbiddenCapabilities.cbegin(), + TrimCapabilitiesPass::kForbiddenCapabilities.cend()), + untouchableCapabilities_( + TrimCapabilitiesPass::kUntouchableCapabilities.cbegin(), + TrimCapabilitiesPass::kUntouchableCapabilities.cend()), + opcodeHandlers_(kOpcodeHandlers.cbegin(), kOpcodeHandlers.cend()) {} + +void TrimCapabilitiesPass::addInstructionRequirementsForOpcode( + spv::Op opcode, CapabilitySet* capabilities, + ExtensionSet* extensions) const { + // Ignoring OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT + // because they have three possible capabilities, only one of which is needed + if (opcode == spv::Op::OpBeginInvocationInterlockEXT || + opcode == spv::Op::OpEndInvocationInterlockEXT) { + return; + } + + const spv_opcode_desc_t* desc = {}; + auto result = context()->grammar().lookupOpcode(opcode, &desc); + if (result != SPV_SUCCESS) { + return; + } + + addSupportedCapabilitiesToSet(desc, capabilities); + addSupportedExtensionsToSet(desc, extensions); +} + +void TrimCapabilitiesPass::addInstructionRequirementsForOperand( + const Operand& operand, CapabilitySet* capabilities, + ExtensionSet* extensions) const { + // No supported capability relies on a 2+-word operand. + if (operand.words.size() != 1) { + return; + } + + // No supported capability relies on a literal string operand or an ID. + if (operand.type == SPV_OPERAND_TYPE_LITERAL_STRING || + operand.type == SPV_OPERAND_TYPE_ID || + operand.type == SPV_OPERAND_TYPE_RESULT_ID) { + return; + } + + // If the Vulkan memory model is declared and any instruction uses Device + // scope, the VulkanMemoryModelDeviceScope capability must be declared. This + // rule cannot be covered by the grammar, so must be checked explicitly. + if (operand.type == SPV_OPERAND_TYPE_SCOPE_ID) { + const Instruction* memory_model = context()->GetMemoryModel(); + if (memory_model && memory_model->GetSingleWordInOperand(1u) == + uint32_t(spv::MemoryModel::Vulkan)) { + capabilities->insert(spv::Capability::VulkanMemoryModelDeviceScope); + } + } + + // 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); + if (result != SPV_SUCCESS) { + return; + } + addSupportedCapabilitiesToSet(desc, capabilities); + addSupportedExtensionsToSet(desc, extensions); + return; + } + + // case 2: operand can be a bitmask, we need to decompose the lookup. + for (uint32_t i = 0; i < 32; i++) { + const uint32_t mask = (1 << i) & operand.words[0]; + if (!mask) { + continue; + } + + const spv_operand_desc_t* desc = {}; + auto result = context()->grammar().lookupOperand(operand.type, mask, &desc); + if (result != SPV_SUCCESS) { + continue; + } + + addSupportedCapabilitiesToSet(desc, capabilities); + addSupportedExtensionsToSet(desc, extensions); + } +} + +void TrimCapabilitiesPass::addInstructionRequirements( + Instruction* instruction, CapabilitySet* capabilities, + ExtensionSet* extensions) const { + // Ignoring OpCapability and OpExtension instructions. + if (instruction->opcode() == spv::Op::OpCapability || + instruction->opcode() == spv::Op::OpExtension) { + return; + } + + addInstructionRequirementsForOpcode(instruction->opcode(), capabilities, + extensions); + + // Second case: one of the opcode operand is gated by a capability. + const uint32_t operandCount = instruction->NumOperands(); + for (uint32_t i = 0; i < operandCount; i++) { + addInstructionRequirementsForOperand(instruction->GetOperand(i), + capabilities, extensions); + } + + // Last case: some complex logic needs to be run to determine capabilities. + auto[begin, end] = opcodeHandlers_.equal_range(instruction->opcode()); + for (auto it = begin; it != end; it++) { + const OpcodeHandler handler = it->second; + auto result = handler(instruction); + if (!result.has_value()) { + continue; + } + + capabilities->insert(*result); + } +} + +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); + if (result != SPV_SUCCESS) { + return; + } + addSupportedExtensionsToSet(desc, extensions); +} + +std::pair<CapabilitySet, ExtensionSet> +TrimCapabilitiesPass::DetermineRequiredCapabilitiesAndExtensions() const { + CapabilitySet required_capabilities; + ExtensionSet required_extensions; + + get_module()->ForEachInst([&](Instruction* instruction) { + addInstructionRequirements(instruction, &required_capabilities, + &required_extensions); + }); + + for (auto capability : required_capabilities) { + AddExtensionsForOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast<uint32_t>(capability), + &required_extensions); + } + +#if !defined(NDEBUG) + // Debug only. We check the outputted required capabilities against the + // supported capabilities list. The supported capabilities list is useful for + // API users to quickly determine if they can use the pass or not. But this + // list has to remain up-to-date with the pass code. If we can detect a + // capability as required, but it's not listed, it means the list is + // out-of-sync. This method is not ideal, but should cover most cases. + { + for (auto capability : required_capabilities) { + assert(supportedCapabilities_.contains(capability) && + "Module is using a capability that is not listed as supported."); + } + } +#endif + + return std::make_pair(std::move(required_capabilities), + std::move(required_extensions)); +} + +Pass::Status TrimCapabilitiesPass::TrimUnrequiredCapabilities( + const CapabilitySet& required_capabilities) const { + const FeatureManager* feature_manager = context()->get_feature_mgr(); + CapabilitySet capabilities_to_trim; + for (auto capability : feature_manager->GetCapabilities()) { + // Some capabilities cannot be safely removed. Leaving them untouched. + if (untouchableCapabilities_.contains(capability)) { + continue; + } + + // If the capability is unsupported, don't trim it. + if (!supportedCapabilities_.contains(capability)) { + continue; + } + + if (required_capabilities.contains(capability)) { + continue; + } + + capabilities_to_trim.insert(capability); + } + + for (auto capability : capabilities_to_trim) { + context()->RemoveCapability(capability); + } + + return capabilities_to_trim.size() == 0 ? Pass::Status::SuccessWithoutChange + : Pass::Status::SuccessWithChange; +} + +Pass::Status TrimCapabilitiesPass::TrimUnrequiredExtensions( + const ExtensionSet& required_extensions) const { + const auto supported_extensions = + getExtensionsRelatedTo(supportedCapabilities_, context()->grammar()); + + bool modified_module = false; + for (auto extension : supported_extensions) { + if (required_extensions.contains(extension)) { + continue; + } + + if (context()->RemoveExtension(extension)) { + modified_module = true; + } + } + + return modified_module ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +bool TrimCapabilitiesPass::HasForbiddenCapabilities() const { + // EnumSet.HasAnyOf returns `true` if the given set is empty. + if (forbiddenCapabilities_.size() == 0) { + return false; + } + + const auto& capabilities = context()->get_feature_mgr()->GetCapabilities(); + return capabilities.HasAnyOf(forbiddenCapabilities_); +} + +Pass::Status TrimCapabilitiesPass::Process() { + if (HasForbiddenCapabilities()) { + return Status::SuccessWithoutChange; + } + + auto[required_capabilities, required_extensions] = + DetermineRequiredCapabilitiesAndExtensions(); + + Pass::Status capStatus = TrimUnrequiredCapabilities(required_capabilities); + Pass::Status extStatus = TrimUnrequiredExtensions(required_extensions); + + return capStatus == Pass::Status::SuccessWithChange || + extStatus == Pass::Status::SuccessWithChange + ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h new file mode 100644 index 0000000..81c07b8 --- /dev/null +++ b/source/opt/trim_capabilities_pass.h
@@ -0,0 +1,205 @@ +// Copyright (c) 2023 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_OPT_TRIM_CAPABILITIES_PASS_H_ +#define SOURCE_OPT_TRIM_CAPABILITIES_PASS_H_ + +#include <algorithm> +#include <array> +#include <functional> +#include <optional> +#include <unordered_map> +#include <unordered_set> + +#include "source/enum_set.h" +#include "source/extensions.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace opt { + +// This is required for NDK build. The unordered_set/unordered_map +// implementation don't work with class enums. +struct ClassEnumHash { + std::size_t operator()(spv::Capability value) const { + using StoringType = typename std::underlying_type_t<spv::Capability>; + return std::hash<StoringType>{}(static_cast<StoringType>(value)); + } + + std::size_t operator()(spv::Op value) const { + using StoringType = typename std::underlying_type_t<spv::Op>; + return std::hash<StoringType>{}(static_cast<StoringType>(value)); + } +}; + +// An opcode handler is a function which, given an instruction, returns either +// the required capability, or nothing. +// Each handler checks one case for a capability requirement. +// +// Example: +// - `OpTypeImage` can have operand `A` operand which requires capability 1 +// - `OpTypeImage` can also have operand `B` which requires capability 2. +// -> We have 2 handlers: `Handler_OpTypeImage_1` and +// `Handler_OpTypeImage_2`. +using OpcodeHandler = + std::optional<spv::Capability> (*)(const Instruction* instruction); + +// This pass tried to remove superfluous capabilities declared in the module. +// - If all the capabilities listed by an extension are removed, the extension +// is also trimmed. +// - If the module countains any capability listed in `kForbiddenCapabilities`, +// the module is left untouched. +// - No capabilities listed in `kUntouchableCapabilities` are trimmed, even when +// not used. +// - Only capabilitied listed in `kSupportedCapabilities` are supported. +// - If the module contains unsupported capabilities, results might be +// incorrect. +class TrimCapabilitiesPass : public Pass { + private: + // All the capabilities supported by this optimization pass. If your module + // contains unsupported instruction, the pass could yield bad results. + static constexpr std::array kSupportedCapabilities{ + // clang-format off + spv::Capability::ComputeDerivativeGroupLinearNV, + spv::Capability::ComputeDerivativeGroupQuadsNV, + spv::Capability::Float16, + spv::Capability::Float64, + spv::Capability::FragmentShaderPixelInterlockEXT, + spv::Capability::FragmentShaderSampleInterlockEXT, + spv::Capability::FragmentShaderShadingRateInterlockEXT, + spv::Capability::Groups, + spv::Capability::ImageMSArray, + spv::Capability::Int16, + spv::Capability::Int64, + spv::Capability::Linkage, + spv::Capability::MinLod, + spv::Capability::PhysicalStorageBufferAddresses, + spv::Capability::RayQueryKHR, + spv::Capability::RayTracingKHR, + spv::Capability::RayTraversalPrimitiveCullingKHR, + spv::Capability::Shader, + spv::Capability::ShaderClockKHR, + spv::Capability::StorageImageReadWithoutFormat, + spv::Capability::StorageInputOutput16, + spv::Capability::StoragePushConstant16, + spv::Capability::StorageUniform16, + spv::Capability::StorageUniformBufferBlock16, + spv::Capability::VulkanMemoryModelDeviceScope, + spv::Capability::GroupNonUniformPartitionedNV + // clang-format on + }; + + // Those capabilities disable all transformation of the module. + static constexpr std::array kForbiddenCapabilities{ + spv::Capability::Linkage, + }; + + // Those capabilities are never removed from a module because we cannot + // guess from the SPIR-V only if they are required or not. + static constexpr std::array kUntouchableCapabilities{ + spv::Capability::Shader, + }; + + public: + TrimCapabilitiesPass(); + TrimCapabilitiesPass(const TrimCapabilitiesPass&) = delete; + TrimCapabilitiesPass(TrimCapabilitiesPass&&) = delete; + + 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]; + if (supportedCapabilities_.contains(capability)) { + output->insert(capability); + } + } + } + + // 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`. + template <class Descriptor> + inline void addSupportedExtensionsToSet(const Descriptor* const descriptor, + ExtensionSet* output) const { + if (descriptor->minVersion <= + spvVersionForTargetEnv(context()->GetTargetEnv())) { + return; + } + output->insert(descriptor->extensions, + descriptor->extensions + descriptor->numExtensions); + } + + void addInstructionRequirementsForOpcode(spv::Op opcode, + CapabilitySet* capabilities, + ExtensionSet* extensions) const; + void addInstructionRequirementsForOperand(const Operand& operand, + CapabilitySet* capabilities, + ExtensionSet* extensions) const; + + // Given an `instruction`, determines the capabilities it requires, and output + // them in `capabilities`. The returned capabilities form a subset of + // kSupportedCapabilities. + void addInstructionRequirements(Instruction* instruction, + CapabilitySet* capabilities, + ExtensionSet* extensions) const; + + // Given an operand `type` and `value`, adds the extensions it would require + // to `extensions`. + void AddExtensionsForOperand(const spv_operand_type_t type, + const uint32_t value, + ExtensionSet* extensions) const; + + // Returns the list of required capabilities and extensions for the module. + // The returned capabilities form a subset of kSupportedCapabilities. + std::pair<CapabilitySet, ExtensionSet> + DetermineRequiredCapabilitiesAndExtensions() const; + + // Trims capabilities not listed in `required_capabilities` if possible. + // Returns whether or not the module was modified. + Pass::Status TrimUnrequiredCapabilities( + const CapabilitySet& required_capabilities) const; + + // Trims extensions not listed in `required_extensions` if supported by this + // pass. An extensions is considered supported as soon as one capability this + // pass support requires it. + Pass::Status TrimUnrequiredExtensions( + const ExtensionSet& required_extensions) const; + + // Returns if the analyzed module contains any forbidden capability. + bool HasForbiddenCapabilities() const; + + public: + const char* name() const override { return "trim-capabilities"; } + Status Process() override; + + private: + const CapabilitySet supportedCapabilities_; + const CapabilitySet forbiddenCapabilities_; + const CapabilitySet untouchableCapabilities_; + const std::unordered_multimap<spv::Op, OpcodeHandler, ClassEnumHash> + opcodeHandlers_; +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_TRIM_CAPABILITIES_H_
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index 1b1aead..7b609bc 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp
@@ -423,6 +423,23 @@ {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}}); break; } + case Type::kCooperativeMatrixKHR: { + auto coop_mat = type->AsCooperativeMatrixKHR(); + uint32_t const component_type = + GetTypeInstruction(coop_mat->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique<Instruction>( + context(), spv::Op::OpTypeCooperativeMatrixKHR, 0, id, + std::initializer_list<Operand>{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->use_id()}}}); + break; + } default: assert(false && "Unexpected type"); break; @@ -500,13 +517,24 @@ context()->get_def_use_mgr()->AnalyzeInstUse(inst); } -Type* TypeManager::RebuildType(const Type& type) { +Type* TypeManager::RebuildType(uint32_t type_id, const Type& type) { + assert(type_id != 0); + // The comparison and hash on the type pool will avoid inserting the rebuilt // type if an equivalent type already exists. The rebuilt type will be deleted // when it goes out of scope at the end of the function in that case. Repeated // insertions of the same Type will, at most, keep one corresponding object in // the type pool. std::unique_ptr<Type> rebuilt_ty; + + // If |type_id| is already present in the type pool, return the existing type. + // This saves extra work in the type builder and prevents running into + // circular issues (https://github.com/KhronosGroup/SPIRV-Tools/issues/5623). + Type* pool_ty = GetType(type_id); + if (pool_ty != nullptr) { + return pool_ty; + } + switch (type.kind()) { #define DefineNoSubtypeCase(kind) \ case Type::k##kind: \ @@ -533,43 +561,46 @@ case Type::kVector: { const Vector* vec_ty = type.AsVector(); const Type* ele_ty = vec_ty->element_type(); - rebuilt_ty = - MakeUnique<Vector>(RebuildType(*ele_ty), vec_ty->element_count()); + rebuilt_ty = MakeUnique<Vector>(RebuildType(GetId(ele_ty), *ele_ty), + vec_ty->element_count()); break; } case Type::kMatrix: { const Matrix* mat_ty = type.AsMatrix(); const Type* ele_ty = mat_ty->element_type(); - rebuilt_ty = - MakeUnique<Matrix>(RebuildType(*ele_ty), mat_ty->element_count()); + rebuilt_ty = MakeUnique<Matrix>(RebuildType(GetId(ele_ty), *ele_ty), + mat_ty->element_count()); break; } case Type::kImage: { const Image* image_ty = type.AsImage(); const Type* ele_ty = image_ty->sampled_type(); - rebuilt_ty = - MakeUnique<Image>(RebuildType(*ele_ty), image_ty->dim(), - image_ty->depth(), image_ty->is_arrayed(), - image_ty->is_multisampled(), image_ty->sampled(), - image_ty->format(), image_ty->access_qualifier()); + rebuilt_ty = MakeUnique<Image>( + RebuildType(GetId(ele_ty), *ele_ty), image_ty->dim(), + image_ty->depth(), image_ty->is_arrayed(), + image_ty->is_multisampled(), image_ty->sampled(), image_ty->format(), + image_ty->access_qualifier()); break; } case Type::kSampledImage: { const SampledImage* image_ty = type.AsSampledImage(); const Type* ele_ty = image_ty->image_type(); - rebuilt_ty = MakeUnique<SampledImage>(RebuildType(*ele_ty)); + rebuilt_ty = + MakeUnique<SampledImage>(RebuildType(GetId(ele_ty), *ele_ty)); break; } case Type::kArray: { const Array* array_ty = type.AsArray(); - rebuilt_ty = - MakeUnique<Array>(array_ty->element_type(), array_ty->length_info()); + const Type* ele_ty = array_ty->element_type(); + rebuilt_ty = MakeUnique<Array>(RebuildType(GetId(ele_ty), *ele_ty), + array_ty->length_info()); break; } case Type::kRuntimeArray: { const RuntimeArray* array_ty = type.AsRuntimeArray(); const Type* ele_ty = array_ty->element_type(); - rebuilt_ty = MakeUnique<RuntimeArray>(RebuildType(*ele_ty)); + rebuilt_ty = + MakeUnique<RuntimeArray>(RebuildType(GetId(ele_ty), *ele_ty)); break; } case Type::kStruct: { @@ -577,7 +608,7 @@ std::vector<const Type*> subtypes; subtypes.reserve(struct_ty->element_types().size()); for (const auto* ele_ty : struct_ty->element_types()) { - subtypes.push_back(RebuildType(*ele_ty)); + subtypes.push_back(RebuildType(GetId(ele_ty), *ele_ty)); } rebuilt_ty = MakeUnique<Struct>(subtypes); Struct* rebuilt_struct = rebuilt_ty->AsStruct(); @@ -594,7 +625,7 @@ case Type::kPointer: { const Pointer* pointer_ty = type.AsPointer(); const Type* ele_ty = pointer_ty->pointee_type(); - rebuilt_ty = MakeUnique<Pointer>(RebuildType(*ele_ty), + rebuilt_ty = MakeUnique<Pointer>(RebuildType(GetId(ele_ty), *ele_ty), pointer_ty->storage_class()); break; } @@ -604,9 +635,10 @@ std::vector<const Type*> param_types; param_types.reserve(function_ty->param_types().size()); for (const auto* param_ty : function_ty->param_types()) { - param_types.push_back(RebuildType(*param_ty)); + param_types.push_back(RebuildType(GetId(param_ty), *param_ty)); } - rebuilt_ty = MakeUnique<Function>(RebuildType(*ret_ty), param_types); + rebuilt_ty = MakeUnique<Function>(RebuildType(GetId(ret_ty), *ret_ty), + param_types); break; } case Type::kForwardPointer: { @@ -616,7 +648,7 @@ const Pointer* target_ptr = forward_ptr_ty->target_pointer(); if (target_ptr) { rebuilt_ty->AsForwardPointer()->SetTargetPointer( - RebuildType(*target_ptr)->AsPointer()); + RebuildType(GetId(target_ptr), *target_ptr)->AsPointer()); } break; } @@ -624,8 +656,17 @@ const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV(); const Type* component_type = cm_type->component_type(); rebuilt_ty = MakeUnique<CooperativeMatrixNV>( - RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(), - cm_type->columns_id()); + RebuildType(GetId(component_type), *component_type), + cm_type->scope_id(), cm_type->rows_id(), cm_type->columns_id()); + break; + } + case Type::kCooperativeMatrixKHR: { + const CooperativeMatrixKHR* cm_type = type.AsCooperativeMatrixKHR(); + const Type* component_type = cm_type->component_type(); + rebuilt_ty = MakeUnique<CooperativeMatrixKHR>( + RebuildType(GetId(component_type), *component_type), + cm_type->scope_id(), cm_type->rows_id(), cm_type->columns_id(), + cm_type->use_id()); break; } default: @@ -644,7 +685,7 @@ void TypeManager::RegisterType(uint32_t id, const Type& type) { // Rebuild |type| so it and all its constituent types are owned by the type // pool. - Type* rebuilt = RebuildType(type); + Type* rebuilt = RebuildType(id, type); assert(rebuilt->IsSame(&type)); id_to_type_[id] = rebuilt; if (GetId(rebuilt) == 0) { @@ -863,6 +904,12 @@ inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3)); break; + case spv::Op::OpTypeCooperativeMatrixKHR: + type = new CooperativeMatrixKHR( + GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1), inst.GetSingleWordInOperand(2), + inst.GetSingleWordInOperand(3), inst.GetSingleWordInOperand(4)); + break; case spv::Op::OpTypeRayQueryKHR: type = new RayQueryKHR(); break; @@ -870,7 +917,7 @@ type = new HitObjectNV(); break; default: - SPIRV_UNIMPLEMENTED(consumer_, "unhandled type"); + assert(false && "Type not handled by the type manager."); break; } @@ -912,12 +959,10 @@ } if (Struct* st = type->AsStruct()) { st->AddMemberDecoration(index, std::move(data)); - } else { - SPIRV_UNIMPLEMENTED(consumer_, "OpMemberDecorate non-struct type"); } } break; default: - SPIRV_UNREACHABLE(consumer_); + assert(false && "Unexpected opcode for a decoration instruction."); break; } }
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h index c49e193..948b691 100644 --- a/source/opt/type_manager.h +++ b/source/opt/type_manager.h
@@ -144,18 +144,17 @@ // |type| (e.g. should be called in loop of |type|'s decorations). void AttachDecoration(const Instruction& inst, Type* type); - Type* GetUIntType() { - Integer int_type(32, false); - return GetRegisteredType(&int_type); - } + Type* GetUIntType() { return GetIntType(32, false); } uint32_t GetUIntTypeId() { return GetTypeInstruction(GetUIntType()); } - Type* GetSIntType() { - Integer int_type(32, true); + Type* GetIntType(int32_t bitWidth, bool isSigned) { + Integer int_type(bitWidth, isSigned); return GetRegisteredType(&int_type); } + Type* GetSIntType() { return GetIntType(32, true); } + uint32_t GetSIntTypeId() { return GetTypeInstruction(GetSIntType()); } Type* GetFloatType() { @@ -261,7 +260,9 @@ // Returns an equivalent pointer to |type| built in terms of pointers owned by // |type_pool_|. For example, if |type| is a vec3 of bool, it will be rebuilt // replacing the bool subtype with one owned by |type_pool_|. - Type* RebuildType(const Type& type); + // + // The re-built type will have ID |type_id|. + Type* RebuildType(uint32_t type_id, const Type& type); // Completes the incomplete type |type|, by replaces all references to // ForwardPointer by the defining Pointer.
diff --git a/source/opt/types.cpp b/source/opt/types.cpp index 49eec9b..b18b8cb 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp
@@ -128,6 +128,7 @@ DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(CooperativeMatrixKHR); DeclareKindCase(RayQueryKHR); DeclareKindCase(HitObjectNV); #undef DeclareKindCase @@ -175,6 +176,7 @@ DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(CooperativeMatrixKHR); DeclareKindCase(RayQueryKHR); DeclareKindCase(HitObjectNV); #undef DeclareKindCase @@ -230,6 +232,7 @@ DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(CooperativeMatrixKHR); DeclareKindCase(RayQueryKHR); DeclareKindCase(HitObjectNV); #undef DeclareKindCase @@ -708,6 +711,45 @@ columns_id_ == mt->columns_id_ && HasSameDecorations(that); } +CooperativeMatrixKHR::CooperativeMatrixKHR(const Type* type, + const uint32_t scope, + const uint32_t rows, + const uint32_t columns, + const uint32_t use) + : Type(kCooperativeMatrixKHR), + component_type_(type), + scope_id_(scope), + rows_id_(rows), + columns_id_(columns), + use_id_(use) { + assert(type != nullptr); + assert(scope != 0); + assert(rows != 0); + assert(columns != 0); +} + +std::string CooperativeMatrixKHR::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_ + << ", " << columns_id_ << ", " << use_id_ << ">"; + return oss.str(); +} + +size_t CooperativeMatrixKHR::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, scope_id_, rows_id_, columns_id_, use_id_); + return component_type_->ComputeHashValue(hash, seen); +} + +bool CooperativeMatrixKHR::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeMatrixKHR* mt = that->AsCooperativeMatrixKHR(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ && + columns_id_ == mt->columns_id_ && HasSameDecorations(that); +} + } // namespace analysis } // namespace opt } // namespace spvtools
diff --git a/source/opt/types.h b/source/opt/types.h index 26c058c..16a948c 100644 --- a/source/opt/types.h +++ b/source/opt/types.h
@@ -60,6 +60,7 @@ class NamedBarrier; class AccelerationStructureNV; class CooperativeMatrixNV; +class CooperativeMatrixKHR; class RayQueryKHR; class HitObjectNV; @@ -100,6 +101,7 @@ kNamedBarrier, kAccelerationStructureNV, kCooperativeMatrixNV, + kCooperativeMatrixKHR, kRayQueryKHR, kHitObjectNV, kLast @@ -201,6 +203,7 @@ DeclareCastMethod(NamedBarrier) DeclareCastMethod(AccelerationStructureNV) DeclareCastMethod(CooperativeMatrixNV) + DeclareCastMethod(CooperativeMatrixKHR) DeclareCastMethod(RayQueryKHR) DeclareCastMethod(HitObjectNV) #undef DeclareCastMethod @@ -624,6 +627,38 @@ const uint32_t columns_id_; }; +class CooperativeMatrixKHR : public Type { + public: + CooperativeMatrixKHR(const Type* type, const uint32_t scope, + const uint32_t rows, const uint32_t columns, + const uint32_t use); + CooperativeMatrixKHR(const CooperativeMatrixKHR&) = default; + + std::string str() const override; + + CooperativeMatrixKHR* AsCooperativeMatrixKHR() override { return this; } + const CooperativeMatrixKHR* AsCooperativeMatrixKHR() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + const Type* component_type() const { return component_type_; } + uint32_t scope_id() const { return scope_id_; } + uint32_t rows_id() const { return rows_id_; } + uint32_t columns_id() const { return columns_id_; } + uint32_t use_id() const { return use_id_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t scope_id_; + const uint32_t rows_id_; + const uint32_t columns_id_; + const uint32_t use_id_; +}; + #define DefineParameterlessType(type, name) \ class type : public Type { \ public: \
diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp index 5f8e94d..cc33f8b 100644 --- a/source/parsed_operand.cpp +++ b/source/parsed_operand.cpp
@@ -24,6 +24,7 @@ void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, const spv_parsed_operand_t& operand) { if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER && + operand.type != SPV_OPERAND_TYPE_LITERAL_FLOAT && operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER && operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER && operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER)
diff --git a/source/print.cpp b/source/print.cpp index 6c94e2b..f36812e 100644 --- a/source/print.cpp +++ b/source/print.cpp
@@ -17,7 +17,7 @@ #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ defined(SPIRV_IOS) || defined(SPIRV_TVOS) || defined(SPIRV_FREEBSD) || \ defined(SPIRV_OPENBSD) || defined(SPIRV_EMSCRIPTEN) || \ - defined(SPIRV_FUCHSIA) || defined(SPIRV_GNU) + defined(SPIRV_FUCHSIA) || defined(SPIRV_GNU) || defined(SPIRV_QNX) namespace spvtools { clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/source/table.h b/source/table.h index 8097f13..4f1dc1f 100644 --- a/source/table.h +++ b/source/table.h
@@ -74,7 +74,7 @@ const uint32_t ext_inst; const uint32_t numCapabilities; const spv::Capability* capabilities; - const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? + const spv_operand_type_t operandTypes[40]; // vksp needs at least 40 } spv_ext_inst_desc_t; typedef struct spv_ext_inst_group_t {
diff --git a/source/text.cpp b/source/text.cpp index 8f77d62..263bacd 100644 --- a/source/text.cpp +++ b/source/text.cpp
@@ -312,6 +312,17 @@ } } break; + case SPV_OPERAND_TYPE_LITERAL_FLOAT: { + // The current operand is a 32-bit float. + // That's just how the grammar works. + spvtools::IdType expected_type = { + 32, false, spvtools::IdTypeClass::kScalarFloatType}; + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, expected_type, pInst)) { + return error; + } + } break; + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: // This is a context-independent literal number which can be a 32-bit // number of floating point value. @@ -400,9 +411,11 @@ case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: case SPV_OPERAND_TYPE_SELECTION_CONTROL: case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: - case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: { + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: { uint32_t value; if (auto error = grammar.parseMaskOperand(type, textValue, &value)) { return context->diagnostic(error) @@ -544,7 +557,8 @@ std::string equal_sign; error = context->getWord(&equal_sign, &nextPosition); if ("=" != equal_sign) - return context->diagnostic() << "'=' expected after result id."; + return context->diagnostic() << "'=' expected after result id but found '" + << equal_sign << "'."; // The <opcode> after the '=' sign. context->setPosition(nextPosition);
diff --git a/source/val/validate.cpp b/source/val/validate.cpp index e73e466..3236807 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp
@@ -141,6 +141,13 @@ } } + if (auto error = ValidateFloatControls2(_)) { + return error; + } + if (auto error = ValidateDuplicateExecutionModes(_)) { + return error; + } + return SPV_SUCCESS; } @@ -381,6 +388,8 @@ for (const auto& inst : vstate->ordered_instructions()) { if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error; if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error; + if (auto error = ValidateQCOMImageProcessingTextureUsages(*vstate, &inst)) + return error; } return SPV_SUCCESS;
diff --git a/source/val/validate.h b/source/val/validate.h index 2cd229f..78093ce 100644 --- a/source/val/validate.h +++ b/source/val/validate.h
@@ -82,6 +82,25 @@ /// @return SPV_SUCCESS if no errors are found. spv_result_t ValidateInterfaces(ValidationState_t& _); +/// @brief Validates entry point call tree requirements of +/// SPV_KHR_float_controls2 +/// +/// Checks that no entry point using FPFastMathDefault uses: +/// * FPFastMathMode Fast +/// * NoContraction +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t ValidateFloatControls2(ValidationState_t& _); + +/// @brief Validates duplicated execution modes for each entry point. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _); + /// @brief Validates memory instructions /// /// @param[in] _ the validation state of the module @@ -220,6 +239,14 @@ spv_result_t ValidateSmallTypeUses(ValidationState_t& _, const Instruction* inst); +/// Validates restricted uses of QCOM decorated textures +/// +/// The textures that are decorated with some of QCOM image processing +/// decorations must be used in the specified QCOM image processing built-in +/// functions and not used in any other image functions. +spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _, + const Instruction* inst); + /// @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 73d0285..dac3585 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp
@@ -161,7 +161,8 @@ case spv::Decoration::RestrictPointer: case spv::Decoration::AliasedPointer: if (target->opcode() != spv::Op::OpVariable && - target->opcode() != spv::Op::OpFunctionParameter) { + target->opcode() != spv::Op::OpFunctionParameter && + target->opcode() != spv::Op::OpRawAccessChainNV) { return fail(0) << "must be a memory object declaration"; } if (_.GetIdOpcode(target->type_id()) != spv::Op::OpTypePointer) { @@ -267,6 +268,34 @@ } } + if (decoration == spv::Decoration::FPFastMathMode) { + if (_.HasDecoration(target_id, spv::Decoration::NoContraction)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "FPFastMathMode and NoContraction cannot decorate the same " + "target"; + } + auto mask = inst->GetOperandAs<spv::FPFastMathModeMask>(2); + if ((mask & spv::FPFastMathModeMask::AllowTransform) != + spv::FPFastMathModeMask::MaskNone && + ((mask & (spv::FPFastMathModeMask::AllowContract | + spv::FPFastMathModeMask::AllowReassoc)) != + (spv::FPFastMathModeMask::AllowContract | + spv::FPFastMathModeMask::AllowReassoc))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "AllowReassoc and AllowContract must be specified when " + "AllowTransform is specified"; + } + } + + // This is checked from both sides since we register decorations as we go. + if (decoration == spv::Decoration::NoContraction) { + if (_.HasDecoration(target_id, spv::Decoration::FPFastMathMode)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "FPFastMathMode and NoContraction cannot decorate the same " + "target"; + } + } + if (DecorationTakesIdParameters(decoration)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Decorations taking ID parameters may not be used with "
diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp index 4e7dd5e..b608a85 100644 --- a/source/val/validate_arithmetics.cpp +++ b/source/val/validate_arithmetics.cpp
@@ -42,14 +42,29 @@ opcode != spv::Op::OpFMod); if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && - !(supportsCoopMat && _.IsFloatCooperativeMatrixType(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 (_.GetOperandTypeId(inst, operand_index) != result_type) + 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, type_id, result_type); + 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 " @@ -71,7 +86,19 @@ for (size_t operand_index = 2; operand_index < inst->operands().size(); ++operand_index) { - if (_.GetOperandTypeId(inst, operand_index) != result_type) + 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, type_id, result_type); + 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 " @@ -91,7 +118,10 @@ (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem && opcode != spv::Op::OpSMod); if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && - !(supportsCoopMat && _.IsIntCooperativeMatrixType(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); @@ -102,9 +132,26 @@ 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, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } + if (!type_id || (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && - !(supportsCoopMat && _.IsIntCooperativeMatrixType(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 operand: " << spvOpcodeString(opcode) << " operand index " @@ -187,7 +234,7 @@ case spv::Op::OpMatrixTimesScalar: { if (!_.IsFloatMatrixType(result_type) && - !_.IsCooperativeMatrixType(result_type)) + !(_.IsCooperativeMatrixType(result_type))) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected float matrix type as Result Type: " << spvOpcodeString(opcode); @@ -459,22 +506,108 @@ const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); - if (!_.IsCooperativeMatrixType(A_type_id)) { + if (!_.IsCooperativeMatrixNVType(A_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as A Type: " << spvOpcodeString(opcode); } - if (!_.IsCooperativeMatrixType(B_type_id)) { + if (!_.IsCooperativeMatrixNVType(B_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as B Type: " << spvOpcodeString(opcode); } - if (!_.IsCooperativeMatrixType(C_type_id)) { + if (!_.IsCooperativeMatrixNVType(C_type_id)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix type as C Type: " << spvOpcodeString(opcode); } - if (!_.IsCooperativeMatrixType(D_type_id)) { + 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); + } + 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);
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index b745a9e..8ddef17 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp
@@ -144,12 +144,13 @@ case spv::Op::OpAtomicFlagClear: { const uint32_t result_type = inst->type_id(); - // All current atomics only are scalar result // Validate return type first so can just check if pointer type is same // (if applicable) if (HasReturnType(opcode)) { if (HasOnlyFloatReturnType(opcode) && - !_.IsFloatScalarType(result_type)) { + (!(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(result_type)) && + !_.IsFloatScalarType(result_type))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) << ": expected Result Type to be float scalar type"; @@ -160,6 +161,9 @@ << ": expected Result Type to be integer scalar type"; } else if (HasIntOrFloatReturnType(opcode) && !_.IsFloatScalarType(result_type) && + !(opcode == spv::Op::OpAtomicExchange && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(result_type)) && !_.IsIntScalarType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) @@ -222,12 +226,21 @@ if (opcode == spv::Op::OpAtomicFAddEXT) { // result type being float checked already - if ((_.GetBitWidth(result_type) == 16) && - (!_.HasCapability(spv::Capability::AtomicFloat16AddEXT))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": float add atomics require the AtomicFloat32AddEXT " - "capability"; + if (_.GetBitWidth(result_type) == 16) { + if (_.IsFloat16Vector2Or4Type(result_type)) { + if (!_.HasCapability(spv::Capability::AtomicFloat16VectorNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float vector atomics require the " + "AtomicFloat16VectorNV capability"; + } else { + if (!_.HasCapability(spv::Capability::AtomicFloat16AddEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } + } } if ((_.GetBitWidth(result_type) == 32) && (!_.HasCapability(spv::Capability::AtomicFloat32AddEXT))) { @@ -245,12 +258,21 @@ } } else if (opcode == spv::Op::OpAtomicFMinEXT || opcode == spv::Op::OpAtomicFMaxEXT) { - if ((_.GetBitWidth(result_type) == 16) && - (!_.HasCapability(spv::Capability::AtomicFloat16MinMaxEXT))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": float min/max atomics require the " - "AtomicFloat16MinMaxEXT capability"; + if (_.GetBitWidth(result_type) == 16) { + if (_.IsFloat16Vector2Or4Type(result_type)) { + if (!_.HasCapability(spv::Capability::AtomicFloat16VectorNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float vector atomics require the " + "AtomicFloat16VectorNV capability"; + } else { + if (!_.HasCapability(spv::Capability::AtomicFloat16MinMaxEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat16MinMaxEXT capability"; + } + } } if ((_.GetBitWidth(result_type) == 32) && (!_.HasCapability(spv::Capability::AtomicFloat32MinMaxEXT))) {
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 3e81712..a7e9942 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp
@@ -118,13 +118,15 @@ VUIDErrorMax, } VUIDError; -const static uint32_t NumVUIDBuiltins = 36; +const static uint32_t NumVUIDBuiltins = 39; typedef struct { spv::BuiltIn builtIn; uint32_t vuid[VUIDErrorMax]; // execution mode, storage class, type VUIDs } BuiltinVUIDMapping; +// Many built-ins have the same checks (Storage Class, Type, etc) +// This table provides a nice LUT for the VUIDs std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{ // clang-format off {spv::BuiltIn::SubgroupEqMask, {0, 4370, 4371}}, @@ -163,8 +165,11 @@ {spv::BuiltIn::CullMaskKHR, {6735, 6736, 6737}}, {spv::BuiltIn::BaryCoordKHR, {4154, 4155, 4156}}, {spv::BuiltIn::BaryCoordNoPerspKHR, {4160, 4161, 4162}}, - // clang-format off -} }; + {spv::BuiltIn::PrimitivePointIndicesEXT, {7041, 7043, 7044}}, + {spv::BuiltIn::PrimitiveLineIndicesEXT, {7047, 7049, 7050}}, + {spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}}, + // clang-format on +}}; uint32_t GetVUIDForBuiltin(spv::BuiltIn builtIn, VUIDError type) { uint32_t vuid = 0; @@ -356,6 +361,9 @@ spv_result_t ValidateRayTracingBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateMeshShadingEXTBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst); + // The following section contains functions which are called when id defined // by |referenced_inst| is // 1. referenced by |referenced_from_inst| @@ -546,6 +554,11 @@ const Instruction& referenced_inst, const Instruction& referenced_from_inst); + spv_result_t ValidateMeshShadingEXTBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + // Validates that |built_in_inst| is not (even indirectly) referenced from // within a function which can be called with |execution_model|. // @@ -581,6 +594,10 @@ spv_result_t ValidateI32Arr( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag); + spv_result_t ValidateArrayedI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function<spv_result_t(const std::string& message)>& diag); spv_result_t ValidateOptionalArrayedI32( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag); @@ -909,6 +926,45 @@ return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateArrayedI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + 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; + } + + const Instruction* const type_inst = _.FindDef(underlying_type); + if (type_inst->opcode() != spv::Op::OpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsIntVectorType(component_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector."); + } + + const uint32_t actual_num_components = _.GetDimension(component_type); + if (_.GetDimension(component_type) != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + + const uint32_t bit_width = _.GetBitWidth(component_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec( const Decoration& decoration, const Instruction& inst, uint32_t num_components, @@ -1064,7 +1120,7 @@ if (num_components != 0) { uint64_t actual_num_components = 0; - if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) { + if (!_.EvalConstantValUint64(type_inst->word(3), &actual_num_components)) { assert(0 && "Array type definition is corrupt"); } if (actual_num_components != num_components) { @@ -4108,6 +4164,119 @@ return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]); + 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, decoration.params()[0]) + << " variable needs to be a 32-bit int array." + << message; + })) { + return error; + } + } + 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, decoration.params()[0]) + << " 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, decoration.params()[0]) + << " variable needs to be a 3-component 32-bit int " + "array." + << message; + })) { + return error; + } + } + } + // Seed at reference checks with this built-in. + return ValidateMeshShadingEXTBuiltinsAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]); + const spv::StorageClass storage_class = + GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Output) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " to be only used for variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + 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); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " to be used only with MeshEXT 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( + std::bind(&BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( const Decoration& decoration, const Instruction& inst) { const spv::BuiltIn label = spv::BuiltIn(decoration.params()[0]); @@ -4283,6 +4452,11 @@ case spv::BuiltIn::CullMaskKHR: { return ValidateRayTracingBuiltinsAtDefinition(decoration, inst); } + case spv::BuiltIn::PrimitivePointIndicesEXT: + case spv::BuiltIn::PrimitiveLineIndicesEXT: + case spv::BuiltIn::PrimitiveTriangleIndicesEXT: { + return ValidateMeshShadingEXTBuiltinsAtDefinition(decoration, inst); + } case spv::BuiltIn::PrimitiveShadingRateKHR: { return ValidatePrimitiveShadingRateAtDefinition(decoration, inst); }
diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp index 98dab42..81d2ad5 100644 --- a/source/val/validate_capability.cpp +++ b/source/val/validate_capability.cpp
@@ -240,7 +240,7 @@ ExtensionSet operand_exts(operand_desc->numExtensions, operand_desc->extensions); - if (operand_exts.IsEmpty()) return false; + if (operand_exts.empty()) return false; return _.HasAnyOfExtensions(operand_exts); }
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 7b3f748..9b7161f 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp
@@ -190,6 +190,8 @@ "ID of an OpLabel instruction"; } + // A similar requirement for SPV_KHR_maximal_reconvergence is deferred until + // entry point call trees have been reconrded. if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && true_id == false_id) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "In SPIR-V 1.6 or later, True Label and False Label must be " @@ -875,6 +877,95 @@ return SPV_SUCCESS; } +spv_result_t MaximalReconvergenceChecks(ValidationState_t& _) { + // Find all the entry points with the MaximallyReconvergencesKHR execution + // mode. + std::unordered_set<uint32_t> maximal_funcs; + std::unordered_set<uint32_t> maximal_entry_points; + for (auto entry_point : _.entry_points()) { + const auto* exec_modes = _.GetExecutionModes(entry_point); + if (exec_modes && + exec_modes->count(spv::ExecutionMode::MaximallyReconvergesKHR)) { + maximal_entry_points.insert(entry_point); + maximal_funcs.insert(entry_point); + } + } + + if (maximal_entry_points.empty()) { + return SPV_SUCCESS; + } + + // Find all the functions reachable from a maximal reconvergence entry point. + for (const auto& func : _.functions()) { + const auto& entry_points = _.EntryPointReferences(func.id()); + for (auto id : entry_points) { + if (maximal_entry_points.count(id)) { + maximal_funcs.insert(func.id()); + break; + } + } + } + + // Check for conditional branches with the same true and false targets. + for (const auto& inst : _.ordered_instructions()) { + if (inst.opcode() == spv::Op::OpBranchConditional) { + const auto true_id = inst.GetOperandAs<uint32_t>(1); + const auto false_id = inst.GetOperandAs<uint32_t>(2); + if (true_id == false_id && maximal_funcs.count(inst.function()->id())) { + return _.diag(SPV_ERROR_INVALID_ID, &inst) + << "In entry points using the MaximallyReconvergesKHR execution " + "mode, True Label and False Label must be different labels"; + } + } + } + + // Check for invalid multiple predecessors. Only loop headers, continue + // targets, merge targets or switch targets or defaults may have multiple + // unique predecessors. + for (const auto& func : _.functions()) { + if (!maximal_funcs.count(func.id())) continue; + + for (const auto* block : func.ordered_blocks()) { + std::unordered_set<uint32_t> unique_preds; + const auto* preds = block->predecessors(); + if (!preds) continue; + + for (const auto* pred : *preds) { + unique_preds.insert(pred->id()); + } + if (unique_preds.size() < 2) continue; + + const auto* terminator = block->terminator(); + const auto index = terminator - &_.ordered_instructions()[0]; + const auto* pre_terminator = &_.ordered_instructions()[index - 1]; + if (pre_terminator->opcode() == spv::Op::OpLoopMerge) continue; + + const auto* label = _.FindDef(block->id()); + bool ok = false; + for (const auto& pair : label->uses()) { + const auto* use_inst = pair.first; + switch (use_inst->opcode()) { + case spv::Op::OpSelectionMerge: + case spv::Op::OpLoopMerge: + case spv::Op::OpSwitch: + ok = true; + break; + default: + break; + } + } + if (!ok) { + return _.diag(SPV_ERROR_INVALID_CFG, label) + << "In entry points using the MaximallyReconvergesKHR " + "execution mode, this basic block must not have multiple " + "unique predecessors"; + } + } + } + + return SPV_SUCCESS; +} + spv_result_t PerformCfgChecks(ValidationState_t& _) { for (auto& function : _.functions()) { // Check all referenced blocks are defined within a function @@ -999,6 +1090,11 @@ return error; } } + + if (auto error = MaximalReconvergenceChecks(_)) { + return error; + } + return SPV_SUCCESS; }
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp index 2b83c63..26486da 100644 --- a/source/val/validate_composites.cpp +++ b/source/val/validate_composites.cpp
@@ -94,7 +94,7 @@ break; } - if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) { + if (!_.EvalConstantValUint64(type_inst->word(3), &array_size)) { assert(0 && "Array type definition is corrupt"); } if (component_index >= array_size) { @@ -122,6 +122,7 @@ *member_type = type_inst->word(component_index + 2); break; } + case spv::Op::OpTypeCooperativeMatrixKHR: case spv::Op::OpTypeCooperativeMatrixNV: { *member_type = type_inst->word(2); break; @@ -288,7 +289,7 @@ } uint64_t array_size = 0; - if (!_.GetConstantValUint64(array_inst->word(3), &array_size)) { + if (!_.EvalConstantValUint64(array_inst->word(3), &array_size)) { assert(0 && "Array type definition is corrupt"); } @@ -335,6 +336,25 @@ break; } + case spv::Op::OpTypeCooperativeMatrixKHR: { + const auto result_type_inst = _.FindDef(result_type); + assert(result_type_inst); + const auto component_type_id = + result_type_inst->GetOperandAs<uint32_t>(1); + + if (3 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Must be only one constituent"; + } + + const uint32_t operand_type_id = _.GetOperandTypeId(inst, 2); + + if (operand_type_id != component_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the component type"; + } + break; + } case spv::Op::OpTypeCooperativeMatrixNV: { const auto result_type_inst = _.FindDef(result_type); assert(result_type_inst);
diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp index 006e504..4deaa49 100644 --- a/source/val/validate_constants.cpp +++ b/source/val/validate_constants.cpp
@@ -243,6 +243,7 @@ } } } break; + case spv::Op::OpTypeCooperativeMatrixKHR: case spv::Op::OpTypeCooperativeMatrixNV: { if (1 != constituent_count) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -310,6 +311,7 @@ case spv::Op::OpTypeArray: case spv::Op::OpTypeMatrix: case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: case spv::Op::OpTypeVector: { auto base_type = _.FindDef(instruction[2]); return base_type && IsTypeNullable(base_type->words(), _);
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp index 476c1fe..b2892a8 100644 --- a/source/val/validate_conversion.cpp +++ b/source/val/validate_conversion.cpp
@@ -473,7 +473,10 @@ const bool input_is_pointer = _.IsPointerType(input_type); const bool input_is_int_scalar = _.IsIntScalarType(input_type); - if (!result_is_pointer && !result_is_int_scalar && + 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)) @@ -481,13 +484,24 @@ << "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 && + 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); + 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);
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index c1fca45..0a7df65 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp
@@ -452,7 +452,16 @@ return ds; }; - const auto& members = getStructMembers(struct_id, vstate); + // If we are checking 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. + const auto& struct_type = vstate.FindDef(struct_id); + std::vector<uint32_t> members; + if (struct_type->opcode() == spv::Op::OpTypeStruct) { + members = getStructMembers(struct_id, vstate); + } else { + members.push_back(struct_id); + } // To check for member overlaps, we want to traverse the members in // offset order. @@ -461,31 +470,38 @@ uint32_t offset; }; std::vector<MemberOffsetPair> member_offsets; - member_offsets.reserve(members.size()); - for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); - memberIdx < numMembers; memberIdx++) { - uint32_t offset = 0xffffffff; - auto member_decorations = - vstate.id_member_decorations(struct_id, memberIdx); - for (auto decoration = member_decorations.begin; - decoration != member_decorations.end; ++decoration) { - assert(decoration->struct_member_index() == (int)memberIdx); - switch (decoration->dec_type()) { - case spv::Decoration::Offset: - offset = decoration->params()[0]; - break; - default: - break; + + // With physical storage buffers, we might be checking layouts that do not + // originate from a structure. + if (struct_type->opcode() == spv::Op::OpTypeStruct) { + member_offsets.reserve(members.size()); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; memberIdx++) { + uint32_t offset = 0xffffffff; + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case spv::Decoration::Offset: + offset = decoration->params()[0]; + break; + default: + break; + } } + member_offsets.push_back( + MemberOffsetPair{memberIdx, incoming_offset + offset}); } - member_offsets.push_back( - MemberOffsetPair{memberIdx, incoming_offset + offset}); + std::stable_sort( + member_offsets.begin(), member_offsets.end(), + [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) { + return lhs.offset < rhs.offset; + }); + } else { + member_offsets.push_back({0, 0}); } - std::stable_sort( - member_offsets.begin(), member_offsets.end(), - [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) { - return lhs.offset < rhs.offset; - }); // Now scan from lowest offset to highest offset. uint32_t nextValidOffset = 0; @@ -906,9 +922,9 @@ } } - if (vstate.HasCapability( - spv::Capability::WorkgroupMemoryExplicitLayoutKHR) && - num_workgroup_variables > 0 && + const bool workgroup_blocks_allowed = vstate.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayoutKHR); + if (workgroup_blocks_allowed && num_workgroup_variables > 0 && num_workgroup_variables_with_block > 0) { if (num_workgroup_variables != num_workgroup_variables_with_block) { return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) @@ -929,6 +945,13 @@ "Entry point id " << entry_point << " does not meet this requirement."; } + } else if (!workgroup_blocks_allowed && + num_workgroup_variables_with_block > 0) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "Workgroup Storage Class variables can't be decorated with " + "Block unless declaring the WorkgroupMemoryExplicitLayoutKHR " + "capability."; } } } @@ -1023,6 +1046,8 @@ std::unordered_set<uint32_t> uses_push_constant; for (const auto& inst : vstate.ordered_instructions()) { const auto& words = inst.words(); + auto type_id = inst.type_id(); + const Instruction* type_inst = vstate.FindDef(type_id); if (spv::Op::OpVariable == inst.opcode()) { const auto var_id = inst.id(); // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset @@ -1276,6 +1301,23 @@ } } } + } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer && + type_inst->GetOperandAs<spv::StorageClass>(1u) == + spv::StorageClass::PhysicalStorageBuffer) { + const bool scalar_block_layout = vstate.options()->scalar_block_layout; + MemberConstraints constraints; + const bool buffer = true; + const auto data_type_id = type_inst->GetOperandAs<uint32_t>(2u); + const auto* data_type_inst = vstate.FindDef(data_type_id); + if (data_type_inst->opcode() == spv::Op::OpTypeStruct) { + ComputeMemberConstraintsForStruct(&constraints, data_type_id, + LayoutConstraints(), vstate); + } + if (auto res = checkLayout(data_type_id, "PhysicalStorageBuffer", "Block", + !buffer, scalar_block_layout, 0, constraints, + vstate)) { + return res; + } } } return SPV_SUCCESS; @@ -1283,21 +1325,14 @@ // Returns true if |decoration| cannot be applied to the same id more than once. bool AtMostOncePerId(spv::Decoration decoration) { - return decoration == spv::Decoration::ArrayStride; + return decoration != spv::Decoration::UserSemantic && + decoration != spv::Decoration::FuncParamAttr; } // Returns true if |decoration| cannot be applied to the same member more than // once. bool AtMostOncePerMember(spv::Decoration decoration) { - switch (decoration) { - case spv::Decoration::Offset: - case spv::Decoration::MatrixStride: - case spv::Decoration::RowMajor: - case spv::Decoration::ColMajor: - return true; - default: - return false; - } + return decoration != spv::Decoration::UserSemantic; } spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { @@ -1514,7 +1549,8 @@ const auto opcode = inst.opcode(); const auto type_id = inst.type_id(); if (opcode != spv::Op::OpVariable && - opcode != spv::Op::OpFunctionParameter) { + 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 " "declaration (a variable or a function parameter)"; @@ -1527,10 +1563,11 @@ vstate.features().nonwritable_var_in_function_or_private) { // New permitted feature in SPIR-V 1.4. } else if ( - // It may point to a UBO, SSBO, or storage image. + // 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.IsPointerToStorageImage(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 "
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index 0ac62bf..7b73c9c 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp
@@ -482,8 +482,8 @@ return SPV_SUCCESS; } -spv_result_t ValidateClspvReflectionArgumentOffsetBuffer(ValidationState_t& _, - const Instruction* inst) { +spv_result_t ValidateClspvReflectionArgumentOffsetBuffer( + ValidationState_t& _, const Instruction* inst) { const auto num_operands = inst->operands().size(); if (auto error = ValidateKernelDecl(_, inst)) { return error; @@ -802,7 +802,7 @@ } spv_result_t ValidateClspvReflectionPrintfInfo(ValidationState_t& _, - const Instruction* inst) { + const Instruction* inst) { if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "PrintfID must be a 32-bit unsigned integer OpConstant"; @@ -823,8 +823,8 @@ return SPV_SUCCESS; } -spv_result_t ValidateClspvReflectionPrintfStorageBuffer(ValidationState_t& _, - const Instruction* inst) { +spv_result_t ValidateClspvReflectionPrintfStorageBuffer( + ValidationState_t& _, const Instruction* inst) { if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; @@ -843,8 +843,8 @@ return SPV_SUCCESS; } -spv_result_t ValidateClspvReflectionPrintfPushConstant(ValidationState_t& _, - const Instruction* inst) { +spv_result_t ValidateClspvReflectionPrintfPushConstant( + ValidationState_t& _, const Instruction* inst) { if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Offset must be a 32-bit unsigned integer OpConstant"; @@ -3100,7 +3100,7 @@ uint32_t vector_count = inst->word(6); uint64_t const_val; - if (!_.GetConstantValUint64(vector_count, &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"; @@ -3168,16 +3168,16 @@ break; } case CommonDebugInfoDebugTypePointer: { - auto validate_base_type = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); + 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 = - ValidateOperandBaseType(_, inst, 5, ext_inst_name); + 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; @@ -3191,7 +3191,7 @@ uint32_t component_count = inst->word(6); if (vulkanDebugInfo) { uint64_t const_val; - if (!_.GetConstantValUint64(component_count, &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";
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp index 92a4e8e..bcfeb59 100644 --- a/source/val/validate_id.cpp +++ b/source/val/validate_id.cpp
@@ -163,9 +163,12 @@ !inst->IsDebugInfo() && !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction && opcode != spv::Op::OpCooperativeMatrixLengthNV && + opcode != spv::Op::OpCooperativeMatrixLengthKHR && !(opcode == spv::Op::OpSpecConstantOp && - spv::Op(inst->word(3)) == - spv::Op::OpCooperativeMatrixLengthNV)) { + (spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthNV || + spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthKHR))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Operand " << _.getIdName(operand_word) << " cannot be a type"; @@ -179,9 +182,12 @@ opcode != spv::Op::OpLoopMerge && opcode != spv::Op::OpFunction && opcode != spv::Op::OpCooperativeMatrixLengthNV && + opcode != spv::Op::OpCooperativeMatrixLengthKHR && !(opcode == spv::Op::OpSpecConstantOp && - spv::Op(inst->word(3)) == - spv::Op::OpCooperativeMatrixLengthNV)) { + (spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthNV || + spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthKHR))) { 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 733556b..a1a76ea 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp
@@ -1,4 +1,4 @@ -// Copyright (c) 2017 Google Inc. +// Copyright (c) 2017 Google Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights // reserved. // @@ -297,7 +297,6 @@ spv::ImageOperandsMask::ConstOffsets | spv::ImageOperandsMask::Offsets)) > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4662) << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets " "cannot be used together"; } @@ -496,7 +495,7 @@ } uint64_t array_size = 0; - if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) { + if (!_.EvalConstantValUint64(type_inst->word(3), &array_size)) { assert(0 && "Array type definition is corrupt"); } @@ -694,16 +693,11 @@ << "storage image"; } - if (info.multisampled == 1 && + if (info.multisampled == 1 && info.arrayed == 1 && info.sampled == 2 && !_.HasCapability(spv::Capability::ImageMSArray)) { -#if 0 - // TODO(atgoo@github.com) The description of this rule in the spec - // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify - // and reenable. return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageMSArray is required to access storage " - << "image"; -#endif + << "Capability ImageMSArray is required to access storage " + << "image"; } } else if (info.sampled != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -920,7 +914,15 @@ if (info.dim == spv::Dim::SubpassData && info.arrayed != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0"; + << _.VkErrorID(6214) + << "Dim SubpassData requires Arrayed to be 0 in the Vulkan " + "environment"; + } + + if (info.dim == spv::Dim::Rect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(9638) + << "Dim must not be Rect in the Vulkan environment"; } } @@ -984,6 +986,14 @@ case spv::Op::OpImageSparseGather: case spv::Op::OpImageSparseDrefGather: case spv::Op::OpCopyObject: + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: return true; case spv::Op::OpStore: if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true; @@ -1087,6 +1097,18 @@ } } } + + const Instruction* ld_inst; + { + int t_idx = inst->GetOperandAs<int>(2); + ld_inst = _.FindDef(t_idx); + } + + if (ld_inst->opcode() == spv::Op::OpLoad) { + int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load + _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst); + } + return SPV_SUCCESS; } @@ -1108,7 +1130,10 @@ const auto ptr_type = result_type->GetOperandAs<uint32_t>(2); const auto ptr_opcode = _.GetIdOpcode(ptr_type); if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat && - ptr_opcode != spv::Op::OpTypeVoid) { + ptr_opcode != spv::Op::OpTypeVoid && + !(ptr_opcode == spv::Op::OpTypeVector && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(ptr_type))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Result Type to be OpTypePointer whose Type operand " "must be a scalar numerical type or OpTypeVoid"; @@ -1132,7 +1157,14 @@ << "Corrupt image type definition"; } - if (info.sampled_type != ptr_type) { + if (info.sampled_type != ptr_type && + !(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(ptr_type) && + _.GetIdOpcode(info.sampled_type) == spv::Op::OpTypeFloat && + ((_.GetDimension(ptr_type) == 2 && + info.format == spv::ImageFormat::Rg16f) || + (_.GetDimension(ptr_type) == 4 && + info.format == spv::ImageFormat::Rgba16f)))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Image 'Sampled Type' to be the same as the Type " "pointed to by Result Type"; @@ -1190,7 +1222,7 @@ if (info.multisampled == 0) { uint64_t ms = 0; - if (!_.GetConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) || + if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) || ms != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Sample for Image with MS 0 to be a valid <id> for " @@ -1203,7 +1235,10 @@ (info.format != spv::ImageFormat::R64ui) && (info.format != spv::ImageFormat::R32f) && (info.format != spv::ImageFormat::R32i) && - (info.format != spv::ImageFormat::R32ui)) { + (info.format != spv::ImageFormat::R32ui) && + !((info.format == spv::ImageFormat::Rg16f || + info.format == spv::ImageFormat::Rgba16f) && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4658) << "Expected the Image Format in Image to be R64i, R64ui, R32f, " @@ -2130,6 +2165,127 @@ return SPV_SUCCESS; } +spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id, + spv::Decoration decor) { + const Instruction* si_inst = nullptr; + const Instruction* ld_inst = _.FindDef(id); + bool is_intf_obj = (ld_inst->opcode() == spv::Op::OpSampledImage); + if (is_intf_obj == true) { + si_inst = ld_inst; + int t_idx = si_inst->GetOperandAs<int>(2); // texture + ld_inst = _.FindDef(t_idx); + } + if (ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad"; + } + int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t& _, + int id) { + const Instruction* ld_inst = _.FindDef(id); + bool is_intf_obj = (ld_inst->opcode() != spv::Op::OpSampledImage); + if (is_intf_obj == true) { + if (ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad"; + } + int texture_id = ld_inst->GetOperandAs<int>(2); // variable to load + spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM; + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + decor = spv::Decoration::BlockMatchSamplerQCOM; + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + } else { + const Instruction* si_inst = ld_inst; + int t_idx = si_inst->GetOperandAs<int>(2); // texture + const Instruction* t_ld_inst = _.FindDef(t_idx); + if (t_ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, t_ld_inst) + << "Expect to see OpLoad"; + } + int texture_id = t_ld_inst->GetOperandAs<int>(2); // variable to load + spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM; + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + int s_idx = si_inst->GetOperandAs<int>(3); // sampler + const Instruction* s_ld_inst = _.FindDef(s_idx); + if (s_ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, s_ld_inst) + << "Expect to see OpLoad"; + } + int sampler_id = s_ld_inst->GetOperandAs<int>(2); // variable to load + decor = spv::Decoration::BlockMatchSamplerQCOM; + if (!_.HasDecoration(sampler_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _, + const Instruction* inst) { + spv_result_t res = SPV_SUCCESS; + const spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpImageSampleWeightedQCOM: { + int wi_idx = inst->GetOperandAs<int>(4); // weight + res = ValidateImageProcessingQCOMDecoration( + _, wi_idx, spv::Decoration::WeightTextureQCOM); + break; + } + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: { + int tgt_idx = inst->GetOperandAs<int>(2); // target + res = ValidateImageProcessingQCOMDecoration( + _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM); + if (res != SPV_SUCCESS) break; + int ref_idx = inst->GetOperandAs<int>(4); // reference + res = ValidateImageProcessingQCOMDecoration( + _, ref_idx, spv::Decoration::BlockMatchTextureQCOM); + break; + } + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: { + int tgt_idx = inst->GetOperandAs<int>(2); // target + res = ValidateImageProcessing2QCOMWindowDecoration(_, tgt_idx); + if (res != SPV_SUCCESS) break; + int ref_idx = inst->GetOperandAs<int>(4); // reference + res = ValidateImageProcessing2QCOMWindowDecoration(_, ref_idx); + break; + } + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: { + int tgt_idx = inst->GetOperandAs<int>(2); // target + res = ValidateImageProcessingQCOMDecoration( + _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM); + if (res != SPV_SUCCESS) break; + int ref_idx = inst->GetOperandAs<int>(4); // reference + res = ValidateImageProcessingQCOMDecoration( + _, ref_idx, spv::Decoration::BlockMatchTextureQCOM); + break; + } + default: + break; + } + + return res; +} + } // namespace // Validates correctness of image instructions. @@ -2249,6 +2405,16 @@ case spv::Op::OpImageSparseTexelsResident: return ValidateImageSparseTexelsResident(_, inst); + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + return ValidateImageProcessingQCOM(_, inst); + default: break; } @@ -2256,5 +2422,98 @@ return SPV_SUCCESS; } +bool IsImageInstruction(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + + case spv::Op::OpImage: + case spv::Op::OpImageFetch: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseRead: + case spv::Op::OpImageWrite: + + 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: + + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + if (!IsImageInstruction(opcode)) return SPV_SUCCESS; + + switch (opcode) { + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + break; + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + break; + default: + for (size_t i = 0; i < inst->operands().size(); ++i) { + int id = inst->GetOperandAs<int>(i); + const Instruction* operand_inst = _.FindDef(id); + if (operand_inst == nullptr) continue; + if (operand_inst->opcode() == spv::Op::OpLoad) { + if (_.IsQCOMImageProcessingTextureConsumer(id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Illegal use of QCOM image processing decorated texture"; + } + } + if (operand_inst->opcode() == spv::Op::OpSampledImage) { + if (_.IsQCOMImageProcessingTextureConsumer(id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Illegal use of QCOM image processing decorated texture"; + } + } + } + break; + } + return SPV_SUCCESS; +} + } // namespace val } // namespace spvtools
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp index fde6e52..5bc4d2c 100644 --- a/source/val/validate_instruction.cpp +++ b/source/val/validate_instruction.cpp
@@ -38,14 +38,14 @@ std::string ToString(const CapabilitySet& capabilities, const AssemblyGrammar& grammar) { std::stringstream ss; - capabilities.ForEach([&grammar, &ss](spv::Capability cap) { + for (auto capability : capabilities) { spv_operand_desc desc; if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, - uint32_t(cap), &desc)) + uint32_t(capability), &desc)) ss << desc->name << " "; else - ss << uint32_t(cap) << " "; - }); + ss << uint32_t(capability) << " "; + } return ss.str(); } @@ -178,10 +178,11 @@ // Vulkan API requires more capabilities on rounding mode. if (spvIsVulkanEnv(state.context()->target_env)) { - enabling_capabilities.Add(spv::Capability::StorageUniformBufferBlock16); - enabling_capabilities.Add(spv::Capability::StorageUniform16); - enabling_capabilities.Add(spv::Capability::StoragePushConstant16); - enabling_capabilities.Add(spv::Capability::StorageInputOutput16); + enabling_capabilities.insert( + spv::Capability::StorageUniformBufferBlock16); + enabling_capabilities.insert(spv::Capability::StorageUniform16); + enabling_capabilities.insert(spv::Capability::StoragePushConstant16); + enabling_capabilities.insert(spv::Capability::StorageInputOutput16); } } else { enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv( @@ -195,7 +196,7 @@ if (inst->opcode() != spv::Op::OpCapability) { const bool enabled_by_cap = state.HasAnyOfCapabilities(enabling_capabilities); - if (!enabling_capabilities.IsEmpty() && !enabled_by_cap) { + if (!enabling_capabilities.empty() && !enabled_by_cap) { return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst) << "Operand " << which_operand << " of " << spvOpcodeString(inst->opcode()) @@ -303,7 +304,7 @@ } ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions); - if (exts.IsEmpty()) { + if (exts.empty()) { // If no extensions can enable this instruction, then emit error // messages only concerning core SPIR-V versions if errors happen. if (min_version == ~0u) { @@ -469,7 +470,8 @@ } _.set_addressing_model(inst->GetOperandAs<spv::AddressingModel>(0)); _.set_memory_model(inst->GetOperandAs<spv::MemoryModel>(1)); - } else if (opcode == spv::Op::OpExecutionMode) { + } else if (opcode == spv::Op::OpExecutionMode || + opcode == spv::Op::OpExecutionModeId) { const uint32_t entry_point = inst->word(1); _.RegisterExecutionModeForEntryPoint(entry_point, spv::ExecutionMode(inst->word(2)));
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 3566698..ace548a 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp
@@ -173,8 +173,19 @@ } break; } + case spv::Op::OpTypePointer: { + if (_.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64 && + type->GetOperandAs<spv::StorageClass>(1) == + spv::StorageClass::PhysicalStorageBuffer) { + *num_locations = 1; + break; + } + [[fallthrough]]; + } default: - break; + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Invalid type to assign a location"; } return SPV_SUCCESS; @@ -206,6 +217,14 @@ // Skip the array. return NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1))); + case spv::Op::OpTypePointer: + if (_.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64 && + type->GetOperandAs<spv::StorageClass>(1) == + spv::StorageClass::PhysicalStorageBuffer) { + return 2; + } + break; default: // This is an error that is validated elsewhere. break; @@ -235,37 +254,24 @@ // equal. Also track Patch and PerTaskNV decorations. bool has_location = false; uint32_t location = 0; - bool has_component = false; uint32_t component = 0; bool has_index = false; uint32_t index = 0; bool has_patch = false; bool has_per_task_nv = false; bool has_per_vertex_khr = false; + // Duplicate Location, Component, Index are checked elsewhere. for (auto& dec : _.id_decorations(variable->id())) { if (dec.dec_type() == spv::Decoration::Location) { - if (has_location && dec.params()[0] != location) { - return _.diag(SPV_ERROR_INVALID_DATA, variable) - << "Variable has conflicting location decorations"; - } has_location = true; location = dec.params()[0]; } else if (dec.dec_type() == spv::Decoration::Component) { - if (has_component && dec.params()[0] != component) { - return _.diag(SPV_ERROR_INVALID_DATA, variable) - << "Variable has conflicting component decorations"; - } - has_component = true; component = dec.params()[0]; } else if (dec.dec_type() == spv::Decoration::Index) { if (!is_output || !is_fragment) { return _.diag(SPV_ERROR_INVALID_DATA, variable) << "Index can only be applied to Fragment output variables"; } - if (has_index && dec.params()[0] != index) { - return _.diag(SPV_ERROR_INVALID_DATA, variable) - << "Variable has conflicting index decorations"; - } has_index = true; index = dec.params()[0]; } else if (dec.dec_type() == spv::Decoration::BuiltIn) { @@ -363,12 +369,12 @@ sub_type = _.FindDef(sub_type_id); } - for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) { - uint32_t num_locations = 0; - if (auto error = NumConsumedLocations(_, sub_type, &num_locations)) - return error; + uint32_t num_locations = 0; + if (auto error = NumConsumedLocations(_, sub_type, &num_locations)) + return error; + uint32_t num_components = NumConsumedComponents(_, sub_type); - uint32_t num_components = NumConsumedComponents(_, sub_type); + for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) { uint32_t array_location = location + (num_locations * array_idx); uint32_t start = array_location * 4; if (kMaxLocations <= start) { @@ -388,6 +394,7 @@ for (uint32_t i = start; i < end; ++i) { if (!locs->insert(i).second) { return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721)) << "Entry-point has conflicting " << storage_class << " location assignment at location " << i / 4 << ", component " << i % 4; @@ -459,6 +466,7 @@ uint32_t check = 4 * l + c; if (!locations->insert(check).second) { return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721)) << "Entry-point has conflicting " << storage_class << " location assignment at location " << l << ", component " << c; @@ -476,6 +484,7 @@ for (uint32_t l = start; l < end; ++l) { if (!locations->insert(l).second) { return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721)) << "Entry-point has conflicting " << storage_class << " location assignment at location " << l / 4 << ", component " << l % 4; @@ -536,6 +545,64 @@ return SPV_SUCCESS; } +spv_result_t ValidateStorageClass(ValidationState_t& _, + const Instruction* entry_point) { + bool has_push_constant = false; + bool has_ray_payload = false; + bool has_hit_attribute = false; + bool has_callable_data = false; + for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { + auto interface_id = entry_point->GetOperandAs<uint32_t>(i); + auto interface_var = _.FindDef(interface_id); + auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2); + switch (storage_class) { + case spv::StorageClass::PushConstant: { + if (has_push_constant) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(6673) + << "Entry-point has more than one variable with the " + "PushConstant storage class in the interface"; + } + has_push_constant = true; + break; + } + case spv::StorageClass::IncomingRayPayloadKHR: { + if (has_ray_payload) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(4700) + << "Entry-point has more than one variable with the " + "IncomingRayPayloadKHR storage class in the interface"; + } + has_ray_payload = true; + break; + } + case spv::StorageClass::HitAttributeKHR: { + if (has_hit_attribute) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(4702) + << "Entry-point has more than one variable with the " + "HitAttributeKHR storage class in the interface"; + } + has_hit_attribute = true; + break; + } + case spv::StorageClass::IncomingCallableDataKHR: { + if (has_callable_data) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(4706) + << "Entry-point has more than one variable with the " + "IncomingCallableDataKHR storage class in the interface"; + } + has_callable_data = true; + break; + } + default: + break; + } + } + return SPV_SUCCESS; +} + } // namespace spv_result_t ValidateInterfaces(ValidationState_t& _) { @@ -554,6 +621,9 @@ if (auto error = ValidateLocations(_, &inst)) { return error; } + if (auto error = ValidateStorageClass(_, &inst)) { + return error; + } } if (inst.opcode() == spv::Op::OpTypeVoid) break; }
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index 5f7358c..2d6715f 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp
@@ -204,6 +204,7 @@ switch (storage->opcode()) { case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: return true; case spv::Op::OpTypeArray: case spv::Op::OpTypeRuntimeArray: @@ -232,6 +233,7 @@ spv::StorageClass src_sc = spv::StorageClass::Max; switch (inst->opcode()) { case spv::Op::OpCooperativeMatrixLoadNV: + case spv::Op::OpCooperativeMatrixLoadKHR: case spv::Op::OpLoad: { auto load_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(2)); auto load_pointer_type = _.FindDef(load_pointer->type_id()); @@ -239,6 +241,7 @@ break; } case spv::Op::OpCooperativeMatrixStoreNV: + case spv::Op::OpCooperativeMatrixStoreKHR: case spv::Op::OpStore: { auto store_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0)); auto store_pointer_type = _.FindDef(store_pointer->type_id()); @@ -326,7 +329,8 @@ const uint32_t mask = inst->GetOperandAs<uint32_t>(index); if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) { if (inst->opcode() == spv::Op::OpLoad || - inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) { + inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV || + inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "MakePointerAvailableKHR cannot be used with OpLoad."; } @@ -442,6 +446,7 @@ 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 && @@ -475,8 +480,8 @@ "can only be used with non-externally visible shader Storage " "Classes: Workgroup, CrossWorkgroup, Private, Function, " "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, " - "HitAttributeKHR, CallableDataKHR, or " - "IncomingCallableDataKHR"; + "HitAttributeKHR, CallableDataKHR, " + "IncomingCallableDataKHR, or UniformConstant"; } } } @@ -961,6 +966,8 @@ } } + _.RegisterQCOMImageProcessingTextureConsumer(pointer_id, inst, nullptr); + return SPV_SUCCESS; } @@ -1356,6 +1363,7 @@ case spv::Op::OpTypeMatrix: case spv::Op::OpTypeVector: case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: case spv::Op::OpTypeArray: case spv::Op::OpTypeRuntimeArray: { // In OpTypeMatrix, OpTypeVector, spv::Op::OpTypeCooperativeMatrixNV, @@ -1366,33 +1374,30 @@ case spv::Op::OpTypeStruct: { // In case of structures, there is an additional constraint on the // index: the index must be an OpConstant. - if (spv::Op::OpConstant != cur_word_instr->opcode()) { + 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 " "structure must be an OpConstant."; } - // Get the index value from the OpConstant (word 3 of OpConstant). - // OpConstant could be a signed integer. But it's okay to treat it as - // unsigned because a negative constant int would never be seen as - // correct as a struct offset, since structs can't have more than 2 - // billion members. - const uint32_t cur_index = cur_word_instr->word(3); + // The index points to the struct member we want, therefore, the index // should be less than the number of struct members. - const uint32_t num_struct_members = - static_cast<uint32_t>(type_pointee->words().size() - 2); - if (cur_index >= num_struct_members) { + 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 - << " can not find index " << cur_index + << " 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 << "."; } // Struct members IDs start at word 2 of OpTypeStruct. - auto structMemberId = type_pointee->word(cur_index + 2); + const size_t word_index = static_cast<size_t>(cur_index) + 2; + auto structMemberId = type_pointee->word(word_index); type_pointee = _.FindDef(structMemberId); break; } @@ -1405,7 +1410,7 @@ } } } - // At this point, we have fully walked down from the base using the indeces. + // At this point, we have fully walked down from the base using the indices. // The type being pointed to should be the same as the result type. if (type_pointee->id() != result_type_pointee->id()) { return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -1422,6 +1427,126 @@ return SPV_SUCCESS; } +spv_result_t ValidateRawAccessChain(ValidationState_t& _, + const Instruction* inst) { + std::string instr_name = "Op" + std::string(spvOpcodeString(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> " + << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op" + << spvOpcodeString(result_type->opcode()) << '.'; + } + + // The pointed storage class must be valid. + const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1); + if (storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer && + storage_class != spv::StorageClass::Uniform) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Result Type of " << instr_name << " <id> " + << _.getIdName(inst->id()) + << " must point to a storage class of " + "StorageBuffer, PhysicalStorageBuffer, or Uniform."; + } + + // The pointed type must not be one in the list below. + const auto result_type_pointee = + _.FindDef(result_type->GetOperandAs<uint32_t>(2)); + if (result_type_pointee->opcode() == spv::Op::OpTypeArray || + 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> " + << _.getIdName(inst->id()) + << " must not point to " + "OpTypeArray, OpTypeMatrix, or OpTypeStruct."; + } + + // Validate Stride is a OpConstant. + 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> " + << _.getIdName(inst->id()) << " must be OpConstant. Found Op" + << spvOpcodeString(stride->opcode()) << '.'; + } + // Stride type must be OpTypeInt + 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> " + << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op" + << spvOpcodeString(stride_type->opcode()) << '.'; + } + + // Index and Offset type must be OpTypeInt with a width of 32 + const auto ValidateType = [&](const char* name, + int operandIndex) -> spv_result_t { + const auto value = _.FindDef(inst->GetOperandAs<uint32_t>(operandIndex)); + 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" + << 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 << '.'; + } + return SPV_SUCCESS; + }; + spv_result_t result; + result = ValidateType("Index", 4); + if (result != SPV_SUCCESS) { + return result; + } + result = ValidateType("Offset", 5); + if (result != SPV_SUCCESS) { + return result; + } + + uint32_t access_operands = 0; + if (inst->operands().size() >= 7) { + access_operands = inst->GetOperandAs<uint32_t>(6); + } + if (access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV)) { + uint64_t stride_value = 0; + if (_.EvalConstantValUint64(stride->id(), &stride_value) && + stride_value == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Stride must not be zero when per-element robustness is used."; + } + } + if (access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerComponentNV) || + access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV)) { + if (storage_class == spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Storage class cannot be PhysicalStorageBuffer when " + "raw access chain robustness is used."; + } + } + if (access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerComponentNV) && + access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Per-component robustness and per-element robustness are " + "mutually exclusive."; + } + + return SPV_SUCCESS; +} + spv_result_t ValidatePtrAccessChain(ValidationState_t& _, const Instruction* inst) { if (_.addressing_model() == spv::AddressingModel::Logical) { @@ -1553,9 +1678,15 @@ << " 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 = state.FindDef(type_id); - if (type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { + if (isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The type in " << instr_name << " <id> " + << state.getIdName(type_id) + << " must be OpTypeCooperativeMatrixKHR."; + } else if (!isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { return state.diag(SPV_ERROR_INVALID_ID, inst) << "The type in " << instr_name << " <id> " << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV."; @@ -1667,6 +1798,113 @@ return SPV_SUCCESS; } +spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _, + const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) { + type_id = inst->type_id(); + opname = "spv::Op::OpCooperativeMatrixLoadKHR"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs<uint32_t>(1))->type_id(); + opname = "spv::Op::OpCooperativeMatrixStoreKHR"; + } + + auto matrix_type = _.FindDef(type_id); + + if (matrix_type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixLoadKHR Result Type <id> " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixStoreKHR Object type <id> " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } + } + + const auto pointer_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 2u : 0u; + 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) + << _.VkErrorID(8973) << opname + << " storage class for pointer type <id> " + << _.getIdName(pointer_type_id) + << " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer."; + } + + const auto pointee_id = pointer_type->GetOperandAs<uint32_t>(2); + const auto pointee_type = _.FindDef(pointee_id); + if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) || + _.IsFloatScalarOrVectorType(pointee_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer <id> " << _.getIdName(pointer->id()) + << "s Type must be a scalar or vector type."; + } + + const auto layout_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 3u : 2u; + const auto colmajor_id = inst->GetOperandAs<uint32_t>(layout_index); + const auto colmajor = _.FindDef(colmajor_id); + if (!colmajor || !_.IsIntScalarType(colmajor->type_id()) || + !(spvOpcodeIsConstant(colmajor->opcode()) || + spvOpcodeIsSpecConstant(colmajor->opcode()))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout operand <id> " << _.getIdName(colmajor_id) + << " must be a 32-bit integer constant instruction."; + } + + 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())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Stride operand <id> " << _.getIdName(stride_id) + << " must be a scalar integer type."; + } + } + + const auto memory_access_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 5u : 4u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + return SPV_SUCCESS; +} + spv_result_t ValidatePtrComparison(ValidationState_t& _, const Instruction* inst) { if (_.addressing_model() == spv::AddressingModel::Logical && @@ -1748,6 +1986,9 @@ case spv::Op::OpInBoundsPtrAccessChain: if (auto error = ValidateAccessChain(_, inst)) return error; break; + case spv::Op::OpRawAccessChainNV: + if (auto error = ValidateRawAccessChain(_, inst)) return error; + break; case spv::Op::OpArrayLength: if (auto error = ValidateArrayLength(_, inst)) return error; break; @@ -1756,9 +1997,15 @@ if (auto error = ValidateCooperativeMatrixLoadStoreNV(_, inst)) return error; break; + case spv::Op::OpCooperativeMatrixLengthKHR: case spv::Op::OpCooperativeMatrixLengthNV: if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error; break; + case spv::Op::OpCooperativeMatrixLoadKHR: + case spv::Op::OpCooperativeMatrixStoreKHR: + if (auto error = ValidateCooperativeMatrixLoadStoreKHR(_, inst)) + return error; + break; case spv::Op::OpPtrEqual: case spv::Op::OpPtrNotEqual: case spv::Op::OpPtrDiff:
diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp index c4f22a6..dab7b5a 100644 --- a/source/val/validate_memory_semantics.cpp +++ b/source/val/validate_memory_semantics.cpp
@@ -203,15 +203,12 @@ "storage class"; } -#if 0 - // TODO(atgoo@github.com): this check fails Vulkan CTS, reenable once fixed. if (opcode == spv::Op::OpControlBarrier && value && !includes_storage_class) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4650) << spvOpcodeString(opcode) << ": expected Memory Semantics to include a Vulkan-supported " "storage class if Memory Semantics is not None"; } -#endif } if (opcode == spv::Op::OpAtomicFlagClear &&
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index d757d4f..8502fda 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp
@@ -340,29 +340,93 @@ const auto mode = inst->GetOperandAs<spv::ExecutionMode>(1); if (inst->opcode() == spv::Op::OpExecutionModeId) { + bool valid_mode = false; + switch (mode) { + case spv::ExecutionMode::SubgroupsPerWorkgroupId: + case spv::ExecutionMode::LocalSizeHintId: + case spv::ExecutionMode::LocalSizeId: + case spv::ExecutionMode::FPFastMathDefault: + case spv::ExecutionMode::MaximumRegistersIdINTEL: + valid_mode = true; + break; + default: + valid_mode = false; + break; + } + if (!valid_mode) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpExecutionModeId is only valid when the Mode operand is an " + "execution mode that takes Extra Operands that are id " + "operands."; + } + size_t operand_count = inst->operands().size(); for (size_t i = 2; i < operand_count; ++i) { - const auto operand_id = inst->GetOperandAs<uint32_t>(2); + const auto operand_id = inst->GetOperandAs<uint32_t>(i); const auto* operand_inst = _.FindDef(operand_id); - if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId || - mode == spv::ExecutionMode::LocalSizeHintId || - mode == spv::ExecutionMode::LocalSizeId) { - if (!spvOpcodeIsConstant(operand_inst->opcode())) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "For OpExecutionModeId all Extra Operand ids must be " - "constant " - "instructions."; - } - } else { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpExecutionModeId is only valid when the Mode operand is an " - "execution mode that takes Extra Operands that are id " - "operands."; + switch (mode) { + case spv::ExecutionMode::SubgroupsPerWorkgroupId: + case spv::ExecutionMode::LocalSizeHintId: + case spv::ExecutionMode::LocalSizeId: + if (!spvOpcodeIsConstant(operand_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For OpExecutionModeId all Extra Operand ids must be " + "constant instructions."; + } + break; + case spv::ExecutionMode::FPFastMathDefault: + if (i == 2) { + if (!_.IsFloatScalarType(operand_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Target Type operand must be a floating-point " + "scalar type"; + } + } else { + bool is_int32 = false; + bool is_const = false; + uint32_t value = 0; + std::tie(is_int32, is_const, value) = + _.EvalInt32IfConst(operand_id); + if (is_int32 && is_const) { + // Valid values include up to 0x00040000 (AllowTransform). + uint32_t invalid_mask = 0xfff80000; + if ((invalid_mask & value) != 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand is an invalid bitmask " + "value"; + } + if (value & + static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand must not include Fast"; + } + const auto reassoc_contract = + spv::FPFastMathModeMask::AllowContract | + spv::FPFastMathModeMask::AllowReassoc; + if ((value & static_cast<uint32_t>( + spv::FPFastMathModeMask::AllowTransform)) != 0 && + ((value & static_cast<uint32_t>(reassoc_contract)) != + static_cast<uint32_t>(reassoc_contract))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand must include " + "AllowContract and AllowReassoc when AllowTransform " + "is specified"; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand must be a " + "non-specialization constant"; + } + } + break; + default: + break; } } } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId || mode == spv::ExecutionMode::LocalSizeHintId || - mode == spv::ExecutionMode::LocalSizeId) { + mode == spv::ExecutionMode::LocalSizeId || + mode == spv::ExecutionMode::FPFastMathDefault) { 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 " @@ -494,6 +558,17 @@ "model."; } break; + case spv::ExecutionMode::QuadDerivativesKHR: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return (model == spv::ExecutionModel::Fragment || + model == spv::ExecutionModel::GLCompute); + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Fragment or " + "GLCompute execution model."; + } + break; case spv::ExecutionMode::PixelCenterInteger: case spv::ExecutionMode::OriginUpperLeft: case spv::ExecutionMode::OriginLowerLeft: @@ -518,6 +593,7 @@ case spv::ExecutionMode::StencilRefUnchangedBackAMD: case spv::ExecutionMode::StencilRefGreaterBackAMD: case spv::ExecutionMode::StencilRefLessBackAMD: + case spv::ExecutionMode::RequireFullQuadsKHR: if (!std::all_of(models->begin(), models->end(), [](const spv::ExecutionModel& model) { return model == spv::ExecutionModel::Fragment; @@ -579,6 +655,20 @@ break; } + if (mode == spv::ExecutionMode::FPFastMathDefault) { + const auto* modes = _.GetExecutionModes(entry_point_id); + if (modes && modes->count(spv::ExecutionMode::ContractionOff)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "FPFastMathDefault and ContractionOff execution modes cannot " + "be applied to the same entry point"; + } + if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "FPFastMathDefault and SignedZeroInfNanPreserve execution " + "modes cannot be applied to the same entry point"; + } + } + if (spvIsVulkanEnv(_.context()->target_env)) { if (mode == spv::ExecutionMode::OriginLowerLeft) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -634,8 +724,91 @@ return SPV_SUCCESS; } +bool PerEntryExecutionMode(spv::ExecutionMode mode) { + switch (mode) { + // These execution modes can be specified multiple times per entry point. + case spv::ExecutionMode::DenormPreserve: + case spv::ExecutionMode::DenormFlushToZero: + case spv::ExecutionMode::SignedZeroInfNanPreserve: + case spv::ExecutionMode::RoundingModeRTE: + case spv::ExecutionMode::RoundingModeRTZ: + case spv::ExecutionMode::FPFastMathDefault: + case spv::ExecutionMode::RoundingModeRTPINTEL: + case spv::ExecutionMode::RoundingModeRTNINTEL: + case spv::ExecutionMode::FloatingPointModeALTINTEL: + case spv::ExecutionMode::FloatingPointModeIEEEINTEL: + return false; + default: + return true; + } +} + } // namespace +spv_result_t ValidateFloatControls2(ValidationState_t& _) { + std::unordered_set<uint32_t> fp_fast_math_default_entry_points; + for (auto entry_point : _.entry_points()) { + const auto* exec_modes = _.GetExecutionModes(entry_point); + if (exec_modes && + exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) { + fp_fast_math_default_entry_points.insert(entry_point); + } + } + + std::vector<std::pair<const Instruction*, spv::Decoration>> worklist; + for (const auto& inst : _.ordered_instructions()) { + if (inst.opcode() != spv::Op::OpDecorate) { + continue; + } + + const auto decoration = inst.GetOperandAs<spv::Decoration>(1); + const auto target_id = inst.GetOperandAs<uint32_t>(0); + const auto target = _.FindDef(target_id); + if (decoration == spv::Decoration::NoContraction) { + worklist.push_back(std::make_pair(target, decoration)); + } else if (decoration == spv::Decoration::FPFastMathMode) { + auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2); + if ((mask & spv::FPFastMathModeMask::Fast) != + spv::FPFastMathModeMask::MaskNone) { + worklist.push_back(std::make_pair(target, decoration)); + } + } + } + + std::unordered_set<const Instruction*> visited; + while (!worklist.empty()) { + const auto inst = worklist.back().first; + const auto decoration = worklist.back().second; + worklist.pop_back(); + + if (!visited.insert(inst).second) { + continue; + } + + const auto function = inst->function(); + if (function) { + const auto& entry_points = _.FunctionEntryPoints(function->id()); + for (auto entry_point : entry_points) { + if (fp_fast_math_default_entry_points.count(entry_point)) { + const std::string dec = decoration == spv::Decoration::NoContraction + ? "NoContraction" + : "FPFastMathMode Fast"; + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << dec + << " cannot be used by an entry point with the " + "FPFastMathDefault execution mode"; + } + } + } else { + for (const auto& pair : inst->uses()) { + worklist.push_back(std::make_pair(pair.first, decoration)); + } + } + } + + return SPV_SUCCESS; +} + spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) { switch (inst->opcode()) { case spv::Op::OpEntryPoint: @@ -654,5 +827,52 @@ return SPV_SUCCESS; } +spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _) { + using PerEntryKey = std::tuple<spv::ExecutionMode, uint32_t>; + using PerOperandKey = std::tuple<spv::ExecutionMode, uint32_t, uint32_t>; + 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); + } + return "Unknown"; + }; + + for (const auto& inst : _.ordered_instructions()) { + if (inst.opcode() != spv::Op::OpExecutionMode && + inst.opcode() != spv::Op::OpExecutionModeId) { + continue; + } + + const auto entry = inst.GetOperandAs<uint32_t>(0); + const auto mode = inst.GetOperandAs<spv::ExecutionMode>(1); + if (PerEntryExecutionMode(mode)) { + if (!seen_per_entry.insert(std::make_tuple(mode, entry)).second) { + return _.diag(SPV_ERROR_INVALID_ID, &inst) + << lookupMode(mode) + << " execution mode must not be specified multiple times per " + "entry point"; + } + } else { + // Execution modes allowed multiple times all take a single operand. + const auto operand = inst.GetOperandAs<uint32_t>(2); + if (!seen_per_operand.insert(std::make_tuple(mode, entry, operand)) + .second) { + return _.diag(SPV_ERROR_INVALID_ID, &inst) + << lookupMode(mode) + << " execution mode must not be specified multiple times for " + "the same entry point and operands"; + } + } + } + + return SPV_SUCCESS; +} + } // namespace val } // namespace spvtools
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp index af04e76..75967d2 100644 --- a/source/val/validate_non_uniform.cpp +++ b/source/val/validate_non_uniform.cpp
@@ -26,6 +26,207 @@ namespace val { namespace { +spv_result_t ValidateGroupNonUniformElect(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformAnyAll(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 ValidateGroupNonUniformAllEqual(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar type"; + } + + const auto value_type = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarOrVectorType(value_type) && + !_.IsIntScalarOrVectorType(value_type) && + !_.IsBoolScalarOrVectorType(value_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a scalar or vector of integer, floating-point, or " + "boolean type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBroadcastShuffle(ValidationState_t& _, + const Instruction* inst) { + const auto 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 auto 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"; + } + + const auto GetOperandName = [](const spv::Op opcode) { + std::string operand; + switch (opcode) { + case spv::Op::OpGroupNonUniformBroadcast: + case spv::Op::OpGroupNonUniformShuffle: + operand = "Id"; + break; + case spv::Op::OpGroupNonUniformShuffleXor: + operand = "Mask"; + break; + case spv::Op::OpGroupNonUniformQuadBroadcast: + operand = "Index"; + break; + case spv::Op::OpGroupNonUniformQuadSwap: + operand = "Direction"; + break; + case spv::Op::OpGroupNonUniformShuffleUp: + case spv::Op::OpGroupNonUniformShuffleDown: + default: + operand = "Delta"; + break; + } + return operand; + }; + + const auto id_type_id = _.GetOperandTypeId(inst, 4); + if (!_.IsUnsignedIntScalarType(id_type_id)) { + std::string operand = GetOperandName(inst->opcode()); + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << operand << " must be an unsigned integer scalar"; + } + + const bool should_be_constant = + inst->opcode() == spv::Op::OpGroupNonUniformQuadSwap || + ((inst->opcode() == spv::Op::OpGroupNonUniformBroadcast || + inst->opcode() == spv::Op::OpGroupNonUniformQuadBroadcast) && + _.version() < SPV_SPIRV_VERSION_WORD(1, 5)); + if (should_be_constant) { + const auto id_id = inst->GetOperandAs<uint32_t>(4); + const auto id_op = _.GetIdOpcode(id_id); + if (!spvOpcodeIsConstant(id_op)) { + std::string operand = GetOperandName(inst->opcode()); + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Before SPIR-V 1.5, " << operand + << " must be a constant instruction"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBroadcastFirst(ValidationState_t& _, + const Instruction* inst) { + const auto 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 auto 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 ValidateGroupNonUniformBallot(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsUnsignedIntVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(inst->type_id()) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 4-component unsigned integer vector"; + } + + const auto pred_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsBoolScalarType(pred_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Predicate must be a boolean scalar"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformInverseBallot(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntVectorType(value_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(value_type_id) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBallotBitExtract(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntVectorType(value_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(value_type_id) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + const auto id_type_id = _.GetOperandTypeId(inst, 4); + if (!_.IsUnsignedIntScalarType(id_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Id must be an unsigned integer scalar"; + } + + return SPV_SUCCESS; +} + spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _, const Instruction* inst) { // Scope is already checked by ValidateExecutionScope() above. @@ -60,6 +261,107 @@ return SPV_SUCCESS; } +spv_result_t ValidateGroupNonUniformBallotFind(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsUnsignedIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an unsigned integer scalar"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntVectorType(value_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(value_type_id) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformArithmetic(ValidationState_t& _, + const Instruction* inst) { + const bool is_unsigned = inst->opcode() == spv::Op::OpGroupNonUniformUMin || + inst->opcode() == spv::Op::OpGroupNonUniformUMax; + const bool is_float = inst->opcode() == spv::Op::OpGroupNonUniformFAdd || + inst->opcode() == spv::Op::OpGroupNonUniformFMul || + inst->opcode() == spv::Op::OpGroupNonUniformFMin || + inst->opcode() == spv::Op::OpGroupNonUniformFMax; + const bool is_bool = inst->opcode() == spv::Op::OpGroupNonUniformLogicalAnd || + inst->opcode() == spv::Op::OpGroupNonUniformLogicalOr || + inst->opcode() == spv::Op::OpGroupNonUniformLogicalXor; + if (is_float) { + if (!_.IsFloatScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a floating-point scalar or vector"; + } + } else if (is_bool) { + if (!_.IsBoolScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar or vector"; + } + } else if (is_unsigned) { + if (!_.IsUnsignedIntScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an unsigned integer scalar or vector"; + } + } else if (!_.IsIntScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an integer scalar or vector"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 4); + if (value_type_id != inst->type_id()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of Value must match the Result type"; + } + + const auto group_op = inst->GetOperandAs<spv::GroupOperation>(3); + bool is_clustered_reduce = group_op == spv::GroupOperation::ClusteredReduce; + bool is_partitioned_nv = + group_op == spv::GroupOperation::PartitionedReduceNV || + group_op == spv::GroupOperation::PartitionedInclusiveScanNV || + group_op == spv::GroupOperation::PartitionedExclusiveScanNV; + if (inst->operands().size() <= 5) { + if (is_clustered_reduce) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be present when Operation is ClusteredReduce"; + } else if (is_partitioned_nv) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ballot must be present when Operation is PartitionedReduceNV, " + "PartitionedInclusiveScanNV, or PartitionedExclusiveScanNV"; + } + } else { + const auto operand_id = inst->GetOperandAs<uint32_t>(5); + const auto* operand = _.FindDef(operand_id); + if (is_partitioned_nv) { + if (!operand || !_.IsIntScalarOrVectorType(operand->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ballot must be a 4-component integer vector"; + } + + if (_.GetDimension(operand->type_id()) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ballot must be a 4-component integer vector"; + } + } else { + if (!operand || !_.IsUnsignedIntScalarType(operand->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be an unsigned integer scalar"; + } + + if (!spvOpcodeIsConstant(operand->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be a constant instruction"; + } + } + } + return SPV_SUCCESS; +} + spv_result_t ValidateGroupNonUniformRotateKHR(ValidationState_t& _, const Instruction* inst) { // Scope is already checked by ValidateExecutionScope() above. @@ -87,20 +389,25 @@ if (inst->words().size() > 6) { const uint32_t cluster_size_op_id = inst->GetOperandAs<uint32_t>(5); - const uint32_t cluster_size_type = _.GetTypeId(cluster_size_op_id); + const Instruction* cluster_size_inst = _.FindDef(cluster_size_op_id); + const uint32_t cluster_size_type = + cluster_size_inst ? cluster_size_inst->type_id() : 0; if (!_.IsUnsignedIntScalarType(cluster_size_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "ClusterSize must be a scalar of integer type, whose " "Signedness operand is 0."; } - uint64_t cluster_size; - if (!_.GetConstantValUint64(cluster_size_op_id, &cluster_size)) { + if (!spvOpcodeIsConstant(cluster_size_inst->opcode())) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "ClusterSize must come from a constant instruction."; } - if ((cluster_size == 0) || ((cluster_size & (cluster_size - 1)) != 0)) { + uint64_t cluster_size; + const bool valid_const = + _.EvalConstantValUint64(cluster_size_op_id, &cluster_size); + if (valid_const && + ((cluster_size == 0) || ((cluster_size & (cluster_size - 1)) != 0))) { return _.diag(SPV_WARNING, inst) << "Behavior is undefined unless ClusterSize is at least 1 and a " "power of 2."; @@ -120,15 +427,63 @@ const spv::Op opcode = inst->opcode(); if (spvOpcodeIsNonUniformGroupOperation(opcode)) { - const uint32_t execution_scope = inst->word(3); - if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { - return error; + // OpGroupNonUniformQuadAllKHR and OpGroupNonUniformQuadAnyKHR don't have + // scope paramter + if ((opcode != spv::Op::OpGroupNonUniformQuadAllKHR) && + (opcode != spv::Op::OpGroupNonUniformQuadAnyKHR)) { + const uint32_t execution_scope = inst->GetOperandAs<uint32_t>(2); + if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { + return error; + } } } switch (opcode) { + case spv::Op::OpGroupNonUniformElect: + return ValidateGroupNonUniformElect(_, inst); + case spv::Op::OpGroupNonUniformAny: + case spv::Op::OpGroupNonUniformAll: + return ValidateGroupNonUniformAnyAll(_, inst); + case spv::Op::OpGroupNonUniformAllEqual: + return ValidateGroupNonUniformAllEqual(_, inst); + 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: + return ValidateGroupNonUniformBroadcastShuffle(_, inst); + case spv::Op::OpGroupNonUniformBroadcastFirst: + return ValidateGroupNonUniformBroadcastFirst(_, inst); + case spv::Op::OpGroupNonUniformBallot: + return ValidateGroupNonUniformBallot(_, inst); + case spv::Op::OpGroupNonUniformInverseBallot: + return ValidateGroupNonUniformInverseBallot(_, inst); + case spv::Op::OpGroupNonUniformBallotBitExtract: + return ValidateGroupNonUniformBallotBitExtract(_, inst); case spv::Op::OpGroupNonUniformBallotBitCount: return ValidateGroupNonUniformBallotBitCount(_, inst); + case spv::Op::OpGroupNonUniformBallotFindLSB: + case spv::Op::OpGroupNonUniformBallotFindMSB: + return ValidateGroupNonUniformBallotFind(_, inst); + case spv::Op::OpGroupNonUniformIAdd: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformIMul: + case spv::Op::OpGroupNonUniformFMul: + case spv::Op::OpGroupNonUniformSMin: + case spv::Op::OpGroupNonUniformUMin: + case spv::Op::OpGroupNonUniformFMin: + case spv::Op::OpGroupNonUniformSMax: + case spv::Op::OpGroupNonUniformUMax: + case spv::Op::OpGroupNonUniformFMax: + case spv::Op::OpGroupNonUniformBitwiseAnd: + case spv::Op::OpGroupNonUniformBitwiseOr: + case spv::Op::OpGroupNonUniformBitwiseXor: + case spv::Op::OpGroupNonUniformLogicalAnd: + case spv::Op::OpGroupNonUniformLogicalOr: + case spv::Op::OpGroupNonUniformLogicalXor: + return ValidateGroupNonUniformArithmetic(_, inst); case spv::Op::OpGroupNonUniformRotateKHR: return ValidateGroupNonUniformRotateKHR(_, inst); default:
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 40c49d1..6b49353 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp
@@ -97,8 +97,10 @@ // Vulkan 1.1 specific rules if (_.context()->target_env != SPV_ENV_VULKAN_1_0) { // Scope for Non Uniform Group Operations must be limited to Subgroup - if (spvOpcodeIsNonUniformGroupOperation(opcode) && - value != spv::Scope::Subgroup) { + if ((spvOpcodeIsNonUniformGroupOperation(opcode) && + (opcode != spv::Op::OpGroupNonUniformQuadAllKHR) && + (opcode != spv::Op::OpGroupNonUniformQuadAnyKHR)) && + (value != spv::Scope::Subgroup)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << _.VkErrorID(4642) << spvOpcodeString(opcode) << ": in Vulkan environment Execution scope is limited to " @@ -178,6 +180,8 @@ // Scope for execution must be limited to Workgroup or Subgroup for // non-uniform operations if (spvOpcodeIsNonUniformGroupOperation(opcode) && + opcode != spv::Op::OpGroupNonUniformQuadAllKHR && + opcode != spv::Op::OpGroupNonUniformQuadAnyKHR && value != spv::Scope::Subgroup && value != spv::Scope::Workgroup) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode)
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 430d819..cb26a52 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp
@@ -24,21 +24,6 @@ namespace val { namespace { -// Returns, as an int64_t, the literal value from an OpConstant or the -// default value of an OpSpecConstant, assuming it is an integral type. -// For signed integers, relies the rule that literal value is sign extended -// to fill out to word granularity. Assumes that the constant value -// has -int64_t ConstantLiteralAsInt64(uint32_t width, - const std::vector<uint32_t>& const_words) { - const uint32_t lo_word = const_words[3]; - if (width <= 32) return int32_t(lo_word); - assert(width <= 64); - assert(const_words.size() > 4); - const uint32_t hi_word = const_words[4]; // Must exist, per spec. - return static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32); -} - // Validates that type declarations are unique, unless multiple declarations // of the same data type are allowed by the specification. // (see section 2.8 Types and Variables) @@ -252,29 +237,17 @@ << " is not a constant integer type."; } - switch (length->opcode()) { - case spv::Op::OpSpecConstant: - case spv::Op::OpConstant: { - auto& type_words = const_result_type->words(); - const bool is_signed = type_words[3] > 0; - const uint32_t width = type_words[2]; - const int64_t ivalue = ConstantLiteralAsInt64(width, length->words()); - if (ivalue == 0 || (ivalue < 0 && is_signed)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Length <id> " << _.getIdName(length_id) - << " default value must be at least 1: found " << ivalue; - } - } break; - case spv::Op::OpConstantNull: + int64_t length_value; + if (_.EvalConstantValInt64(length_id, &length_value)) { + auto& type_words = const_result_type->words(); + const bool is_signed = type_words[3] > 0; + if (length_value == 0 || (length_value < 0 && is_signed)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypeArray Length <id> " << _.getIdName(length_id) - << " default value must be at least 1."; - case spv::Op::OpSpecConstantOp: - // Assume it's OK, rather than try to evaluate the operation. - break; - default: - assert(0 && "bug in spvOpcodeIsConstant() or result type isn't int"); + << " default value must be at least 1: found " << length_value; + } } + return SPV_SUCCESS; } @@ -552,8 +525,8 @@ return SPV_SUCCESS; } -spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, - const Instruction* inst) { +spv_result_t ValidateTypeCooperativeMatrix(ValidationState_t& _, + const Instruction* inst) { const auto component_type_index = 1; const auto component_type_id = inst->GetOperandAs<uint32_t>(component_type_index); @@ -561,7 +534,7 @@ if (!component_type || (spv::Op::OpTypeFloat != component_type->opcode() && spv::Op::OpTypeInt != component_type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Component Type <id> " + << "OpTypeCooperativeMatrix Component Type <id> " << _.getIdName(component_type_id) << " is not a scalar numerical type."; } @@ -572,7 +545,7 @@ if (!scope || !_.IsIntScalarType(scope->type_id()) || !spvOpcodeIsConstant(scope->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Scope <id> " << _.getIdName(scope_id) + << "OpTypeCooperativeMatrix Scope <id> " << _.getIdName(scope_id) << " is not a constant instruction with scalar integer type."; } @@ -582,7 +555,7 @@ if (!rows || !_.IsIntScalarType(rows->type_id()) || !spvOpcodeIsConstant(rows->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Rows <id> " << _.getIdName(rows_id) + << "OpTypeCooperativeMatrix Rows <id> " << _.getIdName(rows_id) << " is not a constant instruction with scalar integer type."; } @@ -592,10 +565,22 @@ if (!cols || !_.IsIntScalarType(cols->type_id()) || !spvOpcodeIsConstant(cols->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeCooperativeMatrixNV Cols <id> " << _.getIdName(cols_id) + << "OpTypeCooperativeMatrix Cols <id> " << _.getIdName(cols_id) << " is not a constant instruction with scalar integer type."; } + if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { + const auto use_index = 5; + const auto use_id = inst->GetOperandAs<uint32_t>(use_index); + const auto use = _.FindDef(use_id); + if (!use || !_.IsIntScalarType(use->type_id()) || + !spvOpcodeIsConstant(use->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixKHR Use <id> " << _.getIdName(use_id) + << " is not a constant instruction with scalar integer type."; + } + } + return SPV_SUCCESS; } } // namespace @@ -640,7 +625,8 @@ if (auto error = ValidateTypeForwardPointer(_, inst)) return error; break; case spv::Op::OpTypeCooperativeMatrixNV: - if (auto error = ValidateTypeCooperativeMatrixNV(_, inst)) return error; + case spv::Op::OpTypeCooperativeMatrixKHR: + if (auto error = ValidateTypeCooperativeMatrix(_, inst)) return error; break; default: break;
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index dbf0ba6..87322d2 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp
@@ -21,6 +21,7 @@ #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" +#include "source/util/make_unique.h" #include "source/val/basic_block.h" #include "source/val/construct.h" #include "source/val/function.h" @@ -359,14 +360,16 @@ // Avoid redundant work. Otherwise the recursion could induce work // quadrdatic in the capability dependency depth. (Ok, not much, but // it's something.) - if (module_capabilities_.Contains(cap)) return; + if (module_capabilities_.contains(cap)) return; - module_capabilities_.Add(cap); + module_capabilities_.insert(cap); spv_operand_desc desc; if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, uint32_t(cap), &desc)) { - CapabilitySet(desc->numCapabilities, desc->capabilities) - .ForEach([this](spv::Capability c) { RegisterCapability(c); }); + for (auto capability : + CapabilitySet(desc->numCapabilities, desc->capabilities)) { + RegisterCapability(capability); + } } switch (cap) { @@ -418,9 +421,9 @@ } void ValidationState_t::RegisterExtension(Extension ext) { - if (module_extensions_.Contains(ext)) return; + if (module_extensions_.contains(ext)) return; - module_extensions_.Add(ext); + module_extensions_.insert(ext); switch (ext) { case kSPV_AMD_gpu_shader_half_float: @@ -608,6 +611,19 @@ sampled_image_consumers_[sampled_image_id].push_back(consumer); } +void ValidationState_t::RegisterQCOMImageProcessingTextureConsumer( + uint32_t texture_id, const Instruction* consumer0, + const Instruction* consumer1) { + if (HasDecoration(texture_id, spv::Decoration::WeightTextureQCOM) || + HasDecoration(texture_id, spv::Decoration::BlockMatchTextureQCOM) || + HasDecoration(texture_id, spv::Decoration::BlockMatchSamplerQCOM)) { + qcom_image_processing_consumers_.insert(consumer0->id()); + if (consumer1) { + qcom_image_processing_consumers_.insert(consumer1->id()); + } + } +} + void ValidationState_t::RegisterStorageClassConsumer( spv::StorageClass storage_class, Instruction* consumer) { if (spvIsVulkanEnv(context()->target_env)) { @@ -665,39 +681,39 @@ if (storage_class == spv::StorageClass::CallableDataKHR) { std::string errorVUID = VkErrorID(4704); function(consumer->function()->id()) - ->RegisterExecutionModelLimitation([errorVUID]( - spv::ExecutionModel model, - std::string* message) { - if (model != spv::ExecutionModel::RayGenerationKHR && - model != spv::ExecutionModel::ClosestHitKHR && - model != spv::ExecutionModel::CallableKHR && - model != spv::ExecutionModel::MissKHR) { - if (message) { - *message = errorVUID + - "CallableDataKHR Storage Class is limited to " - "RayGenerationKHR, ClosestHitKHR, CallableKHR, and " - "MissKHR execution model"; - } - return false; - } - return true; - }); + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::CallableKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + errorVUID + + "CallableDataKHR Storage Class is limited to " + "RayGenerationKHR, ClosestHitKHR, CallableKHR, and " + "MissKHR execution model"; + } + return false; + } + return true; + }); } else if (storage_class == spv::StorageClass::IncomingCallableDataKHR) { std::string errorVUID = VkErrorID(4705); function(consumer->function()->id()) - ->RegisterExecutionModelLimitation([errorVUID]( - spv::ExecutionModel model, - std::string* message) { - if (model != spv::ExecutionModel::CallableKHR) { - if (message) { - *message = errorVUID + - "IncomingCallableDataKHR Storage Class is limited to " - "CallableKHR execution model"; - } - return false; - } - return true; - }); + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::CallableKHR) { + if (message) { + *message = + errorVUID + + "IncomingCallableDataKHR Storage Class is limited to " + "CallableKHR execution model"; + } + return false; + } + return true; + }); } else if (storage_class == spv::StorageClass::RayPayloadKHR) { std::string errorVUID = VkErrorID(4698); function(consumer->function()->id()) @@ -859,6 +875,7 @@ return GetComponentType(inst->word(2)); case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: return inst->word(2); default: @@ -886,6 +903,7 @@ return inst->word(3); case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: // Actual dimension isn't known, return 0 return 0; @@ -937,6 +955,20 @@ return false; } +bool ValidationState_t::IsFloat16Vector2Or4Type(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == spv::Op::OpTypeVector) { + uint32_t vectorDim = GetDimension(id); + return IsFloatScalarType(GetComponentType(id)) && + (vectorDim == 2 || vectorDim == 4) && + (GetBitWidth(GetComponentType(id)) == 16); + } + + return false; +} + bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { const Instruction* inst = FindDef(id); if (!inst) { @@ -1007,6 +1039,23 @@ return false; } +bool ValidationState_t::IsUnsignedIntScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeInt) { + return inst->GetOperandAs<uint32_t>(2) == 0; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsUnsignedIntScalarType(GetComponentType(id)); + } + + return false; +} + bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { const Instruction* inst = FindDef(id); return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 1; @@ -1143,21 +1192,67 @@ bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { const Instruction* inst = FindDef(id); + return inst && (inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV || + inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR); +} + +bool ValidationState_t::IsCooperativeMatrixNVType(uint32_t id) const { + const Instruction* inst = FindDef(id); return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV; } +bool ValidationState_t::IsCooperativeMatrixKHRType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR; +} + +bool ValidationState_t::IsCooperativeMatrixAType(uint32_t id) const { + if (!IsCooperativeMatrixKHRType(id)) return false; + const Instruction* inst = FindDef(id); + uint64_t matrixUse = 0; + if (EvalConstantValUint64(inst->word(6), &matrixUse)) { + return matrixUse == + static_cast<uint64_t>(spv::CooperativeMatrixUse::MatrixAKHR); + } + return false; +} + +bool ValidationState_t::IsCooperativeMatrixBType(uint32_t id) const { + if (!IsCooperativeMatrixKHRType(id)) return false; + const Instruction* inst = FindDef(id); + uint64_t matrixUse = 0; + if (EvalConstantValUint64(inst->word(6), &matrixUse)) { + return matrixUse == + static_cast<uint64_t>(spv::CooperativeMatrixUse::MatrixBKHR); + } + return false; +} +bool ValidationState_t::IsCooperativeMatrixAccType(uint32_t id) const { + if (!IsCooperativeMatrixKHRType(id)) return false; + const Instruction* inst = FindDef(id); + uint64_t matrixUse = 0; + if (EvalConstantValUint64(inst->word(6), &matrixUse)) { + return matrixUse == static_cast<uint64_t>( + spv::CooperativeMatrixUse::MatrixAccumulatorKHR); + } + return false; +} + bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { - if (!IsCooperativeMatrixType(id)) return false; + if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) + return false; return IsFloatScalarType(FindDef(id)->word(2)); } bool ValidationState_t::IsIntCooperativeMatrixType(uint32_t id) const { - if (!IsCooperativeMatrixType(id)) return false; + if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) + return false; return IsIntScalarType(FindDef(id)->word(2)); } bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const { - if (!IsCooperativeMatrixType(id)) return false; + if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) + return false; return IsUnsignedIntScalarType(FindDef(id)->word(2)); } @@ -1173,8 +1268,7 @@ const auto m1_type = FindDef(m1); const auto m2_type = FindDef(m2); - if (m1_type->opcode() != spv::Op::OpTypeCooperativeMatrixNV || - m2_type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { + if (m1_type->opcode() != m2_type->opcode()) { return diag(SPV_ERROR_INVALID_DATA, inst) << "Expected cooperative matrix types"; } @@ -1224,6 +1318,21 @@ << "identical"; } + if (m1_type->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { + uint32_t m1_use_id = m1_type->GetOperandAs<uint32_t>(5); + uint32_t m2_use_id = m2_type->GetOperandAs<uint32_t>(5); + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_use_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_use_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Use of Matrix type and Result Type to be " + << "identical"; + } + } + return SPV_SUCCESS; } @@ -1232,20 +1341,23 @@ return GetTypeId(inst->GetOperandAs<uint32_t>(operand_index)); } -bool ValidationState_t::GetConstantValUint64(uint32_t id, uint64_t* val) const { +bool ValidationState_t::EvalConstantValUint64(uint32_t id, + uint64_t* val) const { const Instruction* inst = FindDef(id); if (!inst) { assert(0 && "Instruction not found"); return false; } - if (inst->opcode() != spv::Op::OpConstant && - inst->opcode() != spv::Op::OpSpecConstant) - return false; - if (!IsIntScalarType(inst->type_id())) return false; - if (inst->words().size() == 4) { + if (inst->opcode() == spv::Op::OpConstantNull) { + *val = 0; + } else if (inst->opcode() != spv::Op::OpConstant) { + // Spec constant values cannot be evaluated so don't consider constant for + // static validation + return false; + } else if (inst->words().size() == 4) { *val = inst->word(3); } else { assert(inst->words().size() == 5); @@ -1255,6 +1367,32 @@ return true; } +bool ValidationState_t::EvalConstantValInt64(uint32_t id, int64_t* val) const { + const Instruction* inst = FindDef(id); + if (!inst) { + assert(0 && "Instruction not found"); + return false; + } + + if (!IsIntScalarType(inst->type_id())) return false; + + if (inst->opcode() == spv::Op::OpConstantNull) { + *val = 0; + } else if (inst->opcode() != spv::Op::OpConstant) { + // Spec constant values cannot be evaluated so don't consider constant for + // static validation + return false; + } else if (inst->words().size() == 4) { + *val = int32_t(inst->word(3)); + } else { + assert(inst->words().size() == 5); + const uint32_t lo_word = inst->word(3); + const uint32_t hi_word = inst->word(4); + *val = static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32); + } + return true; +} + std::tuple<bool, bool, uint32_t> ValidationState_t::EvalInt32IfConst( uint32_t id) const { const Instruction* const inst = FindDef(id); @@ -1489,6 +1627,7 @@ case spv::Op::OpTypeImage: case spv::Op::OpTypeSampledImage: case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: return ContainsType(inst->GetOperandAs<uint32_t>(1u), f, traverse_all_types); case spv::Op::OpTypePointer: @@ -2030,6 +2169,8 @@ 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: @@ -2048,8 +2189,6 @@ return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658); case 4659: return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659); - case 4662: - return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662); case 4663: return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663); case 4664: @@ -2078,14 +2217,20 @@ return VUID_WRAP(VUID-StandaloneSpirv-RayPayloadKHR-04698); case 4699: return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699); + case 4700: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04700); case 4701: return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04701); + case 4702: + return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04702); case 4703: return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04703); case 4704: return VUID_WRAP(VUID-StandaloneSpirv-CallableDataKHR-04704); case 4705: return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04705); + case 4706: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04706); case 7119: return VUID_WRAP(VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119); case 4708: @@ -2144,6 +2289,8 @@ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671); case 6672: return VUID_WRAP(VUID-StandaloneSpirv-Location-06672); + case 6673: + return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-06673); case 6674: return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674); case 6675: @@ -2164,6 +2311,24 @@ return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808); case 6925: return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925); + case 7041: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041); + case 7043: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043); + case 7044: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044); + case 7047: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07047); + case 7049: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07049); + case 7050: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07050); + case 7053: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07053); + case 7055: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07055); + case 7056: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07056); case 7102: return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102); case 7320: @@ -2180,6 +2345,14 @@ return VUID_WRAP(VUID-StandaloneSpirv-Component-07703); case 7951: return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951); + case 8721: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08721); + case 8722: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08722); + case 8973: + return VUID_WRAP(VUID-StandaloneSpirv-Pointer-08973); + case 9638: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-09638); default: return ""; // unknown id }
diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 4d5ac00..27acdcc 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h
@@ -317,7 +317,7 @@ /// Returns true if the capability is enabled in the module. bool HasCapability(spv::Capability cap) const { - return module_capabilities_.Contains(cap); + return module_capabilities_.contains(cap); } /// Returns a reference to the set of capabilities in the module. @@ -328,7 +328,7 @@ /// Returns true if the extension is enabled in the module. bool HasExtension(Extension ext) const { - return module_extensions_.Contains(ext); + return module_extensions_.contains(ext); } /// Returns true if any of the capabilities is enabled, or if |capabilities| @@ -485,6 +485,13 @@ void RegisterSampledImageConsumer(uint32_t sampled_image_id, Instruction* consumer); + // Record a cons_id as a consumer of texture_id + // if texture 'texture_id' has a QCOM image processing decoration + // and consumer is a load or a sampled image instruction + void RegisterQCOMImageProcessingTextureConsumer(uint32_t texture_id, + const Instruction* consumer0, + const Instruction* consumer1); + // Record a function's storage class consumer instruction void RegisterStorageClassConsumer(spv::StorageClass storage_class, Instruction* consumer); @@ -595,6 +602,7 @@ bool IsVoidType(uint32_t id) const; bool IsFloatScalarType(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; @@ -602,6 +610,7 @@ bool IsIntScalarOrVectorType(uint32_t id) const; bool IsUnsignedIntScalarType(uint32_t id) const; bool IsUnsignedIntVectorType(uint32_t id) const; + bool IsUnsignedIntScalarOrVectorType(uint32_t id) const; bool IsSignedIntScalarType(uint32_t id) const; bool IsSignedIntVectorType(uint32_t id) const; bool IsBoolScalarType(uint32_t id) const; @@ -610,6 +619,11 @@ bool IsPointerType(uint32_t id) const; bool IsAccelerationStructureType(uint32_t id) const; bool IsCooperativeMatrixType(uint32_t id) const; + bool IsCooperativeMatrixNVType(uint32_t id) const; + bool IsCooperativeMatrixKHRType(uint32_t id) const; + bool IsCooperativeMatrixAType(uint32_t id) const; + bool IsCooperativeMatrixBType(uint32_t id) const; + bool IsCooperativeMatrixAccType(uint32_t id) const; bool IsFloatCooperativeMatrixType(uint32_t id) const; bool IsIntCooperativeMatrixType(uint32_t id) const; bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const; @@ -634,10 +648,6 @@ const std::function<bool(const Instruction*)>& f, bool traverse_all_types = true) const; - // Gets value from OpConstant and OpSpecConstant as uint64. - // Returns false on failure (no instruction, wrong instruction, not int). - bool GetConstantValUint64(uint32_t id, uint64_t* val) const; - // Returns type_id if id has type or zero otherwise. uint32_t GetTypeId(uint32_t id) const; @@ -712,6 +722,14 @@ pointer_to_storage_image_.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 + // during validation. + bool EvalConstantValUint64(uint32_t id, uint64_t* val) const; + // Same as EvalConstantValUint64 but returns a signed int + bool EvalConstantValInt64(uint32_t id, int64_t* val) const; + // Tries to evaluate a 32-bit signed or unsigned scalar integer constant. // Returns tuple <is_int32, is_const_int32, value>. // OpSpecConstant* return |is_const_int32| as false since their values cannot @@ -786,6 +804,13 @@ current_layout_section_ = section; } + // Check if instruction 'id' is a consumer of a texture decorated + // with a QCOM image processing decoration + bool IsQCOMImageProcessingTextureConsumer(uint32_t id) { + return qcom_image_processing_consumers_.find(id) != + qcom_image_processing_consumers_.end(); + } + private: ValidationState_t(const ValidationState_t&); @@ -820,6 +845,10 @@ std::unordered_map<uint32_t, std::vector<Instruction*>> sampled_image_consumers_; + /// Stores load instructions that load textures used + // in QCOM image processing functions + std::unordered_set<uint32_t> qcom_image_processing_consumers_; + /// A map of operand IDs and their names defined by the OpName instruction std::unordered_map<uint32_t, std::string> operand_names_;
diff --git a/source/wasm/build.sh b/source/wasm/build.sh index 4f7b701..69468c9 100755 --- a/source/wasm/build.sh +++ b/source/wasm/build.sh
@@ -45,7 +45,7 @@ emcc \ --bind \ -I../../include \ - -std=c++11 \ + -std=c++17 \ ../../source/wasm/spirv-tools.cpp \ source/libSPIRV-Tools.a \ -o spirv-tools.js \
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 37c5e1d..40c64f8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt
@@ -12,22 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +if (${SPIRV_SKIP_TESTS}) + return() +endif() + +if (TARGET gmock_main) + message(STATUS "Found Google Mock, building tests.") +else() + message(STATUS "Did not find googletest, tests will not be built. " + "To enable tests place googletest in '<spirv-dir>/external/googletest'.") +endif() + # Add a SPIR-V Tools unit test. Signature: # add_spvtools_unittest( # TARGET target_name # SRCS src_file.h src_file.cpp # LIBS lib1 lib2 # ) - -if (NOT "${SPIRV_SKIP_TESTS}") - if (TARGET gmock_main) - message(STATUS "Found Google Mock, building tests.") - else() - message(STATUS "Did not find googletest, tests will not be built. " - "To enable tests place googletest in '<spirv-dir>/external/googletest'.") - endif() -endif() - function(add_spvtools_unittest) if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main) set(one_value_args TARGET PCH_FILE) @@ -111,7 +112,6 @@ hex_float_test.cpp immediate_int_test.cpp libspirv_macros_test.cpp - log_test.cpp named_id_test.cpp name_mapper_test.cpp opcode_make_test.cpp
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index 50710cd..1a868db 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp
@@ -1154,7 +1154,10 @@ {"%2 = OpSpecConstantOp %1 !1000 %2", "Invalid OpSpecConstantOp opcode: 1000"}, {"OpCapability !9999", "Invalid capability operand: 9999"}, - {"OpSource !9999 100", "Invalid source language operand: 9999"}, + {"OpSource !9999 100", + "Invalid source language operand: 9999, if you are creating a new " + "source language please use value 0 (Unknown) and when ready, add " + "your source language to SPRIV-Headers"}, {"OpEntryPoint !9999", "Invalid execution model operand: 9999"}, {"OpMemoryModel !9999", "Invalid addressing model operand: 9999"}, {"OpMemoryModel Logical !9999", "Invalid memory model operand: 9999"},
diff --git a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp index 9944c2c..bd5a7d5 100644 --- a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp +++ b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp
@@ -95,8 +95,7 @@ constexpr char kDiff[] = R"( ; SPIR-V ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 --; Bound: 15 -+; Bound: 16 + ; Bound: 15 ; Schema: 0 OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" @@ -199,8 +198,7 @@ constexpr char kDiff[] = R"( ; SPIR-V ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 --; Bound: 15 -+; Bound: 16 + ; Bound: 15 ; Schema: 0 OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450"
diff --git a/test/diff/diff_files/basic_autogen.cpp b/test/diff/diff_files/basic_autogen.cpp index f3afc70..d4b6846 100644 --- a/test/diff/diff_files/basic_autogen.cpp +++ b/test/diff/diff_files/basic_autogen.cpp
@@ -129,7 +129,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 36 ++; Bound: 30 ; Schema: 0 OpCapability Shader +%27 = OpExtInstImport "GLSL.std.450" @@ -272,7 +272,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 36 ++; Bound: 30 ; Schema: 0 OpCapability Shader +%27 = OpExtInstImport "GLSL.std.450" @@ -324,7 +324,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 36 ++; Bound: 30 ; Schema: 0 OpCapability Shader +%27 = OpExtInstImport "GLSL.std.450" @@ -384,8 +384,8 @@ 6 -> 14 [TypeInt] 13 -> 19 [TypePointer] 14 -> 27 [Variable] - 15 -> 34 [Constant] - 16 -> 35 [TypeArray] + 15 -> 28 [Constant] + 16 -> 29 [TypeArray] 17 -> 11 [TypeStruct] 18 -> 12 [TypePointer] 19 -> 13 [Variable]
diff --git a/test/diff/diff_files/constant_array_size_autogen.cpp b/test/diff/diff_files/constant_array_size_autogen.cpp index 16975ff..2b6d7d8 100644 --- a/test/diff/diff_files/constant_array_size_autogen.cpp +++ b/test/diff/diff_files/constant_array_size_autogen.cpp
@@ -125,7 +125,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 34 ++; Bound: 28 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450 @@ -259,7 +259,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 34 ++; Bound: 28 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake index 6440d0b..51cb62f 100644 --- a/test/diff/diff_files/diff_test_files_autogen.cmake +++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -36,6 +36,7 @@ "diff_files/large_functions_small_diffs_autogen.cpp" "diff_files/multiple_different_entry_points_autogen.cpp" "diff_files/multiple_same_entry_points_autogen.cpp" +"diff_files/ray_query_types_autogen.cpp" "diff_files/reordered_if_blocks_autogen.cpp" "diff_files/reordered_switch_blocks_autogen.cpp" "diff_files/small_functions_small_diffs_autogen.cpp"
diff --git a/test/diff/diff_files/different_decorations_fragment_autogen.cpp b/test/diff/diff_files/different_decorations_fragment_autogen.cpp index 0d34654..ec9074c 100644 --- a/test/diff/diff_files/different_decorations_fragment_autogen.cpp +++ b/test/diff/diff_files/different_decorations_fragment_autogen.cpp
@@ -977,7 +977,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 82 -+; Bound: 92 ++; Bound: 89 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450 @@ -1030,8 +1030,7 @@ +OpDecorate %83 DescriptorSet 0 +OpDecorate %83 Binding 0 OpDecorate %32 RelaxedPrecision --OpDecorate %33 RelaxedPrecision -+OpDecorate %84 RelaxedPrecision + OpDecorate %33 RelaxedPrecision OpDecorate %36 RelaxedPrecision OpDecorate %37 RelaxedPrecision OpDecorate %38 RelaxedPrecision @@ -1040,10 +1039,8 @@ OpDecorate %42 RelaxedPrecision OpDecorate %43 RelaxedPrecision OpDecorate %48 RelaxedPrecision --OpDecorate %49 RelaxedPrecision --OpDecorate %50 RelaxedPrecision -+OpDecorate %85 RelaxedPrecision -+OpDecorate %86 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision OpDecorate %52 RelaxedPrecision OpDecorate %53 RelaxedPrecision OpDecorate %54 RelaxedPrecision @@ -1082,13 +1079,13 @@ %61 = OpTypeVoid %69 = OpConstant %16 0 %78 = OpConstant %16 1 -+%88 = OpTypePointer Private %2 ++%85 = OpTypePointer Private %2 %3 = OpTypePointer Input %2 %7 = OpTypePointer UniformConstant %6 %10 = OpTypePointer UniformConstant %9 %13 = OpTypePointer Uniform %12 %19 = OpTypePointer Uniform %18 -+%89 = OpTypePointer Private %2 ++%86 = OpTypePointer Private %2 %21 = OpTypePointer Output %2 %28 = OpTypePointer Uniform %27 %30 = OpTypePointer Function %2 @@ -1106,19 +1103,16 @@ %22 = OpVariable %21 Output -%29 = OpVariable %28 Uniform +%83 = OpVariable %28 Uniform -+%90 = OpConstant %23 0 -+%91 = OpConstant %1 0.5 ++%87 = OpConstant %23 0 ++%88 = OpConstant %1 0.5 %32 = OpFunction %2 None %31 --%33 = OpFunctionParameter %30 -+%84 = OpFunctionParameter %30 + %33 = OpFunctionParameter %30 %34 = OpLabel %36 = OpLoad %6 %8 --%37 = OpLoad %2 %33 -+%37 = OpLoad %2 %84 + %37 = OpLoad %2 %33 %38 = OpVectorShuffle %35 %37 %37 0 1 %39 = OpImageSampleImplicitLod %2 %36 %38 --%41 = OpLoad %2 %33 -+%41 = OpLoad %2 %84 + %41 = OpLoad %2 %33 %42 = OpVectorShuffle %35 %41 %41 2 3 %43 = OpConvertFToS %40 %42 %44 = OpLoad %9 %11 @@ -1127,16 +1121,12 @@ OpReturnValue %46 OpFunctionEnd %48 = OpFunction %2 None %47 --%49 = OpFunctionParameter %30 --%50 = OpFunctionParameter %30 -+%85 = OpFunctionParameter %30 -+%86 = OpFunctionParameter %30 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 %51 = OpLabel --%52 = OpLoad %2 %49 -+%52 = OpLoad %2 %85 + %52 = OpLoad %2 %49 %53 = OpVectorShuffle %35 %52 %52 0 1 --%54 = OpLoad %2 %50 -+%54 = OpLoad %2 %86 + %54 = OpLoad %2 %50 %55 = OpVectorShuffle %35 %54 %54 2 3 %56 = OpCompositeExtract %1 %53 0 %57 = OpCompositeExtract %1 %53 1 @@ -1154,9 +1144,9 @@ OpStore %65 %66 %67 = OpFunctionCall %2 %32 %65 -%71 = OpAccessChain %70 %14 %69 -+%87 = OpAccessChain %70 %82 %69 ++%84 = OpAccessChain %70 %82 %69 -%72 = OpLoad %2 %71 -+%72 = OpLoad %2 %87 ++%72 = OpLoad %2 %84 OpStore %68 %72 -%74 = OpAccessChain %70 %20 %69 %69 +%74 = OpAccessChain %70 %14 %69 %69
diff --git a/test/diff/diff_files/different_decorations_vertex_autogen.cpp b/test/diff/diff_files/different_decorations_vertex_autogen.cpp index f65ee5a..134ebb4 100644 --- a/test/diff/diff_files/different_decorations_vertex_autogen.cpp +++ b/test/diff/diff_files/different_decorations_vertex_autogen.cpp
@@ -777,7 +777,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 58 -+; Bound: 79 ++; Bound: 77 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450 @@ -817,12 +817,10 @@ -OpMemberDecorate %23 3 BuiltIn CullDistance OpDecorate %23 Block OpDecorate %28 RelaxedPrecision --OpDecorate %29 RelaxedPrecision -+OpDecorate %59 RelaxedPrecision + OpDecorate %29 RelaxedPrecision OpDecorate %31 RelaxedPrecision OpDecorate %32 RelaxedPrecision --OpDecorate %33 RelaxedPrecision -+OpDecorate %60 RelaxedPrecision + OpDecorate %33 RelaxedPrecision OpDecorate %35 RelaxedPrecision OpDecorate %36 RelaxedPrecision OpDecorate %37 RelaxedPrecision @@ -845,9 +843,9 @@ +%23 = OpTypeStruct %2 %38 = OpTypeVoid %45 = OpConstant %12 0 -+%65 = OpTypePointer Private %2 ++%63 = OpTypePointer Private %2 %3 = OpTypePointer Input %2 -+%66 = OpTypePointer Private %2 ++%64 = OpTypePointer Private %2 %7 = OpTypePointer Output %2 %10 = OpTypePointer Uniform %9 %18 = OpTypePointer Uniform %17 @@ -865,26 +863,21 @@ -%19 = OpVariable %18 Uniform +%19 = OpVariable %10 Uniform %20 = OpVariable %7 Output -+%58 = OpVariable %66 Private ++%58 = OpVariable %64 Private %25 = OpVariable %24 Output -+%67 = OpConstant %13 0 -+%68 = OpConstant %1 0.5 ++%65 = OpConstant %13 0 ++%66 = OpConstant %1 0.5 %28 = OpFunction %2 None %27 --%29 = OpFunctionParameter %26 -+%59 = OpFunctionParameter %26 + %29 = OpFunctionParameter %26 %30 = OpLabel --%31 = OpLoad %2 %29 -+%31 = OpLoad %2 %59 + %31 = OpLoad %2 %29 OpReturnValue %31 OpFunctionEnd %32 = OpFunction %2 None %27 --%33 = OpFunctionParameter %26 -+%60 = OpFunctionParameter %26 + %33 = OpFunctionParameter %26 %34 = OpLabel --%35 = OpLoad %2 %33 -+%35 = OpLoad %2 %60 --%36 = OpLoad %2 %33 -+%36 = OpLoad %2 %60 + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 %37 = OpFAdd %2 %35 %36 OpReturnValue %37 OpFunctionEnd @@ -894,41 +887,41 @@ %50 = OpVariable %26 Function %53 = OpVariable %26 Function -%43 = OpLoad %2 %4 -+%61 = OpLoad %2 %5 ++%59 = OpLoad %2 %5 -OpStore %42 %43 -+OpStore %42 %61 ++OpStore %42 %59 %44 = OpFunctionCall %2 %28 %42 -%47 = OpAccessChain %46 %11 %45 -+%62 = OpAccessChain %46 %19 %45 ++%60 = OpAccessChain %46 %19 %45 -%48 = OpLoad %2 %47 -+%48 = OpLoad %2 %62 ++%48 = OpLoad %2 %60 %49 = OpFAdd %2 %44 %48 -OpStore %8 %49 +OpStore %20 %49 -%51 = OpLoad %2 %5 -+%63 = OpLoad %2 %6 ++%61 = OpLoad %2 %6 -OpStore %50 %51 -+OpStore %50 %63 ++OpStore %50 %61 %52 = OpFunctionCall %2 %32 %50 -%54 = OpLoad %2 %6 -+%64 = OpLoad %2 %4 ++%62 = OpLoad %2 %4 -OpStore %53 %54 -+OpStore %53 %64 ++OpStore %53 %62 %55 = OpFunctionCall %2 %28 %53 %56 = OpFAdd %2 %52 %55 %57 = OpAccessChain %7 %25 %45 OpStore %57 %56 -+%69 = OpAccessChain %7 %25 %67 -+%70 = OpLoad %2 %69 -+%71 = OpCompositeExtract %1 %70 0 -+%72 = OpCompositeExtract %1 %70 1 -+%73 = OpCompositeExtract %1 %70 2 -+%74 = OpCompositeExtract %1 %70 3 -+%76 = OpFNegate %1 %71 -+%77 = OpFAdd %1 %73 %74 -+%78 = OpFMul %1 %77 %68 -+%75 = OpCompositeConstruct %2 %72 %76 %78 %74 -+OpStore %69 %75 ++%67 = OpAccessChain %7 %25 %65 ++%68 = OpLoad %2 %67 ++%69 = OpCompositeExtract %1 %68 0 ++%70 = OpCompositeExtract %1 %68 1 ++%71 = OpCompositeExtract %1 %68 2 ++%72 = OpCompositeExtract %1 %68 3 ++%74 = OpFNegate %1 %69 ++%75 = OpFAdd %1 %71 %72 ++%76 = OpFMul %1 %75 %66 ++%73 = OpCompositeConstruct %2 %70 %74 %76 %72 ++OpStore %67 %73 OpReturn OpFunctionEnd )";
diff --git a/test/diff/diff_files/different_function_parameter_count_autogen.cpp b/test/diff/diff_files/different_function_parameter_count_autogen.cpp index 3a077fb..e31a4a8 100644 --- a/test/diff/diff_files/different_function_parameter_count_autogen.cpp +++ b/test/diff/diff_files/different_function_parameter_count_autogen.cpp
@@ -128,7 +128,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 25 -+; Bound: 33 ++; Bound: 31 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -143,7 +143,7 @@ +OpName %26 "v2" OpName %20 "o" OpName %23 "param" -+OpName %31 "param" ++OpName %29 "param" OpDecorate %20 RelaxedPrecision OpDecorate %20 Location 0 %2 = OpTypeVoid @@ -162,13 +162,13 @@ %4 = OpFunction %2 None %3 %5 = OpLabel %23 = OpVariable %8 Function -+%31 = OpVariable %8 Function ++%29 = OpVariable %8 Function OpStore %23 %22 -%24 = OpFunctionCall %7 %11 %23 -+OpStore %31 %15 -+%32 = OpFunctionCall %7 %11 %23 %31 ++OpStore %29 %15 ++%30 = OpFunctionCall %7 %11 %23 %29 -OpStore %20 %24 -+OpStore %20 %32 ++OpStore %20 %30 OpReturn OpFunctionEnd -%11 = OpFunction %7 None %9 @@ -280,7 +280,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 25 -+; Bound: 34 ++; Bound: 31 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -306,28 +306,26 @@ %4 = OpFunction %2 None %3 %5 = OpLabel %23 = OpVariable %8 Function -+%32 = OpVariable %8 Function ++%29 = OpVariable %8 Function OpStore %23 %22 -%24 = OpFunctionCall %7 %11 %23 -+OpStore %32 %15 -+%33 = OpFunctionCall %7 %11 %23 %32 ++OpStore %29 %15 ++%30 = OpFunctionCall %7 %11 %23 %29 -OpStore %20 %24 -+OpStore %20 %33 ++OpStore %20 %30 OpReturn OpFunctionEnd -%11 = OpFunction %7 None %9 +%11 = OpFunction %7 None %25 --%10 = OpFunctionParameter %8 + %10 = OpFunctionParameter %8 +%26 = OpFunctionParameter %8 -+%27 = OpFunctionParameter %8 %12 = OpLabel --%13 = OpLoad %7 %10 -+%13 = OpLoad %7 %26 + %13 = OpLoad %7 %10 -%16 = OpFAdd %7 %13 %15 -+%28 = OpLoad %7 %27 -+%29 = OpFAdd %7 %13 %28 ++%27 = OpLoad %7 %26 ++%28 = OpFAdd %7 %13 %27 -OpReturnValue %16 -+OpReturnValue %29 ++OpReturnValue %28 OpFunctionEnd )"; Options options;
diff --git a/test/diff/diff_files/extra_if_block_autogen.cpp b/test/diff/diff_files/extra_if_block_autogen.cpp index 4f91319..fee34ae 100644 --- a/test/diff/diff_files/extra_if_block_autogen.cpp +++ b/test/diff/diff_files/extra_if_block_autogen.cpp
@@ -303,7 +303,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 69 -+; Bound: 81 ++; Bound: 77 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -352,10 +352,10 @@ OpDecorate %54 RelaxedPrecision OpDecorate %55 RelaxedPrecision OpDecorate %56 RelaxedPrecision -+OpDecorate %72 RelaxedPrecision ++OpDecorate %70 RelaxedPrecision OpDecorate %57 RelaxedPrecision -+OpDecorate %77 RelaxedPrecision -+OpDecorate %78 RelaxedPrecision ++OpDecorate %75 RelaxedPrecision ++OpDecorate %76 RelaxedPrecision OpDecorate %58 RelaxedPrecision OpDecorate %63 RelaxedPrecision OpDecorate %63 Location 0 @@ -383,7 +383,7 @@ %32 = OpConstant %19 1 %49 = OpConstant %6 10 %52 = OpConstant %6 0.5 -+%76 = OpConstant %6 0.100000001 ++%74 = OpConstant %6 0.100000001 %53 = OpConstant %6 0.699999988 %61 = OpTypeVector %6 4 %62 = OpTypePointer Output %61 @@ -439,20 +439,20 @@ %55 = OpLoad %6 %45 %56 = OpFMul %6 %55 %54 OpStore %45 %56 -+%71 = OpAccessChain %21 %18 %32 -+%72 = OpLoad %15 %71 -+%73 = OpINotEqual %25 %72 %24 -+OpSelectionMerge %75 None -+OpBranchConditional %73 %74 %75 -+%74 = OpLabel ++%69 = OpAccessChain %21 %18 %32 ++%70 = OpLoad %15 %69 ++%71 = OpINotEqual %25 %70 %24 ++OpSelectionMerge %73 None ++OpBranchConditional %71 %72 %73 ++%72 = OpLabel %57 = OpLoad %6 %45 -+%77 = OpFSub %6 %57 %76 -+OpStore %45 %77 -+OpBranch %75 -+%75 = OpLabel -+%78 = OpLoad %6 %45 ++%75 = OpFSub %6 %57 %74 ++OpStore %45 %75 ++OpBranch %73 ++%73 = OpLabel ++%76 = OpLoad %6 %45 -%58 = OpExtInst %6 %1 Exp %57 -+%58 = OpExtInst %6 %1 Exp %78 ++%58 = OpExtInst %6 %1 Exp %76 OpReturnValue %58 OpFunctionEnd )"; @@ -716,7 +716,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 69 -+; Bound: 81 ++; Bound: 77 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -754,10 +754,10 @@ OpDecorate %54 RelaxedPrecision OpDecorate %55 RelaxedPrecision OpDecorate %56 RelaxedPrecision -+OpDecorate %72 RelaxedPrecision ++OpDecorate %70 RelaxedPrecision OpDecorate %57 RelaxedPrecision -+OpDecorate %77 RelaxedPrecision -+OpDecorate %78 RelaxedPrecision ++OpDecorate %75 RelaxedPrecision ++OpDecorate %76 RelaxedPrecision OpDecorate %58 RelaxedPrecision OpDecorate %63 RelaxedPrecision OpDecorate %63 Location 0 @@ -785,7 +785,7 @@ %32 = OpConstant %19 1 %49 = OpConstant %6 10 %52 = OpConstant %6 0.5 -+%76 = OpConstant %6 0.100000001 ++%74 = OpConstant %6 0.100000001 %53 = OpConstant %6 0.699999988 %61 = OpTypeVector %6 4 %62 = OpTypePointer Output %61 @@ -841,20 +841,20 @@ %55 = OpLoad %6 %45 %56 = OpFMul %6 %55 %54 OpStore %45 %56 -+%71 = OpAccessChain %21 %18 %32 -+%72 = OpLoad %15 %71 -+%73 = OpINotEqual %25 %72 %24 -+OpSelectionMerge %75 None -+OpBranchConditional %73 %74 %75 -+%74 = OpLabel ++%69 = OpAccessChain %21 %18 %32 ++%70 = OpLoad %15 %69 ++%71 = OpINotEqual %25 %70 %24 ++OpSelectionMerge %73 None ++OpBranchConditional %71 %72 %73 ++%72 = OpLabel %57 = OpLoad %6 %45 -+%77 = OpFSub %6 %57 %76 -+OpStore %45 %77 -+OpBranch %75 -+%75 = OpLabel -+%78 = OpLoad %6 %45 ++%75 = OpFSub %6 %57 %74 ++OpStore %45 %75 ++OpBranch %73 ++%73 = OpLabel ++%76 = OpLoad %6 %45 -%58 = OpExtInst %6 %1 Exp %57 -+%58 = OpExtInst %6 %1 Exp %78 ++%58 = OpExtInst %6 %1 Exp %76 OpReturnValue %58 OpFunctionEnd )";
diff --git a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp index 187722e..11bb811 100644 --- a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp +++ b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp
@@ -371,10 +371,10 @@ 3 -> 16 [TypePointer] 4 -> 17 [Variable] 5 -> 8 [TypeInt] - 8 -> 23 [TypeVector] + 8 -> 21 [TypeVector] 13 -> 19 [TypePointer] - 15 -> 29 [Constant] - 16 -> 30 [TypeArray] + 15 -> 22 [Constant] + 16 -> 23 [TypeArray] 17 -> 11 [TypeStruct] 18 -> 12 [TypePointer] 19 -> 13 [Variable]
diff --git a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp index 9d01166..00bee6b 100644 --- a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp +++ b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp
@@ -125,9 +125,8 @@ OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -+OpEntryPoint Vertex %12 "main2" %13 %14 %15 OpEntryPoint Vertex %4 "main1" %8 %10 --OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 OpSource ESSL 310 OpName %4 "main1" OpName %12 "main2" @@ -257,9 +256,8 @@ OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -+OpEntryPoint Vertex %12 "main2" %13 %14 %15 OpEntryPoint Vertex %4 "main1" %8 %10 --OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 OpSource ESSL 310 OpDecorate %8 Location 0 OpDecorate %10 Location 0 @@ -304,9 +302,8 @@ OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -+OpEntryPoint Vertex %12 "main2" %13 %14 %15 OpEntryPoint Vertex %4 "main1" %8 %10 --OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 OpSource ESSL 310 OpName %4 "main1" OpName %12 "main2"
diff --git a/test/diff/diff_files/ray_query_types_autogen.cpp b/test/diff/diff_files/ray_query_types_autogen.cpp new file mode 100644 index 0000000..5507def --- /dev/null +++ b/test/diff/diff_files/ray_query_types_autogen.cpp
@@ -0,0 +1,148 @@ +// 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 { + +// Test that OpTypeAccelerationStructureNV and OpTypeRayQueryKHR are +// matched. +constexpr char kSrc[] = R"(OpCapability RayQueryKHR +OpCapability Shader +OpExtension "SPV_KHR_ray_query" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" +OpExecutionMode %43 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeAccelerationStructureNV +%13 = OpTypeRayQueryKHR +%44 = OpTypeFunction %2 +%43 = OpFunction %2 None %44 +%42 = OpLabel +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.4 +; Generator: rspirv +; Bound: 95 +OpCapability RayQueryKHR +OpCapability Shader +OpExtension "SPV_KHR_ray_query" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" +OpExecutionMode %43 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeAccelerationStructureNV +%13 = OpTypeRayQueryKHR +%44 = OpTypeFunction %2 +%43 = OpFunction %2 None %44 +%42 = OpLabel +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, RayQueryTypes) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 45 + ; Schema: 0 + OpCapability RayQueryKHR + OpCapability Shader + OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %43 "main" + OpExecutionMode %43 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeAccelerationStructureKHR + %13 = OpTypeRayQueryKHR + %44 = OpTypeFunction %2 + %43 = OpFunction %2 None %44 + %42 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, RayQueryTypesNoDebug) { + constexpr char kSrcNoDebug[] = R"(OpCapability RayQueryKHR +OpCapability Shader +OpExtension "SPV_KHR_ray_query" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" +OpExecutionMode %43 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeAccelerationStructureNV +%13 = OpTypeRayQueryKHR +%44 = OpTypeFunction %2 +%43 = OpFunction %2 None %44 +%42 = OpLabel +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.4 +; Generator: rspirv +; Bound: 95 +OpCapability RayQueryKHR +OpCapability Shader +OpExtension "SPV_KHR_ray_query" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" +OpExecutionMode %43 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeAccelerationStructureNV +%13 = OpTypeRayQueryKHR +%44 = OpTypeFunction %2 +%43 = OpFunction %2 None %44 +%42 = OpLabel +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 45 + ; Schema: 0 + OpCapability RayQueryKHR + OpCapability Shader + OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %43 "main" + OpExecutionMode %43 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeAccelerationStructureKHR + %13 = OpTypeRayQueryKHR + %44 = OpTypeFunction %2 + %43 = OpFunction %2 None %44 + %42 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools
diff --git a/test/diff/diff_files/ray_query_types_dst.spvasm b/test/diff/diff_files/ray_query_types_dst.spvasm new file mode 100644 index 0000000..5f8be53 --- /dev/null +++ b/test/diff/diff_files/ray_query_types_dst.spvasm
@@ -0,0 +1,18 @@ +; SPIR-V +; Version: 1.4 +; Generator: rspirv +; Bound: 95 +OpCapability RayQueryKHR +OpCapability Shader +OpExtension "SPV_KHR_ray_query" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" +OpExecutionMode %43 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeAccelerationStructureNV +%13 = OpTypeRayQueryKHR +%44 = OpTypeFunction %2 +%43 = OpFunction %2 None %44 +%42 = OpLabel +OpReturn +OpFunctionEnd
diff --git a/test/diff/diff_files/ray_query_types_src.spvasm b/test/diff/diff_files/ray_query_types_src.spvasm new file mode 100644 index 0000000..0b64015 --- /dev/null +++ b/test/diff/diff_files/ray_query_types_src.spvasm
@@ -0,0 +1,16 @@ +;; Test that OpTypeAccelerationStructureNV and OpTypeRayQueryKHR are +;; matched. +OpCapability RayQueryKHR +OpCapability Shader +OpExtension "SPV_KHR_ray_query" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %43 "main" +OpExecutionMode %43 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeAccelerationStructureNV +%13 = OpTypeRayQueryKHR +%44 = OpTypeFunction %2 +%43 = OpFunction %2 None %44 +%42 = OpLabel +OpReturn +OpFunctionEnd
diff --git a/test/diff/diff_files/reordered_if_blocks_autogen.cpp b/test/diff/diff_files/reordered_if_blocks_autogen.cpp index 0788199..3abaf40 100644 --- a/test/diff/diff_files/reordered_if_blocks_autogen.cpp +++ b/test/diff/diff_files/reordered_if_blocks_autogen.cpp
@@ -203,8 +203,7 @@ constexpr char kDiff[] = R"( ; SPIR-V ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 --; Bound: 46 -+; Bound: 47 + ; Bound: 46 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -471,8 +470,7 @@ constexpr char kDiff[] = R"( ; SPIR-V ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 --; Bound: 46 -+; Bound: 47 + ; Bound: 46 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450"
diff --git a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp index c0ba48d..ade5350 100644 --- a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp +++ b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp
@@ -212,8 +212,7 @@ constexpr char kDiff[] = R"( ; SPIR-V ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 --; Bound: 58 -+; Bound: 62 + ; Bound: 58 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -485,8 +484,7 @@ constexpr char kDiff[] = R"( ; SPIR-V ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 --; Bound: 58 -+; Bound: 62 + ; Bound: 58 ; Schema: 0 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450"
diff --git a/test/diff/diff_files/spec_constant_array_size_autogen.cpp b/test/diff/diff_files/spec_constant_array_size_autogen.cpp index 1962d27..98ad072 100644 --- a/test/diff/diff_files/spec_constant_array_size_autogen.cpp +++ b/test/diff/diff_files/spec_constant_array_size_autogen.cpp
@@ -125,7 +125,7 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 36 ++; Bound: 29 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450 @@ -140,7 +140,7 @@ OpName %19 "" OpName %22 "main" OpDecorate %4 Location 0 -+OpDecorate %34 SpecId 4 ++OpDecorate %27 SpecId 4 OpMemberDecorate %17 1 RelaxedPrecision OpMemberDecorate %17 0 BuiltIn Position OpMemberDecorate %17 1 BuiltIn PointSize @@ -153,10 +153,10 @@ %8 = OpTypeVector %5 4 -%15 = OpConstant %5 8 -%16 = OpTypeArray %1 %15 -+%34 = OpSpecConstant %5 8 -+%35 = OpTypeArray %1 %34 ++%27 = OpSpecConstant %5 8 ++%28 = OpTypeArray %1 %27 -%17 = OpTypeStruct %2 %1 %16 %16 -+%17 = OpTypeStruct %2 %1 %35 %35 ++%17 = OpTypeStruct %2 %1 %28 %28 %20 = OpTypeVoid %25 = OpConstant %5 0 %3 = OpTypePointer Input %2 @@ -261,14 +261,14 @@ ; Version: 1.6 ; Generator: Khronos SPIR-V Tools Assembler; 0 -; Bound: 27 -+; Bound: 36 ++; Bound: 29 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %22 "main" %4 %19 OpSource GLSL 450 OpDecorate %4 Location 0 -+OpDecorate %34 SpecId 4 ++OpDecorate %27 SpecId 4 OpMemberDecorate %17 1 RelaxedPrecision OpMemberDecorate %17 0 BuiltIn Position OpMemberDecorate %17 1 BuiltIn PointSize @@ -281,10 +281,10 @@ %8 = OpTypeVector %5 4 -%15 = OpConstant %5 8 -%16 = OpTypeArray %1 %15 -+%34 = OpSpecConstant %5 8 -+%35 = OpTypeArray %1 %34 ++%27 = OpSpecConstant %5 8 ++%28 = OpTypeArray %1 %27 -%17 = OpTypeStruct %2 %1 %16 %16 -+%17 = OpTypeStruct %2 %1 %35 %35 ++%17 = OpTypeStruct %2 %1 %28 %28 %20 = OpTypeVoid %25 = OpConstant %5 0 %3 = OpTypePointer Input %2
diff --git a/test/enum_set_test.cpp b/test/enum_set_test.cpp index 1f72715..11105f9 100644 --- a/test/enum_set_test.cpp +++ b/test/enum_set_test.cpp
@@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "source/enum_set.h" + #include <algorithm> +#include <array> +#include <random> #include <utility> #include <vector> #include "gmock/gmock.h" -#include "source/enum_set.h" #include "test/unit_spirv.h" namespace spvtools { @@ -25,208 +28,800 @@ using spvtest::ElementsIn; using ::testing::Eq; +using ::testing::Values; using ::testing::ValuesIn; +enum class TestEnum : uint32_t { + ZERO = 0, + ONE = 1, + TWO = 2, + THREE = 3, + FOUR = 4, + FIVE = 5, + EIGHT = 8, + TWENTY = 20, + TWENTY_FOUR = 24, + THIRTY = 30, + ONE_HUNDRED = 100, + ONE_HUNDRED_FIFTY = 150, + TWO_HUNDRED = 200, + THREE_HUNDRED = 300, + FOUR_HUNDRED = 400, + FIVE_HUNDRED = 500, + SIX_HUNDRED = 600, +}; + +constexpr std::array kCapabilities{ + spv::Capability::Matrix, + spv::Capability::Shader, + spv::Capability::Geometry, + spv::Capability::Tessellation, + spv::Capability::Addresses, + spv::Capability::Linkage, + spv::Capability::Kernel, + spv::Capability::Vector16, + spv::Capability::Float16Buffer, + spv::Capability::Float16, + spv::Capability::Float64, + spv::Capability::Int64, + spv::Capability::Int64Atomics, + spv::Capability::ImageBasic, + spv::Capability::ImageReadWrite, + spv::Capability::ImageMipmap, + spv::Capability::Pipes, + spv::Capability::Groups, + spv::Capability::DeviceEnqueue, + spv::Capability::LiteralSampler, + spv::Capability::AtomicStorage, + spv::Capability::Int16, + spv::Capability::TessellationPointSize, + spv::Capability::GeometryPointSize, + spv::Capability::ImageGatherExtended, + spv::Capability::StorageImageMultisample, + spv::Capability::UniformBufferArrayDynamicIndexing, + spv::Capability::SampledImageArrayDynamicIndexing, + spv::Capability::StorageBufferArrayDynamicIndexing, + spv::Capability::StorageImageArrayDynamicIndexing, + spv::Capability::ClipDistance, + spv::Capability::CullDistance, + spv::Capability::ImageCubeArray, + spv::Capability::SampleRateShading, + spv::Capability::ImageRect, + spv::Capability::SampledRect, + spv::Capability::GenericPointer, + spv::Capability::Int8, + spv::Capability::InputAttachment, + spv::Capability::SparseResidency, + spv::Capability::MinLod, + spv::Capability::Sampled1D, + spv::Capability::Image1D, + spv::Capability::SampledCubeArray, + spv::Capability::SampledBuffer, + spv::Capability::ImageBuffer, + spv::Capability::ImageMSArray, + spv::Capability::StorageImageExtendedFormats, + spv::Capability::ImageQuery, + spv::Capability::DerivativeControl, + spv::Capability::InterpolationFunction, + spv::Capability::TransformFeedback, + spv::Capability::GeometryStreams, + spv::Capability::StorageImageReadWithoutFormat, + spv::Capability::StorageImageWriteWithoutFormat, + spv::Capability::MultiViewport, + spv::Capability::SubgroupDispatch, + spv::Capability::NamedBarrier, + spv::Capability::PipeStorage, + spv::Capability::GroupNonUniform, + spv::Capability::GroupNonUniformVote, + spv::Capability::GroupNonUniformArithmetic, + spv::Capability::GroupNonUniformBallot, + spv::Capability::GroupNonUniformShuffle, + spv::Capability::GroupNonUniformShuffleRelative, + spv::Capability::GroupNonUniformClustered, + spv::Capability::GroupNonUniformQuad, + spv::Capability::ShaderLayer, + spv::Capability::ShaderViewportIndex, + spv::Capability::UniformDecoration, + spv::Capability::CoreBuiltinsARM, + spv::Capability::FragmentShadingRateKHR, + spv::Capability::SubgroupBallotKHR, + spv::Capability::DrawParameters, + spv::Capability::WorkgroupMemoryExplicitLayoutKHR, + spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR, + spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR, + spv::Capability::SubgroupVoteKHR, + spv::Capability::StorageBuffer16BitAccess, + spv::Capability::StorageUniformBufferBlock16, + spv::Capability::StorageUniform16, + spv::Capability::UniformAndStorageBuffer16BitAccess, + spv::Capability::StoragePushConstant16, + spv::Capability::StorageInputOutput16, + spv::Capability::DeviceGroup, + spv::Capability::MultiView, + spv::Capability::VariablePointersStorageBuffer, + spv::Capability::VariablePointers, + spv::Capability::AtomicStorageOps, + spv::Capability::SampleMaskPostDepthCoverage, + spv::Capability::StorageBuffer8BitAccess, + spv::Capability::UniformAndStorageBuffer8BitAccess, + spv::Capability::StoragePushConstant8, + spv::Capability::DenormPreserve, + spv::Capability::DenormFlushToZero, + spv::Capability::SignedZeroInfNanPreserve, + spv::Capability::RoundingModeRTE, + spv::Capability::RoundingModeRTZ, + spv::Capability::RayQueryProvisionalKHR, + spv::Capability::RayQueryKHR, + spv::Capability::RayTraversalPrimitiveCullingKHR, + spv::Capability::RayTracingKHR, + spv::Capability::Float16ImageAMD, + spv::Capability::ImageGatherBiasLodAMD, + spv::Capability::FragmentMaskAMD, + spv::Capability::StencilExportEXT, + spv::Capability::ImageReadWriteLodAMD, + spv::Capability::Int64ImageEXT, + spv::Capability::ShaderClockKHR, + spv::Capability::SampleMaskOverrideCoverageNV, + spv::Capability::GeometryShaderPassthroughNV, + spv::Capability::ShaderViewportIndexLayerEXT, + spv::Capability::ShaderViewportIndexLayerNV, + spv::Capability::ShaderViewportMaskNV, + spv::Capability::ShaderStereoViewNV, + spv::Capability::PerViewAttributesNV, + spv::Capability::FragmentFullyCoveredEXT, + spv::Capability::MeshShadingNV, + spv::Capability::ImageFootprintNV, + spv::Capability::MeshShadingEXT, + spv::Capability::FragmentBarycentricKHR, + spv::Capability::FragmentBarycentricNV, + spv::Capability::ComputeDerivativeGroupQuadsNV, + spv::Capability::FragmentDensityEXT, + spv::Capability::ShadingRateNV, + spv::Capability::GroupNonUniformPartitionedNV, + spv::Capability::ShaderNonUniform, + spv::Capability::ShaderNonUniformEXT, + spv::Capability::RuntimeDescriptorArray, + spv::Capability::RuntimeDescriptorArrayEXT, + spv::Capability::InputAttachmentArrayDynamicIndexing, + spv::Capability::InputAttachmentArrayDynamicIndexingEXT, + spv::Capability::UniformTexelBufferArrayDynamicIndexing, + spv::Capability::UniformTexelBufferArrayDynamicIndexingEXT, + spv::Capability::StorageTexelBufferArrayDynamicIndexing, + spv::Capability::StorageTexelBufferArrayDynamicIndexingEXT, + spv::Capability::UniformBufferArrayNonUniformIndexing, + spv::Capability::UniformBufferArrayNonUniformIndexingEXT, + spv::Capability::SampledImageArrayNonUniformIndexing, + spv::Capability::SampledImageArrayNonUniformIndexingEXT, + spv::Capability::StorageBufferArrayNonUniformIndexing, + spv::Capability::StorageBufferArrayNonUniformIndexingEXT, + spv::Capability::StorageImageArrayNonUniformIndexing, + spv::Capability::StorageImageArrayNonUniformIndexingEXT, + spv::Capability::InputAttachmentArrayNonUniformIndexing, + spv::Capability::InputAttachmentArrayNonUniformIndexingEXT, + spv::Capability::UniformTexelBufferArrayNonUniformIndexing, + spv::Capability::UniformTexelBufferArrayNonUniformIndexingEXT, + spv::Capability::StorageTexelBufferArrayNonUniformIndexing, + spv::Capability::StorageTexelBufferArrayNonUniformIndexingEXT, + spv::Capability::RayTracingNV, + spv::Capability::RayTracingMotionBlurNV, + spv::Capability::VulkanMemoryModel, + spv::Capability::VulkanMemoryModelKHR, + spv::Capability::VulkanMemoryModelDeviceScope, + spv::Capability::VulkanMemoryModelDeviceScopeKHR, + spv::Capability::PhysicalStorageBufferAddresses, + spv::Capability::PhysicalStorageBufferAddressesEXT, + spv::Capability::ComputeDerivativeGroupLinearNV, + spv::Capability::RayTracingProvisionalKHR, + spv::Capability::CooperativeMatrixNV, + spv::Capability::FragmentShaderSampleInterlockEXT, + spv::Capability::FragmentShaderShadingRateInterlockEXT, + spv::Capability::ShaderSMBuiltinsNV, + spv::Capability::FragmentShaderPixelInterlockEXT, + spv::Capability::DemoteToHelperInvocation, + spv::Capability::DemoteToHelperInvocationEXT, + spv::Capability::RayTracingOpacityMicromapEXT, + spv::Capability::ShaderInvocationReorderNV, + spv::Capability::BindlessTextureNV, + spv::Capability::SubgroupShuffleINTEL, + spv::Capability::SubgroupBufferBlockIOINTEL, + spv::Capability::SubgroupImageBlockIOINTEL, + spv::Capability::SubgroupImageMediaBlockIOINTEL, + spv::Capability::RoundToInfinityINTEL, + spv::Capability::FloatingPointModeINTEL, + spv::Capability::IntegerFunctions2INTEL, + spv::Capability::FunctionPointersINTEL, + spv::Capability::IndirectReferencesINTEL, + spv::Capability::AsmINTEL, + spv::Capability::AtomicFloat32MinMaxEXT, + spv::Capability::AtomicFloat64MinMaxEXT, + spv::Capability::AtomicFloat16MinMaxEXT, + spv::Capability::VectorComputeINTEL, + spv::Capability::VectorAnyINTEL, + spv::Capability::ExpectAssumeKHR, + spv::Capability::SubgroupAvcMotionEstimationINTEL, + spv::Capability::SubgroupAvcMotionEstimationIntraINTEL, + spv::Capability::SubgroupAvcMotionEstimationChromaINTEL, + spv::Capability::VariableLengthArrayINTEL, + spv::Capability::FunctionFloatControlINTEL, + spv::Capability::FPGAMemoryAttributesINTEL, + spv::Capability::FPFastMathModeINTEL, + spv::Capability::ArbitraryPrecisionIntegersINTEL, + spv::Capability::ArbitraryPrecisionFloatingPointINTEL, + spv::Capability::UnstructuredLoopControlsINTEL, + spv::Capability::FPGALoopControlsINTEL, + spv::Capability::KernelAttributesINTEL, + spv::Capability::FPGAKernelAttributesINTEL, + spv::Capability::FPGAMemoryAccessesINTEL, + spv::Capability::FPGAClusterAttributesINTEL, + spv::Capability::LoopFuseINTEL, + spv::Capability::FPGADSPControlINTEL, + spv::Capability::MemoryAccessAliasingINTEL, + spv::Capability::FPGAInvocationPipeliningAttributesINTEL, + spv::Capability::FPGABufferLocationINTEL, + spv::Capability::ArbitraryPrecisionFixedPointINTEL, + spv::Capability::USMStorageClassesINTEL, + spv::Capability::RuntimeAlignedAttributeINTEL, + spv::Capability::IOPipesINTEL, + spv::Capability::BlockingPipesINTEL, + spv::Capability::FPGARegINTEL, + spv::Capability::DotProductInputAll, + spv::Capability::DotProductInputAllKHR, + spv::Capability::DotProductInput4x8Bit, + spv::Capability::DotProductInput4x8BitKHR, + spv::Capability::DotProductInput4x8BitPacked, + spv::Capability::DotProductInput4x8BitPackedKHR, + spv::Capability::DotProduct, + spv::Capability::DotProductKHR, + spv::Capability::RayCullMaskKHR, + spv::Capability::BitInstructions, + spv::Capability::GroupNonUniformRotateKHR, + spv::Capability::AtomicFloat32AddEXT, + spv::Capability::AtomicFloat64AddEXT, + spv::Capability::LongCompositesINTEL, + spv::Capability::OptNoneINTEL, + spv::Capability::AtomicFloat16AddEXT, + spv::Capability::DebugInfoModuleINTEL, + spv::Capability::SplitBarrierINTEL, + spv::Capability::GroupUniformArithmeticKHR, + spv::Capability::Max, +}; + +namespace { +std::vector<TestEnum> enumerateValuesFromToWithStep(size_t start, size_t end, + size_t step) { + assert(end > start && "end > start"); + std::vector<TestEnum> orderedValues; + for (size_t i = start; i < end; i += step) { + orderedValues.push_back(static_cast<TestEnum>(i)); + } + return orderedValues; +} + +EnumSet<TestEnum> createSetUnorderedInsertion( + const std::vector<TestEnum>& values) { + std::vector shuffledValues(values.cbegin(), values.cend()); + std::mt19937 rng(0); + std::shuffle(shuffledValues.begin(), shuffledValues.end(), rng); + EnumSet<TestEnum> set; + for (auto value : shuffledValues) { + set.insert(value); + } + return set; +} +} // namespace + TEST(EnumSet, IsEmpty1) { - EnumSet<uint32_t> set; - EXPECT_TRUE(set.IsEmpty()); - set.Add(0); - EXPECT_FALSE(set.IsEmpty()); + EnumSet<TestEnum> set; + EXPECT_TRUE(set.empty()); + set.insert(TestEnum::ZERO); + EXPECT_FALSE(set.empty()); } TEST(EnumSet, IsEmpty2) { - EnumSet<uint32_t> set; - EXPECT_TRUE(set.IsEmpty()); - set.Add(150); - EXPECT_FALSE(set.IsEmpty()); + EnumSet<TestEnum> set; + EXPECT_TRUE(set.empty()); + set.insert(TestEnum::ONE_HUNDRED_FIFTY); + EXPECT_FALSE(set.empty()); } TEST(EnumSet, IsEmpty3) { - EnumSet<uint32_t> set(4); - EXPECT_FALSE(set.IsEmpty()); + EnumSet<TestEnum> set(TestEnum::FOUR); + EXPECT_FALSE(set.empty()); } TEST(EnumSet, IsEmpty4) { - EnumSet<uint32_t> set(300); - EXPECT_FALSE(set.IsEmpty()); + EnumSet<TestEnum> set(TestEnum::THREE_HUNDRED); + EXPECT_FALSE(set.empty()); } TEST(EnumSetHasAnyOf, EmptySetEmptyQuery) { - const EnumSet<uint32_t> set; - const EnumSet<uint32_t> empty; + const EnumSet<TestEnum> set; + const EnumSet<TestEnum> empty; EXPECT_TRUE(set.HasAnyOf(empty)); - EXPECT_TRUE(EnumSet<uint32_t>().HasAnyOf(EnumSet<uint32_t>())); + EXPECT_TRUE(EnumSet<TestEnum>().HasAnyOf(EnumSet<TestEnum>())); } TEST(EnumSetHasAnyOf, MaskSetEmptyQuery) { - EnumSet<uint32_t> set; - const EnumSet<uint32_t> empty; - set.Add(5); - set.Add(8); + EnumSet<TestEnum> set; + const EnumSet<TestEnum> empty; + set.insert(TestEnum::FIVE); + set.insert(TestEnum::EIGHT); EXPECT_TRUE(set.HasAnyOf(empty)); } TEST(EnumSetHasAnyOf, OverflowSetEmptyQuery) { - EnumSet<uint32_t> set; - const EnumSet<uint32_t> empty; - set.Add(200); - set.Add(300); + EnumSet<TestEnum> set; + const EnumSet<TestEnum> empty; + set.insert(TestEnum::TWO_HUNDRED); + set.insert(TestEnum::THREE_HUNDRED); EXPECT_TRUE(set.HasAnyOf(empty)); } TEST(EnumSetHasAnyOf, EmptyQuery) { - EnumSet<uint32_t> set; - const EnumSet<uint32_t> empty; - set.Add(5); - set.Add(8); - set.Add(200); - set.Add(300); + EnumSet<TestEnum> set; + const EnumSet<TestEnum> empty; + set.insert(TestEnum::FIVE); + set.insert(TestEnum::EIGHT); + set.insert(TestEnum::TWO_HUNDRED); + set.insert(TestEnum::THREE_HUNDRED); EXPECT_TRUE(set.HasAnyOf(empty)); } TEST(EnumSetHasAnyOf, EmptyQueryAlwaysTrue) { - EnumSet<uint32_t> set; - const EnumSet<uint32_t> empty; + EnumSet<TestEnum> set; + const EnumSet<TestEnum> empty; EXPECT_TRUE(set.HasAnyOf(empty)); - set.Add(5); + set.insert(TestEnum::FIVE); EXPECT_TRUE(set.HasAnyOf(empty)); - EXPECT_TRUE(EnumSet<uint32_t>(100).HasAnyOf(EnumSet<uint32_t>())); + EXPECT_TRUE( + EnumSet<TestEnum>(TestEnum::ONE_HUNDRED).HasAnyOf(EnumSet<TestEnum>())); } TEST(EnumSetHasAnyOf, ReflexiveMask) { - EnumSet<uint32_t> set(3); - set.Add(24); - set.Add(30); + EnumSet<TestEnum> set(TestEnum::THREE); + set.insert(TestEnum::TWENTY_FOUR); + set.insert(TestEnum::THIRTY); EXPECT_TRUE(set.HasAnyOf(set)); } TEST(EnumSetHasAnyOf, ReflexiveOverflow) { - EnumSet<uint32_t> set(200); - set.Add(300); - set.Add(400); + EnumSet<TestEnum> set(TestEnum::TWO_HUNDRED); + set.insert(TestEnum::TWO_HUNDRED); + set.insert(TestEnum::FOUR_HUNDRED); EXPECT_TRUE(set.HasAnyOf(set)); } TEST(EnumSetHasAnyOf, Reflexive) { - EnumSet<uint32_t> set(3); - set.Add(24); - set.Add(300); - set.Add(400); + EnumSet<TestEnum> set(TestEnum::THREE); + set.insert(TestEnum::TWENTY_FOUR); + set.insert(TestEnum::THREE_HUNDRED); + set.insert(TestEnum::FOUR_HUNDRED); EXPECT_TRUE(set.HasAnyOf(set)); } TEST(EnumSetHasAnyOf, EmptySetHasNone) { - EnumSet<uint32_t> set; - EnumSet<uint32_t> items; + EnumSet<TestEnum> set; + EnumSet<TestEnum> items; for (uint32_t i = 0; i < 200; ++i) { - items.Add(i); + TestEnum enumValue = static_cast<TestEnum>(i); + items.insert(enumValue); EXPECT_FALSE(set.HasAnyOf(items)); - EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(i))); + EXPECT_FALSE(set.HasAnyOf(EnumSet<TestEnum>(enumValue))); } } TEST(EnumSetHasAnyOf, MaskSetMaskQuery) { - EnumSet<uint32_t> set(0); - EnumSet<uint32_t> items(1); + EnumSet<TestEnum> set(TestEnum::ZERO); + EnumSet<TestEnum> items(TestEnum::ONE); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(2); - items.Add(3); + set.insert(TestEnum::TWO); + items.insert(TestEnum::THREE); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(3); + set.insert(TestEnum::THREE); EXPECT_TRUE(set.HasAnyOf(items)); - set.Add(4); + set.insert(TestEnum::FOUR); EXPECT_TRUE(set.HasAnyOf(items)); } TEST(EnumSetHasAnyOf, OverflowSetOverflowQuery) { - EnumSet<uint32_t> set(100); - EnumSet<uint32_t> items(200); + EnumSet<TestEnum> set(TestEnum::ONE_HUNDRED); + EnumSet<TestEnum> items(TestEnum::TWO_HUNDRED); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(300); - items.Add(400); + set.insert(TestEnum::THREE_HUNDRED); + items.insert(TestEnum::FOUR_HUNDRED); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(200); + set.insert(TestEnum::TWO_HUNDRED); EXPECT_TRUE(set.HasAnyOf(items)); - set.Add(500); + set.insert(TestEnum::FIVE_HUNDRED); EXPECT_TRUE(set.HasAnyOf(items)); } TEST(EnumSetHasAnyOf, GeneralCase) { - EnumSet<uint32_t> set(0); - EnumSet<uint32_t> items(100); + EnumSet<TestEnum> set(TestEnum::ZERO); + EnumSet<TestEnum> items(TestEnum::ONE_HUNDRED); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(300); - items.Add(4); + set.insert(TestEnum::THREE_HUNDRED); + items.insert(TestEnum::FOUR); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(5); - items.Add(500); + set.insert(TestEnum::FIVE); + items.insert(TestEnum::FIVE_HUNDRED); EXPECT_FALSE(set.HasAnyOf(items)); - set.Add(500); + set.insert(TestEnum::FIVE_HUNDRED); EXPECT_TRUE(set.HasAnyOf(items)); - EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(20))); - EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(600))); - EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(5))); - EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(300))); - EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(0))); + EXPECT_FALSE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::TWENTY))); + EXPECT_FALSE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::SIX_HUNDRED))); + EXPECT_TRUE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::FIVE))); + EXPECT_TRUE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::THREE_HUNDRED))); + EXPECT_TRUE(set.HasAnyOf(EnumSet<TestEnum>(TestEnum::ZERO))); } TEST(EnumSet, DefaultIsEmpty) { - EnumSet<uint32_t> set; + EnumSet<TestEnum> set; for (uint32_t i = 0; i < 1000; ++i) { - EXPECT_FALSE(set.Contains(i)); + EXPECT_FALSE(set.contains(static_cast<TestEnum>(i))); + } +} + +TEST(EnumSet, EqualityCompareEmpty) { + EnumSet<TestEnum> set1; + EnumSet<TestEnum> set2; + + EXPECT_TRUE(set1 == set2); + EXPECT_FALSE(set1 != set2); +} + +TEST(EnumSet, EqualityCompareSame) { + EnumSet<TestEnum> set1; + EnumSet<TestEnum> set2; + + set1.insert(TestEnum::ONE); + set1.insert(TestEnum::TWENTY); + set2.insert(TestEnum::TWENTY); + set2.insert(TestEnum::ONE); + + EXPECT_TRUE(set1 == set2); + EXPECT_FALSE(set1 != set2); +} + +TEST(EnumSet, EqualityCompareDifferent) { + EnumSet<TestEnum> set1; + EnumSet<TestEnum> set2; + + set1.insert(TestEnum::ONE); + set1.insert(TestEnum::TWENTY); + set2.insert(TestEnum::FIVE); + set2.insert(TestEnum::ONE); + + EXPECT_FALSE(set1 == set2); + EXPECT_TRUE(set1 != set2); +} + +TEST(EnumSet, ConstructFromIterators) { + auto orderedValues = enumerateValuesFromToWithStep(0, 2, /* step= */ 1); + EnumSet<TestEnum> set1 = createSetUnorderedInsertion(orderedValues); + + EnumSet<TestEnum> set2(orderedValues.cbegin(), orderedValues.cend()); + + EXPECT_EQ(set1, set2); +} + +TEST(EnumSet, InsertUsingIteratorRange) { + auto orderedValues = enumerateValuesFromToWithStep(0, 2, /* step= */ 1); + EnumSet<TestEnum> set1 = createSetUnorderedInsertion(orderedValues); + + EnumSet<TestEnum> set2; + set2.insert(orderedValues.cbegin(), orderedValues.cend()); + + EXPECT_EQ(set1, set2); +} + +TEST(CapabilitySet, RangeBasedLoopOrderIsEnumOrder) { + auto orderedValues = enumerateValuesFromToWithStep(0, 2, /* step= */ 1); + auto set = createSetUnorderedInsertion(orderedValues); + + size_t index = 0; + for (auto value : set) { + ASSERT_THAT(value, Eq(orderedValues[index])); + index++; } } TEST(CapabilitySet, ConstructSingleMemberMatrix) { CapabilitySet s(spv::Capability::Matrix); - EXPECT_TRUE(s.Contains(spv::Capability::Matrix)); - EXPECT_FALSE(s.Contains(spv::Capability::Shader)); - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(1000))); + EXPECT_TRUE(s.contains(spv::Capability::Matrix)); + EXPECT_FALSE(s.contains(spv::Capability::Shader)); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000))); } TEST(CapabilitySet, ConstructSingleMemberMaxInMask) { CapabilitySet s(static_cast<spv::Capability>(63)); - EXPECT_FALSE(s.Contains(spv::Capability::Matrix)); - EXPECT_FALSE(s.Contains(spv::Capability::Shader)); - EXPECT_TRUE(s.Contains(static_cast<spv::Capability>(63))); - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(64))); - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(1000))); + EXPECT_FALSE(s.contains(spv::Capability::Matrix)); + EXPECT_FALSE(s.contains(spv::Capability::Shader)); + EXPECT_TRUE(s.contains(static_cast<spv::Capability>(63))); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(64))); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000))); } TEST(CapabilitySet, ConstructSingleMemberMinOverflow) { // Check the first one that forces overflow beyond the mask. CapabilitySet s(static_cast<spv::Capability>(64)); - EXPECT_FALSE(s.Contains(spv::Capability::Matrix)); - EXPECT_FALSE(s.Contains(spv::Capability::Shader)); - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(63))); - EXPECT_TRUE(s.Contains(static_cast<spv::Capability>(64))); - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(1000))); + EXPECT_FALSE(s.contains(spv::Capability::Matrix)); + EXPECT_FALSE(s.contains(spv::Capability::Shader)); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(63))); + EXPECT_TRUE(s.contains(static_cast<spv::Capability>(64))); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000))); } TEST(CapabilitySet, ConstructSingleMemberMaxOverflow) { // Check the max 32-bit signed int. CapabilitySet s(static_cast<spv::Capability>(0x7fffffffu)); - EXPECT_FALSE(s.Contains(spv::Capability::Matrix)); - EXPECT_FALSE(s.Contains(spv::Capability::Shader)); - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(1000))); - EXPECT_TRUE(s.Contains(static_cast<spv::Capability>(0x7fffffffu))); + EXPECT_FALSE(s.contains(spv::Capability::Matrix)); + EXPECT_FALSE(s.contains(spv::Capability::Shader)); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(1000))); + EXPECT_TRUE(s.contains(static_cast<spv::Capability>(0x7fffffffu))); } TEST(CapabilitySet, AddEnum) { CapabilitySet s(spv::Capability::Shader); - s.Add(spv::Capability::Kernel); - s.Add(static_cast<spv::Capability>(42)); - EXPECT_FALSE(s.Contains(spv::Capability::Matrix)); - EXPECT_TRUE(s.Contains(spv::Capability::Shader)); - EXPECT_TRUE(s.Contains(spv::Capability::Kernel)); - EXPECT_TRUE(s.Contains(static_cast<spv::Capability>(42))); + s.insert(spv::Capability::Kernel); + s.insert(static_cast<spv::Capability>(42)); + EXPECT_FALSE(s.contains(spv::Capability::Matrix)); + EXPECT_TRUE(s.contains(spv::Capability::Shader)); + EXPECT_TRUE(s.contains(spv::Capability::Kernel)); + EXPECT_TRUE(s.contains(static_cast<spv::Capability>(42))); +} + +TEST(CapabilitySet, InsertReturnsIteratorToInserted) { + CapabilitySet set; + + auto[it, inserted] = set.insert(spv::Capability::Kernel); + + EXPECT_TRUE(inserted); + EXPECT_EQ(*it, spv::Capability::Kernel); +} + +TEST(CapabilitySet, InsertReturnsIteratorToElementOnDoubleInsertion) { + CapabilitySet set; + EXPECT_FALSE(set.contains(spv::Capability::Shader)); + { + auto[it, inserted] = set.insert(spv::Capability::Shader); + EXPECT_TRUE(inserted); + EXPECT_EQ(*it, spv::Capability::Shader); + } + EXPECT_TRUE(set.contains(spv::Capability::Shader)); + + auto[it, inserted] = set.insert(spv::Capability::Shader); + + EXPECT_FALSE(inserted); + EXPECT_EQ(*it, spv::Capability::Shader); + EXPECT_TRUE(set.contains(spv::Capability::Shader)); +} + +TEST(CapabilitySet, InsertWithHintWorks) { + CapabilitySet set; + EXPECT_FALSE(set.contains(spv::Capability::Shader)); + + auto it = set.insert(set.begin(), spv::Capability::Shader); + + EXPECT_EQ(*it, spv::Capability::Shader); + EXPECT_TRUE(set.contains(spv::Capability::Shader)); +} + +TEST(CapabilitySet, InsertWithEndHintWorks) { + CapabilitySet set; + EXPECT_FALSE(set.contains(spv::Capability::Shader)); + + auto it = set.insert(set.end(), spv::Capability::Shader); + + EXPECT_EQ(*it, spv::Capability::Shader); + EXPECT_TRUE(set.contains(spv::Capability::Shader)); +} + +TEST(CapabilitySet, IteratorCanBeCopied) { + CapabilitySet set; + set.insert(spv::Capability::Matrix); + set.insert(spv::Capability::Shader); + set.insert(spv::Capability::Geometry); + set.insert(spv::Capability::Float64); + set.insert(spv::Capability::Float16); + + auto a = set.begin(); + ++a; + auto b = a; + + EXPECT_EQ(*b, *a); + ++b; + EXPECT_NE(*b, *a); + + ++a; + EXPECT_EQ(*b, *a); + + ++a; + EXPECT_NE(*b, *a); +} + +TEST(CapabilitySet, IteratorBeginToEndPostfix) { + auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 1); + auto set = createSetUnorderedInsertion(orderedValues); + + size_t index = 0; + for (auto it = set.cbegin(); it != set.cend(); it++, index++) { + EXPECT_EQ(*it, orderedValues[index]); + } +} + +TEST(CapabilitySet, IteratorBeginToEndPrefix) { + auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 1); + auto set = createSetUnorderedInsertion(orderedValues); + + size_t index = 0; + for (auto it = set.cbegin(); it != set.cend(); ++it, index++) { + EXPECT_EQ(*it, orderedValues[index]); + } +} + +TEST(CapabilitySet, IteratorBeginToEndPrefixStep) { + auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 8); + auto set = createSetUnorderedInsertion(orderedValues); + + size_t index = 0; + for (auto it = set.cbegin(); it != set.cend(); ++it, index++) { + ASSERT_EQ(*it, orderedValues[index]); + } +} + +TEST(CapabilitySet, IteratorBeginOnEmpty) { + CapabilitySet set; + + auto begin = set.begin(); + auto end = set.end(); + ASSERT_EQ(begin, end); +} + +TEST(CapabilitySet, IteratorBeginOnSingleNonZeroValue) { + CapabilitySet set; + set.insert(spv::Capability::Shader); + + auto begin = set.begin(); + auto end = set.end(); + + ASSERT_NE(begin, end); + ASSERT_EQ(*begin, spv::Capability::Shader); +} + +TEST(CapabilitySet, IteratorForLoopNonZeroValue) { + CapabilitySet set; + set.insert(spv::Capability::Shader); + set.insert(spv::Capability::Tessellation); + + auto begin = set.begin(); + auto end = set.end(); + + ASSERT_NE(begin, end); + ASSERT_EQ(*begin, spv::Capability::Shader); + + begin++; + ASSERT_NE(begin, end); + ASSERT_EQ(*begin, spv::Capability::Tessellation); + + begin++; + ASSERT_EQ(begin, end); +} + +TEST(CapabilitySet, IteratorPastEnd) { + CapabilitySet set; + set.insert(spv::Capability::Shader); + + auto begin = set.begin(); + auto end = set.end(); + + ASSERT_NE(begin, end); + ASSERT_EQ(*begin, spv::Capability::Shader); + + begin++; + ASSERT_EQ(begin, end); + + begin++; + ASSERT_EQ(begin, end); +} + +TEST(CapabilitySet, CompatibleWithSTLFind) { + CapabilitySet set; + set.insert(spv::Capability::Matrix); + set.insert(spv::Capability::Shader); + set.insert(spv::Capability::Geometry); + set.insert(spv::Capability::Tessellation); + set.insert(spv::Capability::Addresses); + set.insert(spv::Capability::Linkage); + set.insert(spv::Capability::Kernel); + set.insert(spv::Capability::Vector16); + set.insert(spv::Capability::Float16Buffer); + set.insert(spv::Capability::Float64); + + { + auto it = std::find(set.cbegin(), set.cend(), spv::Capability::Vector16); + ASSERT_NE(it, set.end()); + ASSERT_EQ(*it, spv::Capability::Vector16); + } + + { + auto it = std::find(set.cbegin(), set.cend(), spv::Capability::Float16); + ASSERT_EQ(it, set.end()); + } +} + +TEST(CapabilitySet, CompatibleWithSTLForEach) { + auto orderedValues = enumerateValuesFromToWithStep(0, 100, /* step= */ 15); + auto set = createSetUnorderedInsertion(orderedValues); + + size_t index = 0; + std::for_each(set.cbegin(), set.cend(), [&](auto item) { + ASSERT_EQ(item, orderedValues[index]); + index++; + }); } TEST(CapabilitySet, InitializerListEmpty) { CapabilitySet s{}; for (uint32_t i = 0; i < 1000; i++) { - EXPECT_FALSE(s.Contains(static_cast<spv::Capability>(i))); + EXPECT_FALSE(s.contains(static_cast<spv::Capability>(i))); + } +} + +TEST(CapabilitySet, LargeSetHasInsertedElements) { + CapabilitySet set; + for (auto c : kCapabilities) { + EXPECT_FALSE(set.contains(c)); + } + + for (auto c : kCapabilities) { + set.insert(c); + EXPECT_TRUE(set.contains(c)); + } + + for (auto c : kCapabilities) { + EXPECT_TRUE(set.contains(c)); + } +} + +TEST(CapabilitySet, LargeSetHasUnsortedInsertedElements) { + std::vector shuffledCapabilities(kCapabilities.cbegin(), + kCapabilities.cend()); + std::mt19937 rng(0); + std::shuffle(shuffledCapabilities.begin(), shuffledCapabilities.end(), rng); + CapabilitySet set; + for (auto c : shuffledCapabilities) { + EXPECT_FALSE(set.contains(c)); + } + + for (auto c : shuffledCapabilities) { + set.insert(c); + EXPECT_TRUE(set.contains(c)); + } + + for (auto c : shuffledCapabilities) { + EXPECT_TRUE(set.contains(c)); + } +} + +TEST(CapabilitySet, LargeSetHasUnsortedRemovedElement) { + std::vector shuffledCapabilities(kCapabilities.cbegin(), + kCapabilities.cend()); + std::mt19937 rng(0); + std::shuffle(shuffledCapabilities.begin(), shuffledCapabilities.end(), rng); + CapabilitySet set; + for (auto c : shuffledCapabilities) { + set.insert(c); + EXPECT_TRUE(set.contains(c)); + } + + for (auto c : kCapabilities) { + set.erase(c); + } + + for (auto c : shuffledCapabilities) { + EXPECT_FALSE(set.contains(c)); } } @@ -287,5 +882,20 @@ static_cast<spv::Capability>(0x7fffffff)}}, })); +using BoundaryTestWithParam = ::testing::TestWithParam<spv::Capability>; + +TEST_P(BoundaryTestWithParam, InsertedContains) { + CapabilitySet set; + set.insert(GetParam()); + EXPECT_TRUE(set.contains(GetParam())); +} + +INSTANTIATE_TEST_SUITE_P( + Samples, BoundaryTestWithParam, + Values(static_cast<spv::Capability>(0), static_cast<spv::Capability>(63), + static_cast<spv::Capability>(64), static_cast<spv::Capability>(65), + static_cast<spv::Capability>(127), static_cast<spv::Capability>(128), + static_cast<spv::Capability>(129))); + } // namespace } // namespace spvtools
diff --git a/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp index 4fc9d2d..1280b81 100644 --- a/test/fuzz/transformation_add_no_contraction_decoration_test.cpp +++ b/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
@@ -36,7 +36,6 @@ OpName %8 "x" OpName %10 "y" OpName %14 "i" - OpDecorate %32 NoContraction %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 @@ -110,9 +109,8 @@ ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable( context.get(), transformation_context)); - // It is valid to add NoContraction to each of these ids (and it's fine to - // have duplicates of the decoration, in the case of 32). - for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) { + // It is valid to add NoContraction to each of these ids. + for (uint32_t result_id : {32u, 27u, 29u, 39u}) { TransformationAddNoContractionDecoration transformation(result_id); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); @@ -134,8 +132,6 @@ OpName %10 "y" OpName %14 "i" OpDecorate %32 NoContraction - OpDecorate %32 NoContraction - OpDecorate %32 NoContraction OpDecorate %27 NoContraction OpDecorate %29 NoContraction OpDecorate %39 NoContraction
diff --git a/test/fuzz/transformation_add_opphi_synonym_test.cpp b/test/fuzz/transformation_add_opphi_synonym_test.cpp index 4aca30c..03fd39a 100644 --- a/test/fuzz/transformation_add_opphi_synonym_test.cpp +++ b/test/fuzz/transformation_add_opphi_synonym_test.cpp
@@ -365,8 +365,7 @@ MakeSynonymFact(12, 16)); // Remove the VariablePointers capability. - context.get()->get_feature_mgr()->RemoveCapability( - spv::Capability::VariablePointers); + context.get()->RemoveCapability(spv::Capability::VariablePointers); // The VariablePointers capability is required to add an OpPhi instruction of // pointer type. @@ -374,8 +373,7 @@ .IsApplicable(context.get(), transformation_context)); // Add the VariablePointers capability back. - context.get()->get_feature_mgr()->AddCapability( - spv::Capability::VariablePointers); + context.get()->AddCapability(spv::Capability::VariablePointers); // If the ids have pointer type, the storage class must be Workgroup or // StorageBuffer, but it is Function in this case.
diff --git a/test/fuzz/transformation_add_relaxed_decoration_test.cpp b/test/fuzz/transformation_add_relaxed_decoration_test.cpp index c440882..979eeb7 100644 --- a/test/fuzz/transformation_add_relaxed_decoration_test.cpp +++ b/test/fuzz/transformation_add_relaxed_decoration_test.cpp
@@ -86,9 +86,8 @@ // Invalid: 28 is in a dead block, but returns bool (not numeric). ASSERT_FALSE(TransformationAddRelaxedDecoration(28).IsApplicable( context.get(), transformation_context)); - // It is valid to add RelaxedPrecision to 25 (and it's fine to - // have a duplicate). - for (uint32_t result_id : {25u, 25u}) { + // It is valid to add RelaxedPrecision to 25 + for (uint32_t result_id : {25u}) { TransformationAddRelaxedDecoration transformation(result_id); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); @@ -110,7 +109,6 @@ OpName %10 "b" OpName %14 "c" OpDecorate %25 RelaxedPrecision - OpDecorate %25 RelaxedPrecision %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1
diff --git a/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp index 75f3788..135190a 100644 --- a/test/fuzz/transformation_add_type_float_test.cpp +++ b/test/fuzz/transformation_add_type_float_test.cpp
@@ -74,7 +74,7 @@ // By default, SPIR-V does not support 64-bit float types. // Below we add such capability, so the test should now pass. - context.get()->get_feature_mgr()->AddCapability(spv::Capability::Float64); + context.get()->AddCapability(spv::Capability::Float64); ASSERT_TRUE(TransformationAddTypeFloat(7, 64).IsApplicable( context.get(), transformation_context));
diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp index b41d420..e31730c 100644 --- a/test/fuzz/transformation_add_type_int_test.cpp +++ b/test/fuzz/transformation_add_type_int_test.cpp
@@ -88,13 +88,13 @@ // By default SPIR-V does not support 16-bit integers. // Below we add such capability, so the test should now be successful. - context.get()->get_feature_mgr()->AddCapability(spv::Capability::Int16); + context.get()->AddCapability(spv::Capability::Int16); ASSERT_TRUE(TransformationAddTypeInt(7, 16, true) .IsApplicable(context.get(), transformation_context)); // By default SPIR-V does not support 64-bit integers. // Below we add such capability, so the test should now pass. - context.get()->get_feature_mgr()->AddCapability(spv::Capability::Int64); + context.get()->AddCapability(spv::Capability::Int64); ASSERT_TRUE(TransformationAddTypeInt(7, 64, true) .IsApplicable(context.get(), transformation_context));
diff --git a/test/link/binary_version_test.cpp b/test/link/binary_version_test.cpp index 78da1ae..384255a 100644 --- a/test/link/binary_version_test.cpp +++ b/test/link/binary_version_test.cpp
@@ -73,5 +73,21 @@ "through 1) vs 1.5 (input module 2).")); } +TEST_F(BinaryVersion, UseHighest) { + // clang-format off + spvtest::Binaries binaries = { + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)), + }; + // clang-format on + LinkerOptions options; + options.SetUseHighestVersion(true); + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary, options)) + << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 5), linked_binary[1]); +} + } // namespace } // namespace spvtools
diff --git a/test/log_test.cpp b/test/log_test.cpp deleted file mode 100644 index ec66aa1..0000000 --- a/test/log_test.cpp +++ /dev/null
@@ -1,53 +0,0 @@ -// 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. - -#include "source/opt/log.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace spvtools { -namespace { - -using ::testing::MatchesRegex; - -TEST(Log, Unimplemented) { - int invocation = 0; - auto consumer = [&invocation](spv_message_level_t level, const char* source, - const spv_position_t&, const char* message) { - ++invocation; - EXPECT_EQ(SPV_MSG_INTERNAL_ERROR, level); - EXPECT_THAT(source, MatchesRegex(".*log_test.cpp$")); - EXPECT_STREQ("unimplemented: the-ultimite-feature", message); - }; - - SPIRV_UNIMPLEMENTED(consumer, "the-ultimite-feature"); - EXPECT_EQ(1, invocation); -} - -TEST(Log, Unreachable) { - int invocation = 0; - auto consumer = [&invocation](spv_message_level_t level, const char* source, - const spv_position_t&, const char* message) { - ++invocation; - EXPECT_EQ(SPV_MSG_INTERNAL_ERROR, level); - EXPECT_THAT(source, MatchesRegex(".*log_test.cpp$")); - EXPECT_STREQ("unreachable", message); - }; - - SPIRV_UNREACHABLE(consumer); - EXPECT_EQ(1, invocation); -} - -} // namespace -} // namespace spvtools
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 01b98a6..53dbe07 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp
@@ -18,7 +18,12 @@ #include <vector> #include "gmock/gmock.h" +#include "source/assembly_grammar.h" #include "source/enum_set.h" +#include "source/operand.h" +#include "source/spirv_target_env.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" #include "test/unit_spirv.h" namespace spvtools { @@ -31,6 +36,25 @@ using ::testing::Values; using ::testing::ValuesIn; +// Emits a CapabilitySet to the given ostream, returning the ostream. +inline std::ostream& operator<<(std::ostream& out, const CapabilitySet& cs) { + out << "CapabilitySet{"; + auto ctx = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + spvtools::AssemblyGrammar grammar(ctx); + bool first = true; + for (auto c : cs) { + if (!first) { + out << " "; + first = false; + } + out << grammar.lookupOperandName(SPV_OPERAND_TYPE_CAPABILITY, uint32_t(c)) + << "(" << uint32_t(c) << ")"; + } + spvContextDestroy(ctx); + out << "}"; + return out; +} + // A test case for mapping an enum to a capability mask. struct EnumCapabilityCase { spv_operand_type_t type; @@ -38,6 +62,16 @@ CapabilitySet expected_capabilities; }; +// Emits an EnumCapabilityCase to the given output stream. This is used +// to emit failure cases when they occur, which helps debug tests. +inline std::ostream& operator<<(std::ostream& out, EnumCapabilityCase e) { + out << "{" << spvOperandTypeStr(e.type) << " " << e.value << " " + << e.expected_capabilities << " }"; + return out; +} + +using EnvEnumCapabilityCase = std::tuple<spv_target_env, EnumCapabilityCase>; + // Test fixture for testing EnumCapabilityCases. using EnumCapabilityTest = TestWithParam<std::tuple<spv_target_env, EnumCapabilityCase>>; @@ -56,7 +90,7 @@ EXPECT_THAT(ElementsIn(cap_set), Eq(ElementsIn(std::get<1>(GetParam()).expected_capabilities))) - << " capability value " << std::get<1>(GetParam()).value; + << " enum value " << std::get<1>(GetParam()).value; spvContextDestroy(context); } @@ -237,25 +271,21 @@ SamplerAddressingMode, EnumCapabilityTest, Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), ValuesIn(std::vector<EnumCapabilityCase>{ - CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::None, - Kernel), - CASE1(SAMPLER_ADDRESSING_MODE, - SamplerAddressingMode::ClampToEdge, Kernel), - CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::Clamp, - Kernel), - CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::Repeat, - Kernel), - CASE1(SAMPLER_ADDRESSING_MODE, - SamplerAddressingMode::RepeatMirrored, Kernel), - }))); + CASE0(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::None), + CASE0(SAMPLER_ADDRESSING_MODE, + SamplerAddressingMode::ClampToEdge), + CASE0(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::Clamp), + CASE0(SAMPLER_ADDRESSING_MODE, SamplerAddressingMode::Repeat), + CASE0(SAMPLER_ADDRESSING_MODE, + SamplerAddressingMode::RepeatMirrored)}))); // See SPIR-V Section 3.10 Sampler Filter Mode INSTANTIATE_TEST_SUITE_P( SamplerFilterMode, EnumCapabilityTest, Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), ValuesIn(std::vector<EnumCapabilityCase>{ - CASE1(SAMPLER_FILTER_MODE, SamplerFilterMode::Nearest, Kernel), - CASE1(SAMPLER_FILTER_MODE, SamplerFilterMode::Linear, Kernel), + CASE0(SAMPLER_FILTER_MODE, SamplerFilterMode::Nearest), + CASE0(SAMPLER_FILTER_MODE, SamplerFilterMode::Linear), }))); // See SPIR-V Section 3.11 Image Format @@ -310,56 +340,56 @@ // See SPIR-V Section 3.12 Image Channel Order INSTANTIATE_TEST_SUITE_P( ImageChannelOrder, EnumCapabilityTest, - Combine( - Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), - ValuesIn(std::vector<EnumCapabilityCase>{ - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::R, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::A, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RG, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGB, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGBA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::BGRA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::ARGB, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Intensity, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Luminance, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Rx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGBx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Depth, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::DepthStencil, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGB, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGBx, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGBA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sBGRA, Kernel), - CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrder::ABGR, Kernel), - }))); + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector<EnumCapabilityCase>{ + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::R), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::A), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RG), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RA), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGB), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGBA), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::BGRA), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::ARGB), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Intensity), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Luminance), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Rx), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGx), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::RGBx), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::Depth), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::DepthStencil), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGB), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGBx), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sRGBA), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::sBGRA), + CASE0(IMAGE_CHANNEL_ORDER, ImageChannelOrder::ABGR), + }))); // See SPIR-V Section 3.13 Image Channel Data Type INSTANTIATE_TEST_SUITE_P( ImageChannelDataType, EnumCapabilityTest, - Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), - ValuesIn(std::vector<EnumCapabilityCase>{ - // clang-format off - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SnormInt8, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SnormInt16, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt8, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt16, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormShort565, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormShort555, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt101010, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt8, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt16, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt32, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt8, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt16, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt32, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::HalfFloat, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::Float, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt24, Kernel), - CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt101010_2, Kernel), - // clang-format on - }))); + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector<EnumCapabilityCase>{ + // clang-format off + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SnormInt8), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SnormInt16), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt8), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt16), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormShort565), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormShort555), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt101010), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt8), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt16), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::SignedInt32), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt8), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt16), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnsignedInt32), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::HalfFloat), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::Float), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt24), + CASE0(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataType::UnormInt101010_2), + // clang-format on + }))); // See SPIR-V Section 3.14 Image Operands INSTANTIATE_TEST_SUITE_P( @@ -417,7 +447,7 @@ // See SPIR-V Section 3.20 Decoration INSTANTIATE_TEST_SUITE_P( - Decoration, EnumCapabilityTest, + Decoration_1_1, EnumCapabilityTest, Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), ValuesIn(std::vector<EnumCapabilityCase>{ CASE1(DECORATION, Decoration::RelaxedPrecision, Shader), @@ -461,7 +491,8 @@ CASE1(DECORATION, Decoration::XfbBuffer, TransformFeedback), CASE1(DECORATION, Decoration::XfbStride, TransformFeedback), CASE1(DECORATION, Decoration::FuncParamAttr, Kernel), - CASE1(DECORATION, Decoration::FPFastMathMode, Kernel), + CASE2(DECORATION, Decoration::FPFastMathMode, Kernel, + FloatControls2), CASE1(DECORATION, Decoration::LinkageAttributes, Linkage), CASE1(DECORATION, Decoration::NoContraction, Shader), CASE1(DECORATION, Decoration::InputAttachmentIndex, @@ -469,6 +500,13 @@ CASE1(DECORATION, Decoration::Alignment, Kernel), }))); +// See SPIR-V Section 3.20 Decoration +INSTANTIATE_TEST_SUITE_P(Decoration_1_6, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_6), + ValuesIn(std::vector<EnumCapabilityCase>{ + CASE2(DECORATION, Decoration::Uniform, + Shader, UniformDecoration)}))); + #if 0 // SpecId has different requirements in v1.0 and v1.1: INSTANTIATE_TEST_SUITE_P(DecorationSpecIdV10, EnumCapabilityTest,
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 3b2d384..92d266b 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt
@@ -66,6 +66,7 @@ instruction_list_test.cpp instruction_test.cpp interface_var_sroa_test.cpp + invocation_interlock_placement_test.cpp interp_fixup_test.cpp ir_builder.cpp ir_context_test.cpp @@ -77,6 +78,7 @@ local_single_block_elim.cpp local_single_store_elim_test.cpp local_ssa_elim_test.cpp + modify_maximal_reconvergence_test.cpp module_test.cpp module_utils.h optimizer_test.cpp @@ -103,6 +105,8 @@ strip_debug_info_test.cpp strip_nonsemantic_info_test.cpp struct_cfg_analysis_test.cpp + switch_descriptorset_test.cpp + trim_capabilities_pass_test.cpp type_manager_test.cpp types_test.cpp unify_const_test.cpp @@ -115,3 +119,12 @@ LIBS SPIRV-Tools-opt PCH_FILE pch_test_opt ) +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() + endif() +endif()
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 83aab3c..845c6a5 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -6764,7 +6764,7 @@ %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 + %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 @@ -7884,6 +7884,114 @@ SinglePassRunAndMatch<AggressiveDCEPass>(text, true); } +TEST_F(AggressiveDCETest, FunctionReturnPointer) { + // Run DCE when a function returning a pointer to a reference is present + + const std::string text = R"( + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Vertex %2 "main" %3 %4 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_buffer_reference" + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %4 "color" + OpMemberDecorate %5 0 Offset 0 + OpDecorate %5 Block + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %8 AliasedPointer + OpDecorate %4 Location 0 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + OpTypeForwardPointer %11 PhysicalStorageBuffer + %12 = OpTypeInt 32 0 + %5 = OpTypeStruct %12 + %11 = OpTypePointer PhysicalStorageBuffer %5 +;CHECK: [[pt:%\w+]] = OpTypePointer PhysicalStorageBuffer {{%\w+}} + %13 = OpTypeFunction %11 +;CHECK: [[pt_fn:%\w+]] = OpTypeFunction [[pt]] + %7 = OpTypeStruct %11 + %14 = OpTypePointer PushConstant %7 + %3 = OpVariable %14 PushConstant + %15 = OpTypeInt 32 1 + %16 = OpConstant %15 0 + %17 = OpTypePointer PushConstant %11 + %18 = OpTypePointer Function %11 + %19 = OpTypeFloat 32 + %20 = OpTypeVector %19 4 + %21 = OpTypePointer Output %20 + %4 = OpVariable %21 Output + %22 = OpConstant %19 1 + %23 = OpConstant %19 0 + %24 = OpConstantComposite %20 %22 %23 %22 %22 + %6 = OpFunction %11 None %13 +;CHECK: [[fn:%\w+]] = OpFunction [[pt]] None [[pt_fn]] + %27 = OpLabel + %28 = OpAccessChain %17 %3 %16 + %29 = OpLoad %11 %28 + OpReturnValue %29 + OpFunctionEnd + %2 = OpFunction %9 None %10 + %25 = OpLabel + %8 = OpVariable %18 Function + %26 = OpFunctionCall %11 %6 +;CHECK: {{%\w+}} = OpFunctionCall [[pt]] [[fn]] + OpStore %8 %26 + OpStore %4 %24 + OpReturn + OpFunctionEnd +)"; + + // For physical storage buffer support + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch<AggressiveDCEPass>(text, true); +} + +TEST_F(AggressiveDCETest, KeepBeginEndInvocationInterlock) { + // OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT delimit a + // critical section. As such, they should be treated as if they have side + // effects and should not be removed. + const std::string test = + R"(OpCapability Shader +OpCapability FragmentShaderSampleInterlockEXT +OpExtension "SPV_EXT_fragment_shader_interlock" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %gl_FragCoord +OpExecutionMode %1 OriginUpperLeft +OpExecutionMode %1 SampleInterlockOrderedEXT +OpDecorate %gl_FragCoord BuiltIn FragCoord +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%void = OpTypeVoid +%8 = OpTypeFunction %void +%bool = OpTypeBool +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%1 = OpFunction %void None %8 +%10 = OpLabel +%11 = OpLoad %v4float %gl_FragCoord +%12 = OpCompositeExtract %float %11 0 +%13 = OpFOrdGreaterThan %bool %12 %float_0 +OpSelectionMerge %14 None +OpBranchConditional %13 %15 %16 +%15 = OpLabel +OpBeginInvocationInterlockEXT +OpBranch %14 +%16 = OpLabel +OpBeginInvocationInterlockEXT +OpBranch %14 +%14 = OpLabel +OpEndInvocationInterlockEXT +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck<AggressiveDCEPass>(test, test, true, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/amd_ext_to_khr.cpp b/test/opt/amd_ext_to_khr.cpp index 3340e89..a520d60 100644 --- a/test/opt/amd_ext_to_khr.cpp +++ b/test/opt/amd_ext_to_khr.cpp
@@ -26,15 +26,17 @@ using ::testing::HasSubstr; -std::string GetTest(std::string op_code, std::string new_op_code) { +std::string GetTest(std::string op_code, std::string new_op_code, + bool is_float = false) { const std::string text = R"( ; CHECK: OpCapability Shader ; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" ; CHECK: OpFunction ; CHECK-NEXT: OpLabel -; CHECK-NEXT: [[undef:%\w+]] = OpUndef %uint +; CHECK-NEXT: [[undef:%\w+]] = OpUndef % ; CHECK-NEXT: )" + new_op_code + - R"( %uint %uint_3 Reduce [[undef]] + " %" + (is_float ? "float" : "uint") + + R"( %uint_3 Reduce [[undef]] OpCapability Shader OpCapability Groups OpExtension "SPV_AMD_shader_ballot" @@ -44,12 +46,15 @@ %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 %uint_3 = OpConstant %uint 3 %1 = OpFunction %void None %3 %6 = OpLabel - %7 = OpUndef %uint + %7 = OpUndef %)" + + (is_float ? "float" : "uint") + R"( %8 = )" + op_code + - R"( %uint %uint_3 Reduce %7 + " %" + (is_float ? "float" : "uint") + + R"( %uint_3 Reduce %7 OpReturn OpFunctionEnd @@ -64,7 +69,7 @@ } TEST_F(AmdExtToKhrTest, ReplaceGroupFAddNonUniformAMD) { std::string text = - GetTest("OpGroupFAddNonUniformAMD", "OpGroupNonUniformFAdd"); + GetTest("OpGroupFAddNonUniformAMD", "OpGroupNonUniformFAdd", true); SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true); } TEST_F(AmdExtToKhrTest, ReplaceGroupUMinNonUniformAMD) { @@ -79,7 +84,7 @@ } TEST_F(AmdExtToKhrTest, ReplaceGroupFMinNonUniformAMD) { std::string text = - GetTest("OpGroupFMinNonUniformAMD", "OpGroupNonUniformFMin"); + GetTest("OpGroupFMinNonUniformAMD", "OpGroupNonUniformFMin", true); SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true); } TEST_F(AmdExtToKhrTest, ReplaceGroupUMaxNonUniformAMD) { @@ -94,7 +99,7 @@ } TEST_F(AmdExtToKhrTest, ReplaceGroupFMaxNonUniformAMD) { std::string text = - GetTest("OpGroupFMaxNonUniformAMD", "OpGroupNonUniformFMax"); + GetTest("OpGroupFMaxNonUniformAMD", "OpGroupNonUniformFMax", true); SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true); }
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index 57c5061..331ce3a 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp
@@ -1320,6 +1320,148 @@ SinglePassRunAndMatch<BlockMergePass>(text, true); } +TEST_F(BlockMergeTest, MaximalReconvergenceNoMeldToMerge) { + const std::string text = R"( + OpCapability Shader + OpCapability GroupNonUniformBallot + OpCapability GroupNonUniformArithmetic + OpExtension "SPV_KHR_maximal_reconvergence" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID %output + OpExecutionMode %main LocalSize 1 1 1 + OpExecutionMode %main MaximallyReconvergesKHR + OpSource HLSL 660 + OpName %type_RWStructuredBuffer_uint "type.RWStructuredBuffer.uint" + OpName %output "output" + OpName %main "main" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %output DescriptorSet 0 + OpDecorate %output Binding 0 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_uint 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_uint Block + %uint = OpTypeInt 32 0 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %_runtimearr_uint = OpTypeRuntimeArray %uint + %type_RWStructuredBuffer_uint = OpTypeStruct %_runtimearr_uint + %_ptr_StorageBuffer_type_RWStructuredBuffer_uint = OpTypePointer StorageBuffer %type_RWStructuredBuffer_uint + %v3uint = OpTypeVector %uint 3 + %_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %15 = OpTypeFunction %void + %uint_3 = OpConstant %uint 3 + %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %output = OpVariable %_ptr_StorageBuffer_type_RWStructuredBuffer_uint StorageBuffer + %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %main = OpFunction %void None %15 + %18 = OpLabel + %19 = OpLoad %v3uint %gl_GlobalInvocationID + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None +; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] + OpBranch %23 + %23 = OpLabel + %24 = OpCompositeExtract %uint %19 0 + %25 = OpGroupNonUniformBroadcastFirst %uint %uint_3 %24 + %26 = OpIEqual %bool %24 %25 + OpSelectionMerge %27 None + OpBranchConditional %26 %28 %27 + %28 = OpLabel + %29 = OpGroupNonUniformIAdd %int %uint_3 Reduce %int_1 + %30 = OpBitcast %uint %29 + OpBranch %21 +; CHECK: [[t1:%\w+]] = OpGroupNonUniformIAdd %int %uint_3 Reduce %int_1 +; CHECK-NEXT: [[t2:%\w+]] = OpBitcast %uint [[t1]] +; CHECK-NEXT: OpBranch [[merge]] + %27 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + %31 = OpAccessChain %_ptr_StorageBuffer_uint %output %int_0 %24 + OpStore %31 %30 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SinglePassRunAndMatch<BlockMergePass>(text, true); +} + +TEST_F(BlockMergeTest, NoMaximalReconvergenceMeldToMerge) { + const std::string text = R"( + OpCapability Shader + OpCapability GroupNonUniformBallot + OpCapability GroupNonUniformArithmetic + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID %output + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 660 + OpName %type_RWStructuredBuffer_uint "type.RWStructuredBuffer.uint" + OpName %output "output" + OpName %main "main" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %output DescriptorSet 0 + OpDecorate %output Binding 0 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_uint 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_uint Block + %uint = OpTypeInt 32 0 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %_runtimearr_uint = OpTypeRuntimeArray %uint + %type_RWStructuredBuffer_uint = OpTypeStruct %_runtimearr_uint + %_ptr_StorageBuffer_type_RWStructuredBuffer_uint = OpTypePointer StorageBuffer %type_RWStructuredBuffer_uint + %v3uint = OpTypeVector %uint 3 + %_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %15 = OpTypeFunction %void + %uint_3 = OpConstant %uint 3 + %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %output = OpVariable %_ptr_StorageBuffer_type_RWStructuredBuffer_uint StorageBuffer + %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %main = OpFunction %void None %15 + %18 = OpLabel + %19 = OpLoad %v3uint %gl_GlobalInvocationID + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None +; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] + OpBranch %23 + %23 = OpLabel + %24 = OpCompositeExtract %uint %19 0 + %25 = OpGroupNonUniformBroadcastFirst %uint %uint_3 %24 + %26 = OpIEqual %bool %24 %25 + OpSelectionMerge %27 None + OpBranchConditional %26 %28 %27 + %28 = OpLabel + %29 = OpGroupNonUniformIAdd %int %uint_3 Reduce %int_1 + %30 = OpBitcast %uint %29 + OpBranch %21 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[t1:%\w+]] = OpGroupNonUniformIAdd %int %uint_3 Reduce %int_1 +; CHECK-NEXT: [[t2:%\w+]] = OpBitcast %uint [[t1]] + %27 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + %31 = OpAccessChain %_ptr_StorageBuffer_uint %output %int_0 %24 + OpStore %31 %30 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SinglePassRunAndMatch<BlockMergePass>(text, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // More complex control flow
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp index 27330e1..c577404 100644 --- a/test/opt/convert_relaxed_to_half_test.cpp +++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -1613,6 +1613,175 @@ SinglePassRunAndCheck<ConvertToHalfPass>(test, test, true); } +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. + + // Also ensure that a relaxed local variable does get converted to half + // precision before being passed to an image opeartor. + + // #version 310 es + // + // precision mediump float; + // + // layout(location = 10) in highp vec4 vertex_uv01; + // layout(binding = 0, set = 3) uniform sampler2D materialParams_baseColorMap; + // + // layout(location = 0) out vec4 fragColor; + // + // void main() { + // vec4 uv = vec4(2.0); + // fragColor = texture(materialParams_baseColorMap, uv.xy); + // fragColor = texture(materialParams_baseColorMap, vertex_uv01.xy); + // } + const std::string test = R"( + OpCapability Shader + OpCapability Float16 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %13 %25 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision +;CHECK: OpDecorate [[uv:%\w+]] RelaxedPrecision + OpDecorate %13 Location 0 + OpDecorate %17 DescriptorSet 3 + OpDecorate %17 Binding 0 + OpDecorate %18 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %25 Location 10 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 +;CHECK: [[float32_t:%\w+]] = OpTypeFloat 32 + %7 = OpTypeVector %6 4 +;CHECK: [[vec4_t:%\w+]] = OpTypeVector [[float32_t]] 4 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 2 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %12 = OpTypePointer Output %7 +;CHECK: [[output_ptr_t:%\w+]] = OpTypePointer Output [[vec4_t]] + %13 = OpVariable %12 Output +;CHECK: [[output:%\w+]] = OpVariable [[output_ptr_t]] Output + %14 = OpTypeImage %6 2D 0 0 0 1 Unknown + %15 = OpTypeSampledImage %14 + %16 = OpTypePointer UniformConstant %15 + %17 = OpVariable %16 UniformConstant + %19 = OpTypeVector %6 2 +;CHECK: [[vec2_t:%\w+]] = OpTypeVector [[float32_t]] 2 + %24 = OpTypePointer Input %7 +;CHECK: [[input_ptr_t:%\w+]] = OpTypePointer Input [[vec4_t]] + %25 = OpVariable %24 Input + %29 = OpTypeFloat 16 +;CHECK: [[float16_t:%\w+]] = OpTypeFloat 16 + %30 = OpTypeVector %29 4 + %33 = OpTypeVector %29 2 +;CHECK: [[vec2_16b_t:%\w+]] = OpTypeVector [[float16_t]] 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + +; The only Function storage variable is marked as relaxed + %9 = OpVariable %8 Function +;CHECK: [[uv]] = OpVariable {{%\w+}} Function + OpStore %9 %11 + %18 = OpLoad %15 %17 + %20 = OpLoad %7 %9 + %31 = OpFConvert %30 %20 + %32 = OpFConvert %30 %20 + +; The first sample op should get a 16b coordinate + %21 = OpVectorShuffle %33 %31 %32 0 1 +;CHECK: [[uv_16b:%\w+]] = OpVectorShuffle [[vec2_16b_t]] + %22 = OpImageSampleImplicitLod %7 %18 %21 +;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_16b]] + + OpStore %13 %22 + %23 = OpLoad %15 %17 + %26 = OpLoad %7 %25 + +; The second sample op should get a 32b coordinate + %27 = OpVectorShuffle %19 %26 %26 0 1 +;CHECK: [[uv_32b:%\w+]] = OpVectorShuffle [[vec2_t]] + %28 = OpImageSampleImplicitLod %7 %23 %27 +;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_32b]] + + OpStore %13 %28 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch<ConvertToHalfPass>(test, true); +} + +TEST_F(ConvertToHalfTest, DontRelaxDecoratedOpCompositeExtract) { + // This test checks that a OpCompositeExtract with a Struct operand won't be + // relaxed, even if it is explicitly decorated with RelaxedPrecision. + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %9 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_struct_6 = OpTypeStruct %v4float +%7 = OpUndef %_struct_6 +%1 = OpFunction %void None %3 +%8 = OpLabel +%9 = OpCompositeExtract %float %7 0 3 +OpReturn +OpFunctionEnd +)"; + + const std::string expected = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_struct_6 = OpTypeStruct %v4float +%7 = OpUndef %_struct_6 +%1 = OpFunction %void None %3 +%8 = OpLabel +%9 = OpCompositeExtract %float %7 0 3 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<ConvertToHalfPass>(test, expected, true); +} + +TEST_F(ConvertToHalfTest, DontRelaxOpCompositeExtract) { + // This test checks that a OpCompositeExtract with a Struct operand won't be + // relaxed, even if its result has no uses. + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_struct_6 = OpTypeStruct %v4float +%7 = OpUndef %_struct_6 +%1 = OpFunction %void None %3 +%8 = OpLabel +%9 = OpCompositeExtract %float %7 0 3 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck<ConvertToHalfPass>(test, test, true); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp index 2d4b7a3..16719b8 100644 --- a/test/opt/copy_prop_array_test.cpp +++ b/test/opt/copy_prop_array_test.cpp
@@ -1942,6 +1942,41 @@ SinglePassRunAndCheck<CopyPropagateArrays>(text, text, false); } + +// If the size of an array used in an OpCompositeInsert is not known at compile +// time, then we should not propagate the array, because we do not have a single +// array that represents the final value. +TEST_F(CopyPropArrayPassTest, SpecConstSizedArray) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%7 = OpSpecConstant %uint 32 +%_arr_int_7 = OpTypeArray %int %7 +%int_63 = OpConstant %int 63 +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%int_0 = OpConstant %int 0 +%int_587202566 = OpConstant %int 587202566 +%false = OpConstantFalse %bool +%_ptr_Function__arr_int_7 = OpTypePointer Function %_arr_int_7 +%16 = OpUndef %_arr_int_7 +%2 = OpFunction %void None %4 +%17 = OpLabel +%18 = OpVariable %_ptr_Function__arr_int_7 Function +%19 = OpCompositeInsert %_arr_int_7 %int_0 %16 0 +OpStore %18 %19 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck<CopyPropagateArrays>(text, text, false); +} } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/dead_insert_elim_test.cpp b/test/opt/dead_insert_elim_test.cpp index 268e659..fcc3dde 100644 --- a/test/opt/dead_insert_elim_test.cpp +++ b/test/opt/dead_insert_elim_test.cpp
@@ -736,6 +736,113 @@ SinglePassRunAndMatch<DeadInsertElimPass>(text, true); } +TEST_F(DeadInsertElimTest, PhiOverEmptyStruct) { + // Reproducer for nullptr access error in MarkInsertChain + // that occurs when processing a phi operation with an + // empty struct result type. + // + // Note: Disassembly created from HLSL source with + // dxc -T cs_6_6 -spirv -Oconfig= + // --eliminate-dead-branches,--merge-return,--ssa-rewrite + // + // RWBuffer<float> buf; + // + // struct S { }; + // + // S fn() { + // S s = (S)0; + // if (buf[0] > 0) { + // return s; + // } + // return s; + // } + // + // [numthreads(1,1,1)] + // void main() { + // fn(); + // } + + const std::string disassembly = + R"(OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 660 + OpName %S "S" + OpName %type_buffer_image "type.buffer.image" + OpName %buf "buf" + OpName %main "main" + OpName %src_main "src.main" + OpName %bb_entry "bb.entry" + OpName %fn "fn" + OpName %bb_entry_0 "bb.entry" + OpName %s "s" + OpName %if_true "if.true" + OpName %if_merge "if.merge" + OpDecorate %buf DescriptorSet 0 + OpDecorate %buf Binding 0 + %S = OpTypeStruct + %4 = OpConstantNull %S + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 R32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image + %void = OpTypeVoid + %12 = OpTypeFunction %void + %19 = OpTypeFunction %S +%_ptr_Function_S = OpTypePointer Function %S + %v4float = OpTypeVector %float 4 + %bool = OpTypeBool + %buf = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant + %false = OpConstantFalse %bool +%_ptr_Function_bool = OpTypePointer Function %bool + %true = OpConstantTrue %bool + %main = OpFunction %void None %12 + %13 = OpLabel + %14 = OpFunctionCall %void %src_main + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %12 + %bb_entry = OpLabel + %17 = OpFunctionCall %S %fn + OpReturn + OpFunctionEnd + %fn = OpFunction %S None %19 + %bb_entry_0 = OpLabel + %39 = OpVariable %_ptr_Function_bool Function %false + %34 = OpVariable %_ptr_Function_S Function + %s = OpVariable %_ptr_Function_S Function + OpSelectionMerge %33 None + OpSwitch %uint_0 %36 + %36 = OpLabel + OpStore %s %4 + %23 = OpLoad %type_buffer_image %buf + %25 = OpImageRead %v4float %23 %uint_0 None + %26 = OpCompositeExtract %float %25 0 + %28 = OpFOrdGreaterThan %bool %26 %float_0 + OpSelectionMerge %if_merge None + OpBranchConditional %28 %if_true %if_merge + %if_true = OpLabel + OpStore %39 %true + OpStore %34 %4 + OpBranch %33 + %if_merge = OpLabel + OpStore %39 %true + OpStore %34 %4 + OpBranch %33 + %33 = OpLabel + %41 = OpPhi %S %4 %if_true %4 %if_merge + OpReturnValue %41 + OpFunctionEnd +)"; + // Used to crash with a nullptr access violation when processing %41 + SinglePassRunToBinary<DeadInsertElimPass>(disassembly, true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: //
diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp index 7a118f9..5c166d8 100644 --- a/test/opt/desc_sroa_test.cpp +++ b/test/opt/desc_sroa_test.cpp
@@ -918,6 +918,74 @@ SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true); } +TEST_F(DescriptorScalarReplacementTest, ExpandArrayInOpEntryPoint) { + const std::string text = R"(; SPIR-V +; Version: 1.6 +; Bound: 31 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + +; CHECK: OpEntryPoint GLCompute %main "main" %output_0_ %output_1_ + + OpEntryPoint GLCompute %main "main" %output + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 670 + OpName %type_RWByteAddressBuffer "type.RWByteAddressBuffer" + OpName %output "output" + OpName %main "main" + OpName %src_main "src.main" + OpName %bb_entry "bb.entry" + +; CHECK: OpDecorate %output_1_ DescriptorSet 0 +; CHECK: OpDecorate %output_1_ Binding 1 +; CHECK: OpDecorate %output_0_ DescriptorSet 0 +; CHECK: OpDecorate %output_0_ Binding 0 + + OpDecorate %output DescriptorSet 0 + OpDecorate %output Binding 0 + + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %type_RWByteAddressBuffer 0 Offset 0 + OpDecorate %type_RWByteAddressBuffer Block + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %uint_32 = OpConstant %uint 32 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%type_RWByteAddressBuffer = OpTypeStruct %_runtimearr_uint +%_arr_type_RWByteAddressBuffer_uint_2 = OpTypeArray %type_RWByteAddressBuffer %uint_2 +%_ptr_StorageBuffer__arr_type_RWByteAddressBuffer_uint_2 = OpTypePointer StorageBuffer %_arr_type_RWByteAddressBuffer_uint_2 + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_StorageBuffer_type_RWByteAddressBuffer = OpTypePointer StorageBuffer %type_RWByteAddressBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + +; CHECK: %output_1_ = OpVariable %_ptr_StorageBuffer_type_RWByteAddressBuffer StorageBuffer +; CHECK: %output_0_ = OpVariable %_ptr_StorageBuffer_type_RWByteAddressBuffer StorageBuffer + + %output = OpVariable %_ptr_StorageBuffer__arr_type_RWByteAddressBuffer_uint_2 StorageBuffer + + %main = OpFunction %void None %23 + %26 = OpLabel + %27 = OpFunctionCall %void %src_main + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %23 + %bb_entry = OpLabel + %28 = OpAccessChain %_ptr_StorageBuffer_type_RWByteAddressBuffer %output %int_1 + %29 = OpShiftRightLogical %uint %uint_0 %uint_2 + %30 = OpAccessChain %_ptr_StorageBuffer_uint %28 %uint_0 %29 + OpStore %30 %uint_32 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch<DescriptorScalarReplacement>(text, false); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/feature_manager_test.cpp b/test/opt/feature_manager_test.cpp index a5105a9..7e8f92c 100644 --- a/test/opt/feature_manager_test.cpp +++ b/test/opt/feature_manager_test.cpp
@@ -87,6 +87,25 @@ Extension::kSPV_KHR_storage_buffer_storage_class)); } +TEST_F(FeatureManagerTest, GetExtensionsReturnsExtensions) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_storage_buffer_storage_class" + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + const auto& extensions = context->get_feature_mgr()->GetExtensions(); + EXPECT_EQ(extensions.size(), 2); + EXPECT_TRUE(extensions.contains(Extension::kSPV_KHR_variable_pointers)); + EXPECT_TRUE( + extensions.contains(Extension::kSPV_KHR_storage_buffer_storage_class)); +} + // Test capability checks. TEST_F(FeatureManagerTest, ExplicitlyPresent1) { const std::string text = R"( @@ -142,6 +161,24 @@ context->get_feature_mgr()->HasCapability(spv::Capability::Kernel)); } +TEST_F(FeatureManagerTest, GetCapabilitiesReturnsImplicitCapabilities) { + const std::string text = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + const auto& capabilities = context->get_feature_mgr()->GetCapabilities(); + // Tesselation implies Shader, which implies Matrix. + EXPECT_EQ(capabilities.size(), 3); + EXPECT_TRUE(capabilities.contains(spv::Capability::Tessellation)); + EXPECT_TRUE(capabilities.contains(spv::Capability::Shader)); + EXPECT_TRUE(capabilities.contains(spv::Capability::Matrix)); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp index 93ce873..684e006 100644 --- a/test/opt/fix_storage_class_test.cpp +++ b/test/opt/fix_storage_class_test.cpp
@@ -874,6 +874,47 @@ SinglePassRunAndMatch<FixStorageClass>(text, false); } +TEST_F(FixStorageClassTest, SupportsU64Index) { + const std::string text = R"( +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_LocalInvocationID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %ulong = OpTypeInt 64 0 + %ulong_0 = OpConstant %ulong 0 + %float = OpTypeFloat 32 + %float_123 = OpConstant %float 123 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %30 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %8 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %30 + %38 = OpLabel + %44 = OpLoad %v3uint %gl_LocalInvocationID + %59 = OpCompositeExtract %uint %44 0 + %60 = OpAccessChain %_ptr_Uniform_float %8 %ulong_0 %59 + OpStore %60 %float_123 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<FixStorageClass>(text, false); +} } // namespace } // namespace opt
diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp index f83e86e..335e0f5 100644 --- a/test/opt/fold_spec_const_op_composite_test.cpp +++ b/test/opt/fold_spec_const_op_composite_test.cpp
@@ -674,6 +674,31 @@ SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false); } +// Silently ignore spec constants that cannot be folded +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, UnfoldableOp) { + const std::string test = R"( + OpCapability Shader + OpCapability SignedZeroInfNanPreserve + OpExtension "SPV_KHR_float_controls" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %v SpecId 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v = OpConstant %float 0x1p-1 +%c = OpSpecConstantOp %float QuantizeToF16 %v +;CHECK: {{%\w+}} = OpSpecConstantOp {{%\w+}} QuantizeToF16 {{%\w+}} + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false); +} + // All types and some common constants that are potentially required in // FoldSpecConstantOpAndCompositeTest. std::vector<std::string> CommonTypesAndConstants() { @@ -1033,14 +1058,8 @@ }, // expected { - "%true = OpConstantTrue %bool", - "%true_0 = OpConstantTrue %bool", "%spec_bool_t_vec = OpConstantComposite %v2bool %bool_true %bool_true", - "%false = OpConstantFalse %bool", - "%false_0 = OpConstantFalse %bool", "%spec_bool_f_vec = OpConstantComposite %v2bool %bool_false %bool_false", - "%false_1 = OpConstantFalse %bool", - "%false_2 = OpConstantFalse %bool", "%spec_bool_from_null = OpConstantComposite %v2bool %bool_false %bool_false", }, }, @@ -1055,14 +1074,8 @@ }, // expected { - "%true = OpConstantTrue %bool", - "%true_0 = OpConstantTrue %bool", "%spec_bool_t_vec = OpConstantComposite %v2bool %bool_true %bool_true", - "%false = OpConstantFalse %bool", - "%false_0 = OpConstantFalse %bool", "%spec_bool_f_vec = OpConstantComposite %v2bool %bool_false %bool_false", - "%false_1 = OpConstantFalse %bool", - "%false_2 = OpConstantFalse %bool", "%spec_bool_from_null = OpConstantComposite %v2bool %bool_false %bool_false", }, }, @@ -1077,14 +1090,8 @@ }, // expected { - "%int_1 = OpConstant %int 1", - "%int_1_0 = OpConstant %int 1", "%spec_int_one_vec = OpConstantComposite %v2int %signed_one %signed_one", - "%int_0 = OpConstant %int 0", - "%int_0_0 = OpConstant %int 0", "%spec_int_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", - "%int_0_1 = OpConstant %int 0", - "%int_0_2 = OpConstant %int 0", "%spec_int_from_null = OpConstantComposite %v2int %signed_zero %signed_zero", }, }, @@ -1099,14 +1106,8 @@ }, // expected { - "%int_1 = OpConstant %int 1", - "%int_1_0 = OpConstant %int 1", "%spec_int_one_vec = OpConstantComposite %v2int %signed_one %signed_one", - "%int_0 = OpConstant %int 0", - "%int_0_0 = OpConstant %int 0", "%spec_int_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", - "%int_0_1 = OpConstant %int 0", - "%int_0_2 = OpConstant %int 0", "%spec_int_from_null = OpConstantComposite %v2int %signed_zero %signed_zero", }, }, @@ -1121,14 +1122,8 @@ }, // expected { - "%uint_1 = OpConstant %uint 1", - "%uint_1_0 = OpConstant %uint 1", "%spec_uint_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", - "%uint_0 = OpConstant %uint 0", - "%uint_0_0 = OpConstant %uint 0", "%spec_uint_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", - "%uint_0_1 = OpConstant %uint 0", - "%uint_0_2 = OpConstant %uint 0", "%spec_uint_from_null = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", }, }, @@ -1143,14 +1138,8 @@ }, // expected { - "%uint_1 = OpConstant %uint 1", - "%uint_1_0 = OpConstant %uint 1", "%spec_uint_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", - "%uint_0 = OpConstant %uint 0", - "%uint_0_0 = OpConstant %uint 0", "%spec_uint_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", - "%uint_0_1 = OpConstant %uint 0", - "%uint_0_2 = OpConstant %uint 0", "%spec_uint_from_null = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", }, }, @@ -1159,8 +1148,6 @@ { // original { - "%spec_uint_zero = OpSpecConstantOp %uint UConvert %bool_false", - "%spec_uint_one = OpSpecConstantOp %uint UConvert %bool_true", "%spec_ulong_zero = OpSpecConstantOp %ulong UConvert %unsigned_zero", "%spec_ulong_one = OpSpecConstantOp %ulong UConvert %unsigned_one", "%spec_short_zero = OpSpecConstantOp %ushort UConvert %unsigned_zero", @@ -1172,8 +1159,6 @@ }, // expected { - "%spec_uint_zero = OpConstant %uint 0", - "%spec_uint_one = OpConstant %uint 1", "%spec_ulong_zero = OpConstant %ulong 0", "%spec_ulong_one = OpConstant %ulong 1", "%spec_short_zero = OpConstant %ushort 0", @@ -1211,24 +1196,13 @@ { // original { - "%spec_v2uint_zero = OpSpecConstantOp %v2uint UConvert %bool_false_vec", - "%spec_v2uint_one = OpSpecConstantOp %v2uint UConvert %bool_true_vec", "%spec_v2ulong_zero = OpSpecConstantOp %v2ulong UConvert %unsigned_zero_vec", "%spec_v2ulong_one = OpSpecConstantOp %v2ulong UConvert %unsigned_one_vec", }, // expected { - "%uint_0 = OpConstant %uint 0", - "%uint_0_0 = OpConstant %uint 0", - "%spec_v2uint_zero = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", - "%uint_1 = OpConstant %uint 1", - "%uint_1_0 = OpConstant %uint 1", - "%spec_v2uint_one = OpConstantComposite %v2uint %unsigned_one %unsigned_one", - "%ulong_0 = OpConstant %ulong 0", - "%ulong_0_0 = OpConstant %ulong 0", "%spec_v2ulong_zero = OpConstantComposite %v2ulong %ulong_zero %ulong_zero", "%ulong_1 = OpConstant %ulong 1", - "%ulong_1_0 = OpConstant %ulong 1", "%spec_v2ulong_one = OpConstantComposite %v2ulong %ulong_1 %ulong_1", }, }, @@ -1243,14 +1217,10 @@ }, // expected { - "%long_0 = OpConstant %long 0", - "%long_0_0 = OpConstant %long 0", "%spec_v2long_zero = OpConstantComposite %v2long %long_zero %long_zero", "%long_1 = OpConstant %long 1", - "%long_1_0 = OpConstant %long 1", "%spec_v2long_one = OpConstantComposite %v2long %long_1 %long_1", "%long_n1 = OpConstant %long -1", - "%long_n1_0 = OpConstant %long -1", "%spec_v2long_minus_one = OpConstantComposite %v2long %long_n1 %long_n1", }, }, @@ -1347,7 +1317,7 @@ { "%int_minus_1 = OpConstant %int -1", "%int_minus_2 = OpConstant %int -2", - "%int_neg_null = OpConstant %int 0", + "%int_neg_null = OpConstantNull %int", "%int_max = OpConstant %int 2147483647", "%int_neg_max = OpConstant %int -2147483647", }, @@ -1528,15 +1498,10 @@ }, // expected { - "%int_n1 = OpConstant %int -1", - "%int_n1_0 = OpConstant %int -1", "%v2int_minus_1 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", "%int_n2 = OpConstant %int -2", - "%int_n2_0 = OpConstant %int -2", "%v2int_minus_2 = OpConstantComposite %v2int %int_n2 %int_n2", - "%int_0 = OpConstant %int 0", - "%int_0_0 = OpConstant %int 0", - "%v2int_neg_null = OpConstantComposite %v2int %signed_zero %signed_zero", + "%v2int_neg_null = OpConstantComposite %v2int %signed_null %signed_null", }, }, // vector integer (including null vetors) add, sub, div, mul @@ -1558,35 +1523,23 @@ // expected { "%int_5 = OpConstant %int 5", - "%int_5_0 = OpConstant %int 5", "%spec_v2int_iadd = OpConstantComposite %v2int %int_5 %int_5", "%int_n4 = OpConstant %int -4", - "%int_n4_0 = OpConstant %int -4", "%spec_v2int_isub = OpConstantComposite %v2int %int_n4 %int_n4", "%int_n2 = OpConstant %int -2", - "%int_n2_0 = OpConstant %int -2", "%spec_v2int_sdiv = OpConstantComposite %v2int %int_n2 %int_n2", "%int_n6 = OpConstant %int -6", - "%int_n6_0 = OpConstant %int -6", "%spec_v2int_imul = OpConstantComposite %v2int %int_n6 %int_n6", - "%int_n6_1 = OpConstant %int -6", - "%int_n6_2 = OpConstant %int -6", "%spec_v2int_iadd_null = OpConstantComposite %v2int %int_n6 %int_n6", "%uint_5 = OpConstant %uint 5", - "%uint_5_0 = OpConstant %uint 5", "%spec_v2uint_iadd = OpConstantComposite %v2uint %uint_5 %uint_5", "%uint_4294967292 = OpConstant %uint 4294967292", - "%uint_4294967292_0 = OpConstant %uint 4294967292", "%spec_v2uint_isub = OpConstantComposite %v2uint %uint_4294967292 %uint_4294967292", "%uint_1431655764 = OpConstant %uint 1431655764", - "%uint_1431655764_0 = OpConstant %uint 1431655764", "%spec_v2uint_udiv = OpConstantComposite %v2uint %uint_1431655764 %uint_1431655764", "%uint_2863311528 = OpConstant %uint 2863311528", - "%uint_2863311528_0 = OpConstant %uint 2863311528", "%spec_v2uint_imul = OpConstantComposite %v2uint %uint_2863311528 %uint_2863311528", - "%uint_2863311528_1 = OpConstant %uint 2863311528", - "%uint_2863311528_2 = OpConstant %uint 2863311528", "%spec_v2uint_isub_null = OpConstantComposite %v2uint %uint_2863311528 %uint_2863311528", }, }, @@ -1630,34 +1583,17 @@ "%v2int_minus_3 = OpConstantComposite %v2int %int_minus_3 %int_minus_3", // srem - "%int_1 = OpConstant %int 1", - "%int_1_0 = OpConstant %int 1", "%7_srem_3 = OpConstantComposite %v2int %signed_one %signed_one", - "%int_n1 = OpConstant %int -1", - "%int_n1_0 = OpConstant %int -1", "%minus_7_srem_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", - "%int_1_1 = OpConstant %int 1", - "%int_1_2 = OpConstant %int 1", "%7_srem_minus_3 = OpConstantComposite %v2int %signed_one %signed_one", - "%int_n1_1 = OpConstant %int -1", - "%int_n1_2 = OpConstant %int -1", "%minus_7_srem_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", // smod - "%int_1_3 = OpConstant %int 1", - "%int_1_4 = OpConstant %int 1", "%7_smod_3 = OpConstantComposite %v2int %signed_one %signed_one", - "%int_2 = OpConstant %int 2", - "%int_2_0 = OpConstant %int 2", "%minus_7_smod_3 = OpConstantComposite %v2int %signed_two %signed_two", "%int_n2 = OpConstant %int -2", - "%int_n2_0 = OpConstant %int -2", "%7_smod_minus_3 = OpConstantComposite %v2int %int_n2 %int_n2", - "%int_n1_3 = OpConstant %int -1", - "%int_n1_4 = OpConstant %int -1", "%minus_7_smod_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", // umod - "%uint_1 = OpConstant %uint 1", - "%uint_1_0 = OpConstant %uint 1", "%7_umod_3 = OpConstantComposite %v2uint %unsigned_one %unsigned_one", }, }, @@ -1677,26 +1613,15 @@ }, // expected { - "%int_2 = OpConstant %int 2", - "%int_2_0 = OpConstant %int 2", "%xor_1_3 = OpConstantComposite %v2int %signed_two %signed_two", - "%int_0 = OpConstant %int 0", - "%int_0_0 = OpConstant %int 0", "%and_1_2 = OpConstantComposite %v2int %signed_zero %signed_zero", - "%int_3 = OpConstant %int 3", - "%int_3_0 = OpConstant %int 3", "%or_1_2 = OpConstantComposite %v2int %signed_three %signed_three", "%unsigned_31 = OpConstant %uint 31", "%v2unsigned_31 = OpConstantComposite %v2uint %unsigned_31 %unsigned_31", "%uint_2147483648 = OpConstant %uint 2147483648", - "%uint_2147483648_0 = OpConstant %uint 2147483648", "%unsigned_left_shift_max = OpConstantComposite %v2uint %uint_2147483648 %uint_2147483648", - "%uint_1 = OpConstant %uint 1", - "%uint_1_0 = OpConstant %uint 1", "%unsigned_right_shift_logical = OpConstantComposite %v2uint %unsigned_one %unsigned_one", - "%int_n1 = OpConstant %int -1", - "%int_n1_0 = OpConstant %int -1", "%signed_right_shift_arithmetic = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", }, }, @@ -2066,7 +1991,6 @@ "%spec_int_20 = OpConstant %int 101", "%used_vec_a = OpConstantComposite %v2int %spec_int_18 %spec_int_19", "%int_10201 = OpConstant %int 10201", - "%int_1 = OpConstant %int 1", "%used_vec_b = OpConstantComposite %v2int %int_10201 %signed_one", "%spec_int_21 = OpConstant %int 10201", "%array = OpConstantComposite %type_arr_int_4 %spec_int_20 %spec_int_20 %spec_int_21 %spec_int_21",
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index eff8edf..a4e0447 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp
@@ -16,7 +16,6 @@ #include <limits> #include <memory> #include <string> -#include <unordered_set> #include <vector> #include "effcee/effcee.h" @@ -67,42 +66,136 @@ ResultType expected_result; }; +std::tuple<std::unique_ptr<IRContext>, Instruction*> GetInstructionToFold( + const std::string test_body, const uint32_t id_to_fold, + spv_target_env spv_env) { + // Build module. + std::unique_ptr<IRContext> context = + BuildModule(spv_env, nullptr, test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + if (context == nullptr) { + return {nullptr, nullptr}; + } + + // Fold the instruction to test. + if (id_to_fold != 0) { + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(id_to_fold); + return {std::move(context), inst}; + } + + // If there is not ID, we get the instruction just before a terminator + // instruction. That could be a return or abort. This is used for cases where + // the instruction we want to fold does not have a result id. + Function* func = &*context->module()->begin(); + for (auto& bb : *func) { + Instruction* terminator = bb.terminator(); + if (terminator->IsReturnOrAbort()) { + return {std::move(context), terminator->PreviousNode()}; + } + } + return {nullptr, nullptr}; +} + +std::tuple<std::unique_ptr<IRContext>, Instruction*> FoldInstruction( + const std::string test_body, const uint32_t id_to_fold, + spv_target_env spv_env) { + // Build module. + std::unique_ptr<IRContext> context; + Instruction* inst = nullptr; + std::tie(context, inst) = + GetInstructionToFold(test_body, id_to_fold, spv_env); + + if (context == nullptr) { + return {nullptr, nullptr}; + } + + std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + EXPECT_EQ(inst->result_id(), original_inst->result_id()); + EXPECT_EQ(inst->type_id(), original_inst->type_id()); + + if (!succeeded && inst != nullptr) { + EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands()); + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + EXPECT_EQ(inst->GetOperand(i), original_inst->GetOperand(i)); + } + } + + return {std::move(context), succeeded ? inst : nullptr}; +} + +template <class ElementType, class Function> +void CheckForExpectedScalarConstant(Instruction* inst, + ElementType expected_result, + Function GetValue) { + ASSERT_TRUE(inst); + + IRContext* context = inst->context(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + while (inst->opcode() == spv::Op::OpCopyObject) { + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + } + + // Make sure we have a constant. + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::Constant* constant = const_mrg->GetConstantFromInst(inst); + ASSERT_TRUE(constant); + + // Make sure the constant is a scalar. + const analysis::ScalarConstant* result = constant->AsScalarConstant(); + ASSERT_TRUE(result); + + // Check if the result matches the expected value. + // If ExpectedType is not a float type, it should cast the value to a double + // and never get a nan. + if (!std::isnan(static_cast<double>(expected_result))) { + EXPECT_EQ(expected_result, GetValue(result)); + } else { + EXPECT_TRUE(std::isnan(static_cast<double>(GetValue(result)))); + } +} + +template <class ElementType, class Function> +void CheckForExpectedVectorConstant(Instruction* inst, + std::vector<ElementType> expected_result, + Function GetValue) { + ASSERT_TRUE(inst); + + IRContext* context = inst->context(); + EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite}; + EXPECT_THAT(opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + const std::vector<const analysis::Constant*>& componenets = + result->AsVectorConstant()->GetComponents(); + EXPECT_EQ(componenets.size(), expected_result.size()); + for (size_t i = 0; i < componenets.size(); i++) { + EXPECT_EQ(expected_result[i], GetValue(componenets[i])); + } + } +} + using IntegerInstructionFoldingTest = ::testing::TestWithParam<InstructionFoldingCase<uint32_t>>; TEST_P(IntegerInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); - - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_TRUE(succeeded); - if (inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - EXPECT_EQ(inst->opcode(), spv::Op::OpConstant); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::Constant* constant = const_mrg->GetConstantFromInst(inst); - // We expect to see either integer types or 16-bit float types here. - EXPECT_TRUE((constant->AsIntConstant() != nullptr) || - ((constant->AsFloatConstant() != nullptr) && - (constant->type()->AsFloat()->width() == 16))); - const analysis::ScalarConstant* result = - const_mrg->GetConstantFromInst(inst)->AsScalarConstant(); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - EXPECT_EQ(result->GetU32BitValue(), tc.expected_result); - } - } + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + CheckForExpectedScalarConstant( + inst, tc.expected_result, [](const analysis::Constant* c) { + return c->AsScalarConstant()->GetU32BitValue(); + }); } // Returns a common SPIR-V header for all of the test that follow. @@ -148,14 +241,20 @@ %ulong = OpTypeInt 64 0 %v2int = OpTypeVector %int 2 %v4int = OpTypeVector %int 4 +%v2short = OpTypeVector %short 2 +%v2long = OpTypeVector %long 2 +%v4long = OpTypeVector %long 4 %v4float = OpTypeVector %float 4 %v4double = OpTypeVector %double 4 %v2uint = OpTypeVector %uint 2 +%v2ulong = OpTypeVector %ulong 2 %v2float = OpTypeVector %float 2 %v2double = OpTypeVector %double 2 %v2half = OpTypeVector %half 2 %v2bool = OpTypeVector %bool 2 %m2x2int = OpTypeMatrix %v2int 2 +%mat4v2float = OpTypeMatrix %v2float 4 +%mat2v4float = OpTypeMatrix %v4float 2 %mat4v4float = OpTypeMatrix %v4float 4 %mat4v4double = OpTypeMatrix %v4double 4 %struct_v2int_int_int = OpTypeStruct %v2int %int %int @@ -174,16 +273,18 @@ %_ptr_struct_v2int_int_int = OpTypePointer Function %struct_v2int_int_int %_ptr_v2float = OpTypePointer Function %v2float %_ptr_v2double = OpTypePointer Function %v2double +%int_2 = OpConstant %int 2 +%int_arr_2 = OpTypeArray %int %int_2 %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 %byte_n1 = OpConstant %byte -1 %100 = OpConstant %int 0 ; 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 -%int_2 = OpConstant %int 2 %int_3 = OpConstant %int 3 %int_4 = OpConstant %int 4 %int_10 = OpConstant %int 10 @@ -197,12 +298,20 @@ %long_1 = OpConstant %long 1 %long_2 = OpConstant %long 2 %long_3 = OpConstant %long 3 +%long_n3 = OpConstant %long -3 +%long_7 = OpConstant %long 7 +%long_n7 = OpConstant %long -7 %long_10 = OpConstant %long 10 +%long_32768 = OpConstant %long 32768 +%long_n57344 = OpConstant %long -57344 +%long_n4611686018427387904 = OpConstant %long -4611686018427387904 %long_4611686018427387904 = OpConstant %long 4611686018427387904 %long_n1 = OpConstant %long -1 %long_n3689348814741910323 = OpConstant %long -3689348814741910323 %long_min = OpConstant %long -9223372036854775808 %long_max = OpConstant %long 9223372036854775807 +%ulong_7 = OpConstant %ulong 7 +%ulong_4611686018427387904 = OpConstant %ulong 4611686018427387904 %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 %uint_2 = OpConstant %uint 2 @@ -223,8 +332,12 @@ %v2int_2_2 = OpConstantComposite %v2int %int_2 %int_2 %v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3 %v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2 +%v2int_n1_n24 = OpConstantComposite %v2int %int_n1 %int_n24 %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 +%v2long_2_2 = OpConstantComposite %v2long %long_2 %long_2 +%v2long_2_3 = OpConstantComposite %v2long %long_2 %long_3 %v2bool_null = OpConstantNull %v2bool %v2bool_true_false = OpConstantComposite %v2bool %true %false %v2bool_false_true = OpConstantComposite %v2bool %false %true @@ -282,6 +395,7 @@ %v2double_null = OpConstantNull %v2double %108 = OpConstant %half 0 %half_1 = OpConstant %half 1 +%half_2 = OpConstant %half 2 %half_0_1 = OpConstantComposite %v2half %108 %half_1 %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 @@ -290,7 +404,8 @@ %v4float_1_1_1_1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 %v4float_1_2_3_4 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 %v4float_null = OpConstantNull %v4float -%mat4v4float_null = OpConstantComposite %mat4v4float %v4float_null %v4float_null %v4float_null %v4float_null +%mat2v4float_null = OpConstantNull %mat2v4float +%mat4v4float_null = OpConstantNull %mat4v4float %mat4v4float_1_2_3_4 = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %mat4v4float_1_2_3_4_null = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_null %v4float_1_2_3_4 %v4float_null %107 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0 @@ -301,7 +416,7 @@ %v4double_1_2_3_4 = OpConstantComposite %v4double %double_1 %double_2 %double_3 %double_4 %v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5 %v4double_null = OpConstantNull %v4double -%mat4v4double_null = OpConstantComposite %mat4v4double %v4double_null %v4double_null %v4double_null %v4double_null +%mat4v4double_null = OpConstantNull %mat4v4double %mat4v4double_1_2_3_4 = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %mat4v4double_1_2_3_4_null = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_null %v4double_1_2_3_4 %v4double_null %v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3 @@ -314,8 +429,11 @@ %int_0xC05FD666 = OpConstant %int 0xC05FD666 %int_0x66666666 = OpConstant %int 0x66666666 %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666 = OpConstantComposite %v4int %int_0x00000000 %int_0x3FF00000 %int_0x66666666 %int_0xC05FD666 +%ushort_0x4400 = OpConstant %ushort 0x4400 +%short_0x4400 = OpConstant %short 0x4400 %ushort_0xBC00 = OpConstant %ushort 0xBC00 %short_0xBC00 = OpConstant %short 0xBC00 +%int_arr_2_undef = OpUndef %int_arr_2 )"; return header; @@ -749,7 +867,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 44: UClamp 1 2 x + // Test case 46: UClamp 1 2 x InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -758,7 +876,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 2), - // Test case 45: UClamp 2 x 1 + // Test case 47: UClamp 2 x 1 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -767,7 +885,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 46: Bit-cast int 0 to unsigned int + // Test case 48: Bit-cast int 0 to unsigned int InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -775,7 +893,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 47: Bit-cast int -24 to unsigned int + // Test case 49: Bit-cast int -24 to unsigned int InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -783,7 +901,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, static_cast<uint32_t>(-24)), - // Test case 48: Bit-cast float 1.0f to unsigned int + // Test case 50: Bit-cast float 1.0f to unsigned int InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -791,7 +909,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, static_cast<uint32_t>(0x3f800000)), - // Test case 49: Bit-cast ushort 0xBC00 to ushort + // Test case 51: Bit-cast ushort 0xBC00 to ushort InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -799,7 +917,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00), - // Test case 50: Bit-cast short 0xBC00 to ushort + // Test case 52: Bit-cast short 0xBC00 to ushort InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -807,7 +925,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00), - // Test case 51: Bit-cast half 1 to ushort + // Test case 53: Bit-cast half 1 to ushort InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -815,7 +933,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x3C00), - // Test case 52: Bit-cast ushort 0xBC00 to short + // Test case 54: Bit-cast ushort 0xBC00 to short InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -823,7 +941,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00), - // Test case 53: Bit-cast short 0xBC00 to short + // Test case 55: Bit-cast short 0xBC00 to short InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -831,7 +949,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00), - // Test case 54: Bit-cast half 1 to short + // Test case 56: Bit-cast half 1 to short InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -839,7 +957,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x3C00), - // Test case 55: Bit-cast ushort 0xBC00 to half + // Test case 57: Bit-cast ushort 0xBC00 to half InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -847,7 +965,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xBC00), - // Test case 56: Bit-cast short 0xBC00 to half + // Test case 58: Bit-cast short 0xBC00 to half InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -855,7 +973,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0xFFFFBC00), - // Test case 57: Bit-cast half 1 to half + // Test case 59: Bit-cast half 1 to half InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -863,7 +981,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0x3C00), - // Test case 58: Bit-cast ubyte 1 to byte + // Test case 60: Bit-cast ubyte 1 to byte InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -871,58 +989,302 @@ "OpReturn\n" + "OpFunctionEnd", 2, 1), - // Test case 59: Bit-cast byte -1 to ubyte + // Test case 61: Bit-cast byte -1 to ubyte InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + "%2 = OpBitcast %ubyte %byte_n1\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 0xFFFFFFFF) + 2, 0xFFFFFFFF), + // Test case 62: Negate 2. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %int %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -2), + // Test case 63: Negate negative short. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %short %short_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x4400 /* expected to be sign extended. */), + // Test case 64: Negate positive short. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %short %short_0x4400\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFBC00 /* expected to be sign extended. */), + // Test case 65: Negate a negative short. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %ushort %ushort_0xBC00\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0x4400 /* expected to be zero extended. */), + // Test case 66: Negate positive short. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %ushort %ushort_0x4400\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xBC00 /* expected to be zero extended. */), + // Test case 67: Fold 2 + 3 (short) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpIAdd %short %short_2 %short_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 5), + // Test case 68: Fold 2 + -5 (short) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpIAdd %short %short_2 %short_n5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -3), + // Test case 69: Fold int(3ll) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSConvert %int %long_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 70: Fold short(-3ll) + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSConvert %short %long_n3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -3), + // Test case 71: Fold short(32768ll) - This should do a sign extend when + // converting to short. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSConvert %short %long_32768\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -32768), + // Test case 72: 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" + + "%main_lab = OpLabel\n" + + "%2 = OpSConvert %short %long_n57344\n" + + "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. + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpUConvert %uint %short_n5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 65531), + // Test case 74: 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) )); // clang-format on -using IntVectorInstructionFoldingTest = - ::testing::TestWithParam<InstructionFoldingCase<std::vector<uint32_t>>>; +using LongIntegerInstructionFoldingTest = + ::testing::TestWithParam<InstructionFoldingCase<uint64_t>>; -TEST_P(IntVectorInstructionFoldingTest, Case) { +TEST_P(LongIntegerInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + CheckForExpectedScalarConstant( + inst, tc.expected_result, [](const analysis::Constant* c) { + return c->AsScalarConstant()->GetU64BitValue(); + }); +} - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - spv::Op original_opcode = inst->opcode(); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); +INSTANTIATE_TEST_SUITE_P( + TestCase, LongIntegerInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold 1+4611686018427387904 + InstructionFoldingCase<uint64_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 = OpIAdd %long %long_1 %long_4611686018427387904\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 1 + 4611686018427387904), + // Test case 1: fold 1-4611686018427387904 + InstructionFoldingCase<uint64_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 = OpISub %long %long_1 %long_4611686018427387904\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 1 - 4611686018427387904), + // Test case 2: fold 2*4611686018427387904 + InstructionFoldingCase<uint64_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 = OpIMul %long %long_2 %long_4611686018427387904\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 9223372036854775808ull), + // Test case 3: fold 4611686018427387904/2 (unsigned) + InstructionFoldingCase<uint64_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 = OpUDiv %ulong %ulong_4611686018427387904 %ulong_2\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 4611686018427387904 / 2), + // Test case 4: fold 4611686018427387904/2 (signed) + InstructionFoldingCase<uint64_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 = OpSDiv %long %long_4611686018427387904 %long_2\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 4611686018427387904 / 2), + // Test case 5: fold -4611686018427387904/2 (signed) + InstructionFoldingCase<uint64_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 = OpSDiv %long %long_n4611686018427387904 %long_2\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, -4611686018427387904 / 2), + // Test case 6: fold 4611686018427387904 mod 7 (unsigned) + InstructionFoldingCase<uint64_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 = OpUMod %ulong %ulong_4611686018427387904 %ulong_7\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 4611686018427387904ull % 7ull), + // Test case 7: fold 7 mod 3 (signed) + InstructionFoldingCase<uint64_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 = OpSMod %long %long_7 %long_3\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, 1ull), + // Test case 8: fold 7 rem 3 (signed) + InstructionFoldingCase<uint64_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 = OpSRem %long %long_7 %long_3\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, 1ull), + // Test case 9: fold 7 mod -3 (signed) + InstructionFoldingCase<uint64_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 = OpSMod %long %long_7 %long_n3\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, -2ll), + // Test case 10: fold 7 rem 3 (signed) + InstructionFoldingCase<uint64_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 = OpSRem %long %long_7 %long_n3\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, 1ll), + // Test case 11: fold -7 mod 3 (signed) + InstructionFoldingCase<uint64_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 = OpSMod %long %long_n7 %long_3\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, 2ll), + // Test case 12: fold -7 rem 3 (signed) + InstructionFoldingCase<uint64_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 = OpSRem %long %long_n7 %long_3\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, -1ll), + // Test case 13: fold long(-24) + InstructionFoldingCase<uint64_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 = OpSConvert %long %int_n24\n" + "OpReturn\n" + + "OpFunctionEnd", + 2, -24ll), + // Test case 14: fold long(-24) + InstructionFoldingCase<uint64_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 = OpSConvert %long %int_10\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 10ll), + // Test case 15: fold long(-24(short)). + // The upper bits should be cleared. So 0xFFFFFFE8 should become + // 0x000000000000FFE8. + InstructionFoldingCase<uint64_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + "%2 = OpUConvert %ulong %short_n5\n" + + "OpReturn\n" + "OpFunctionEnd", + 2, 65531ull))); - // Make sure the instruction folded as expected. - EXPECT_EQ(succeeded, inst == nullptr || inst->opcode() != original_opcode); - if (succeeded && inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite}; - EXPECT_THAT(opcodes, Contains(inst->opcode())); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - const std::vector<const analysis::Constant*>& componenets = - result->AsVectorConstant()->GetComponents(); - EXPECT_EQ(componenets.size(), tc.expected_result.size()); - for (size_t i = 0; i < componenets.size(); i++) { - EXPECT_EQ(tc.expected_result[i], componenets[i]->GetU32()); - } - } - } +using UIntVectorInstructionFoldingTest = + ::testing::TestWithParam<InstructionFoldingCase<std::vector<uint32_t>>>; + +TEST_P(UIntVectorInstructionFoldingTest, 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_1); + CheckForExpectedVectorConstant( + inst, tc.expected_result, + [](const analysis::Constant* c) { return c->GetU32(); }); } // clang-format off -INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest, +INSTANTIATE_TEST_SUITE_P(TestCase, UIntVectorInstructionFoldingTest, ::testing::Values( // Test case 0: fold 0*n InstructionFoldingCase<std::vector<uint32_t>>( @@ -943,24 +1305,6 @@ "OpReturn\n" + "OpFunctionEnd", 2, {0,3}), - 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 4294967295 3\n" + - "OpReturn\n" + - "OpFunctionEnd", - 2, {0,0}), - 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 4294967295 \n" + - "OpReturn\n" + - "OpFunctionEnd", - 2, {0,0}), // Test case 4: fold bit-cast int -24 to unsigned int InstructionFoldingCase<std::vector<uint32_t>>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -970,46 +1314,156 @@ "%2 = OpBitcast %v2uint %v2int_min_max\n" + "OpReturn\n" + "OpFunctionEnd", - 2, {2147483648, 2147483647}) + 2, {2147483648, 2147483647}), + // Test case 5: fold SNegate vector of uint + 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 = 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) + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpIAdd %v2uint %v2uint_0x3f800000_0xbf800000 %v2uint_0x3f800000_0xbf800000\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0x7f000000u, 0x7f000000u}), + // Test case 6: fold vector components of uint + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSConvert %v2int %v2short_2_n5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {2,static_cast<uint32_t>(-5)}), + // Test case 6: 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}) )); // clang-format on +using IntVectorInstructionFoldingTest = + ::testing::TestWithParam<InstructionFoldingCase<std::vector<int32_t>>>; + +TEST_P(IntVectorInstructionFoldingTest, 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_1); + + CheckForExpectedVectorConstant( + inst, tc.expected_result, + [](const analysis::Constant* c) { return c->GetS32(); }); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest, +::testing::Values( + // Test case 0: fold negate of a vector + InstructionFoldingCase<std::vector<int32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %v2int %v2int_2_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {-2, -3}), + // Test case 1: fold negate of a vector containing negative values. + InstructionFoldingCase<std::vector<int32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %v2int %v2int_n1_n24\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1, 24}), + // Test case 2: fold negate of a vector at the limits + InstructionFoldingCase<std::vector<int32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %v2int %v2int_min_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {INT_MIN, -INT_MAX}), + // Test case 3: fold vector components of int + InstructionFoldingCase<std::vector<int32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpIMul %v2int %v2int_2_3 %v2int_2_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {4,9}) +)); +// clang-format on + +using LongIntVectorInstructionFoldingTest = + ::testing::TestWithParam<InstructionFoldingCase<std::vector<uint64_t>>>; + +TEST_P(LongIntVectorInstructionFoldingTest, 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_1); + CheckForExpectedVectorConstant( + inst, tc.expected_result, + [](const analysis::Constant* c) { return c->GetU64(); }); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, LongIntVectorInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold {2,2} + {2,3} (Testing that the vector logic works + // correctly. Scalar tests will check that the 64-bit values are correctly + // folded.) + InstructionFoldingCase<std::vector<uint64_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 = OpIAdd %v2long %v2long_2_2 %v2long_2_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {4,5}), + // Test case 0: fold {2,2} / {2,3} (Testing that the vector logic works + // correctly. Scalar tests will check that the 64-bit values are correctly + // folded.) + InstructionFoldingCase<std::vector<uint64_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 = OpSDiv %v2long %v2long_2_2 %v2long_2_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1,0}) + )); +// clang-format on + using DoubleVectorInstructionFoldingTest = ::testing::TestWithParam<InstructionFoldingCase<std::vector<double>>>; TEST_P(DoubleVectorInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); - - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_TRUE(succeeded); - if (succeeded && inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite}; - EXPECT_THAT(opcodes, Contains(inst->opcode())); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - const std::vector<const analysis::Constant*>& componenets = - result->AsVectorConstant()->GetComponents(); - EXPECT_EQ(componenets.size(), tc.expected_result.size()); - for (size_t i = 0; i < componenets.size(); i++) { - EXPECT_EQ(tc.expected_result[i], componenets[i]->GetDouble()); - } - } - } + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + CheckForExpectedVectorConstant( + inst, tc.expected_result, + [](const analysis::Constant* c) { return c->GetDouble(); }); } // clang-format off @@ -1104,37 +1558,10 @@ TEST_P(FloatVectorInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); - - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - spv::Op original_opcode = inst->opcode(); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_EQ(succeeded, inst == nullptr || inst->opcode() != original_opcode); - if (succeeded && inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - std::vector<spv::Op> opcodes = {spv::Op::OpConstantComposite}; - EXPECT_THAT(opcodes, Contains(inst->opcode())); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - const std::vector<const analysis::Constant*>& componenets = - result->AsVectorConstant()->GetComponents(); - EXPECT_EQ(componenets.size(), tc.expected_result.size()); - for (size_t i = 0; i < componenets.size(); i++) { - EXPECT_EQ(tc.expected_result[i], componenets[i]->GetFloat()); - } - } - } + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1); + CheckForExpectedVectorConstant(inst, tc.expected_result, [](const analysis::Constant* c){ return c->GetFloat();}); } // clang-format off @@ -1239,39 +1666,89 @@ 2, {4.0,8.0,12.0,16.0}) )); // clang-format on + +using FloatMatrixInstructionFoldingTest = ::testing::TestWithParam< + InstructionFoldingCase<std::vector<std::vector<float>>>>; + +TEST_P(FloatMatrixInstructionFoldingTest, 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_1); + + EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); + if (inst->opcode() == spv::Op::OpCopyObject) { + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* result = const_mgr->GetConstantFromInst(inst); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + std::vector<const analysis::Constant*> matrix = + result->AsMatrixConstant()->GetComponents(); + EXPECT_EQ(matrix.size(), tc.expected_result.size()); + for (size_t c = 0; c < matrix.size(); c++) { + if (matrix[c]->AsNullConstant() != nullptr) { + matrix[c] = const_mgr->GetNullCompositeConstant(matrix[c]->type()); + } + const analysis::VectorConstant* column_const = + matrix[c]->AsVectorConstant(); + ASSERT_NE(column_const, nullptr); + const std::vector<const analysis::Constant*>& column = + column_const->GetComponents(); + EXPECT_EQ(column.size(), tc.expected_result[c].size()); + for (size_t r = 0; r < column.size(); r++) { + EXPECT_EQ(tc.expected_result[c][r], column[r]->GetFloat()); + } + } + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, FloatMatrixInstructionFoldingTest, +::testing::Values( + // Test case 0: OpTranspose square null matrix + InstructionFoldingCase<std::vector<std::vector<float>>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpTranspose %mat4v4float %mat4v4float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {{0.0f, 0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f, 0.0f},{0.0f, 0.0f, 0.0f, 0.0f}}), + // Test case 1: OpTranspose rectangular null matrix + InstructionFoldingCase<std::vector<std::vector<float>>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpTranspose %mat4v2float %mat2v4float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {{0.0f, 0.0f},{0.0f, 0.0f},{0.0f, 0.0f},{0.0f, 0.0f}}), + InstructionFoldingCase<std::vector<std::vector<float>>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpTranspose %mat4v4float %mat4v4float_1_2_3_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {{1.0f, 1.0f, 1.0f, 1.0f},{2.0f, 2.0f, 2.0f, 2.0f},{3.0f, 3.0f, 3.0f, 3.0f},{4.0f, 4.0f, 4.0f, 4.0f}}) +)); +// clang-format on + using BooleanInstructionFoldingTest = ::testing::TestWithParam<InstructionFoldingCase<bool>>; TEST_P(BooleanInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); - - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_TRUE(succeeded); - if (inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - std::vector<spv::Op> bool_opcodes = {spv::Op::OpConstantTrue, - spv::Op::OpConstantFalse}; - EXPECT_THAT(bool_opcodes, Contains(inst->opcode())); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::BoolConstant* result = - const_mrg->GetConstantFromInst(inst)->AsBoolConstant(); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - EXPECT_EQ(result->value(), tc.expected_result); - } - } + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + CheckForExpectedScalarConstant( + inst, tc.expected_result, + [](const analysis::Constant* c) { return c->AsBoolConstant()->value(); }); } // clang-format off @@ -1857,35 +2334,15 @@ TEST_P(FloatInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_TRUE(succeeded); - if (inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - EXPECT_EQ(inst->opcode(), spv::Op::OpConstant); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::FloatConstant* result = - const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - if (!std::isnan(tc.expected_result)) { - EXPECT_EQ(result->GetFloatValue(), tc.expected_result); - } else { - EXPECT_TRUE(std::isnan(result->GetFloatValue())); - } - } - } + CheckForExpectedScalarConstant(inst, tc.expected_result, + [](const analysis::Constant* c) { + return c->AsFloatConstant()->GetFloatValue(); + }); } // Not testing NaNs because there are no expectations concerning NaNs according @@ -2290,35 +2747,14 @@ TEST_P(DoubleInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); - - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_TRUE(succeeded); - if (inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); - inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); - EXPECT_EQ(inst->opcode(), spv::Op::OpConstant); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::FloatConstant* result = - const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - if (!std::isnan(tc.expected_result)) { - EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); - } else { - EXPECT_TRUE(std::isnan(result->GetDoubleValue())); - } - } - } + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); + CheckForExpectedScalarConstant( + inst, tc.expected_result, [](const analysis::Constant* c) { + return c->AsFloatConstant()->GetDoubleValue(); + }); } // clang-format off @@ -3176,32 +3612,22 @@ TEST_P(IntegerInstructionFoldingTestWithMap, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + GetInstructionToFold(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); inst = context->get_instruction_folder().FoldInstructionToConstant(inst, tc.id_map); - - // Make sure the instruction folded as expected. EXPECT_NE(inst, nullptr); - if (inst != nullptr) { - EXPECT_EQ(inst->opcode(), spv::Op::OpConstant); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::IntConstant* result = - const_mrg->GetConstantFromInst(inst)->AsIntConstant(); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - EXPECT_EQ(result->GetU32BitValue(), tc.expected_result); - } - } + + CheckForExpectedScalarConstant(inst, tc.expected_result, + [](const analysis::Constant* c) { + return c->AsIntConstant()->GetU32BitValue(); + }); } // clang-format off + INSTANTIATE_TEST_SUITE_P(TestCase, IntegerInstructionFoldingTestWithMap, ::testing::Values( // Test case 0: fold %3 = 0; %3 * n @@ -3224,32 +3650,16 @@ TEST_P(BooleanInstructionFoldingTestWithMap, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); - - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + GetInstructionToFold(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5); inst = context->get_instruction_folder().FoldInstructionToConstant(inst, tc.id_map); - - // Make sure the instruction folded as expected. - EXPECT_NE(inst, nullptr); - if (inst != nullptr) { - std::vector<spv::Op> bool_opcodes = {spv::Op::OpConstantTrue, - spv::Op::OpConstantFalse}; - EXPECT_THAT(bool_opcodes, Contains(inst->opcode())); - analysis::ConstantManager* const_mrg = context->get_constant_mgr(); - const analysis::BoolConstant* result = - const_mrg->GetConstantFromInst(inst)->AsBoolConstant(); - EXPECT_NE(result, nullptr); - if (result != nullptr) { - EXPECT_EQ(result->value(), tc.expected_result); - } - } + ASSERT_NE(inst, nullptr); + CheckForExpectedScalarConstant( + inst, tc.expected_result, + [](const analysis::Constant* c) { return c->AsBoolConstant()->value(); }); } // clang-format off @@ -3275,30 +3685,15 @@ TEST_P(GeneralInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_EQ(inst->result_id(), original_inst->result_id()); - EXPECT_EQ(inst->type_id(), original_inst->type_id()); - EXPECT_TRUE((!succeeded) == (tc.expected_result == 0)); - if (succeeded) { + EXPECT_TRUE((inst == nullptr) == (tc.expected_result == 0)); + if (inst != nullptr) { EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject); EXPECT_EQ(inst->GetSingleWordInOperand(0), tc.expected_result); - } else { - EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands()); - for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { - EXPECT_EQ(inst->GetOperand(i), original_inst->GetOperand(i)); - } } } @@ -3725,23 +4120,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 0), - // Test case 38: Don't fold 2 + 3 (long), bad length - InstructionFoldingCase<uint32_t>( - Header() + "%main = OpFunction %void None %void_func\n" + - "%main_lab = OpLabel\n" + - "%2 = OpIAdd %long %long_2 %long_3\n" + - "OpReturn\n" + - "OpFunctionEnd", - 2, 0), - // Test case 39: Don't fold 2 + 3 (short), bad length - InstructionFoldingCase<uint32_t>( - Header() + "%main = OpFunction %void None %void_func\n" + - "%main_lab = OpLabel\n" + - "%2 = OpIAdd %short %short_2 %short_3\n" + - "OpReturn\n" + - "OpFunctionEnd", - 2, 0), - // Test case 40: fold 1*n + // Test case 38: fold 1*n InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -3751,7 +4130,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 41: fold n*1 + // Test case 39: fold n*1 InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + "%main_lab = OpLabel\n" + @@ -3761,7 +4140,7 @@ "OpReturn\n" + "OpFunctionEnd", 2, 3), - // Test case 42: Don't fold comparisons of 64-bit types + // Test case 40: Don't fold comparisons of 64-bit types // (https://github.com/KhronosGroup/SPIRV-Tools/issues/3343). InstructionFoldingCase<uint32_t>( Header() + "%main = OpFunction %void None %void_func\n" + @@ -4299,6 +4678,17 @@ "%2 = OpDot %half %half_0_1 %half_0_1\n" + "OpReturn\n" + "OpFunctionEnd", + 2, 0), + // Test case 23: 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" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_half Function\n" + + "%3 = OpLoad %half %n\n" + + "%2 = OpFDiv %half %half_1 %half_2\n" + + "OpReturn\n" + + "OpFunctionEnd", 2, 0) )); @@ -4611,7 +5001,31 @@ "%2 = OpIAdd %v2int %v2int_0_0 %3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 3) + 2, 3), + // Test case 8: 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>( + 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 4294967295 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 9: 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>( + 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 4294967295 \n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) )); INSTANTIATE_TEST_SUITE_P(ClampAndCmpLHS, GeneralInstructionFoldingTest, @@ -4933,30 +5347,15 @@ TEST_P(ToNegateFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = + FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - - // Make sure the instruction folded as expected. - EXPECT_EQ(inst->result_id(), original_inst->result_id()); - EXPECT_EQ(inst->type_id(), original_inst->type_id()); - EXPECT_TRUE((!succeeded) == (tc.expected_result == 0)); - if (succeeded) { + EXPECT_TRUE((inst == nullptr) == (tc.expected_result == 0)); + if (inst != nullptr) { EXPECT_EQ(inst->opcode(), spv::Op::OpFNegate); EXPECT_EQ(inst->GetSingleWordInOperand(0), tc.expected_result); - } else { - EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands()); - for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { - EXPECT_EQ(inst->GetOperand(i), original_inst->GetOperand(i)); - } } } @@ -5055,19 +5454,12 @@ TEST_P(MatchingInstructionFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - EXPECT_EQ(succeeded, tc.expected_result); - if (succeeded) { + EXPECT_EQ(inst != nullptr, tc.expected_result); + if (inst != nullptr) { Match(tc.test_body, context.get()); } } @@ -7418,7 +7810,24 @@ "%4 = OpCompositeExtract %int %struct_v2int_int_int 3\n" + "OpReturn\n" + "OpFunctionEnd", - 4, false) + 4, false), + // Test case 18: Fold when every element of an array is inserted. + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK-DAG: [[arr_type:%\\w+]] = OpTypeArray [[int]] [[int2]]\n" + + "; CHECK-DAG: [[int10:%\\w+]] = OpConstant [[int]] 10\n" + + "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[arr_type]] [[int10]] [[int1]]\n" + + "; CHECK: %5 = OpCopyObject [[arr_type]] [[construct]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%4 = OpCompositeInsert %int_arr_2 %int_10 %int_arr_2_undef 0\n" + + "%5 = OpCompositeInsert %int_arr_2 %int_1 %4 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true) )); INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest, @@ -7797,27 +8206,13 @@ TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1); - // Fold the instruction to test. - Instruction* inst = nullptr; - Function* func = &*context->module()->begin(); - for (auto& bb : *func) { - Instruction* terminator = bb.terminator(); - if (terminator->IsReturnOrAbort()) { - inst = terminator->PreviousNode(); - break; - } - } - assert(inst && "Invalid test. Could not find instruction to fold."); - std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - EXPECT_EQ(succeeded, tc.expected_result); - if (succeeded) { + // Find the instruction to test. + EXPECT_EQ(inst != nullptr, tc.expected_result); + if (inst != nullptr) { Match(tc.test_body, context.get()); } } @@ -8157,8 +8552,9 @@ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); ASSERT_NE(nullptr, context); - // Fold the instruction to test. + // Find the first entry point. That is the instruction we want to fold. Instruction* inst = nullptr; + ASSERT_FALSE(context->module()->entry_points().empty()); inst = &*context->module()->entry_points().begin(); assert(inst && "Invalid test. Could not find entry point instruction to fold."); std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); @@ -8251,19 +8647,12 @@ TEST_P(SPV14FoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_4); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - EXPECT_EQ(succeeded, tc.expected_result); - if (succeeded) { + EXPECT_EQ(inst != nullptr, tc.expected_result); + if (inst != nullptr) { Match(tc.test_body, context.get()); } } @@ -8364,19 +8753,12 @@ TEST_P(FloatControlsFoldingTest, Case) { const auto& tc = GetParam(); - // Build module. - std::unique_ptr<IRContext> context = - BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, tc.test_body, - SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - ASSERT_NE(nullptr, context); + std::unique_ptr<IRContext> context; + Instruction* inst; + std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_4); - // Fold the instruction to test. - analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); - Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - std::unique_ptr<Instruction> original_inst(inst->Clone(context.get())); - bool succeeded = context->get_instruction_folder().FoldInstruction(inst); - EXPECT_EQ(succeeded, tc.expected_result); - if (succeeded) { + EXPECT_EQ(inst != nullptr, tc.expected_result); + if (inst != nullptr) { Match(tc.test_body, context.get()); } } @@ -8448,6 +8830,7 @@ %v3int = OpTypeVector %int 3 %Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant %gSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %110 = OpConstantComposite %v2int %5 %5 %101 = OpConstantComposite %v2int %int_n1 %int_n1 %20 = OpConstantComposite %v2float %float_0 %float_0 %main = OpFunction %void None %22 @@ -8507,7 +8890,12 @@ InstructionFoldingCase<bool>(ImageOperandsTestBody( " OpImageWrite %88 %5 %101 Offset %101 \n" "; CHECK: OpImageWrite %88 %5 %101 ConstOffset %101 \n") - , 0 /* No result-id */, true) + , 0 /* No result-id */, true), + // Test case 8: OpImageFetch with zero constant Offset + InstructionFoldingCase<bool>(ImageOperandsTestBody( + " %89 = OpImageFetch %10 %88 %101 Lod|Offset %5 %110 \n" + "; CHECK: %89 = OpImageFetch %10 %88 %101 Lod %5 \n") + , 89, true) )); } // namespace
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index 1e5d9f3..bf79181 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp
@@ -4422,6 +4422,55 @@ SinglePassRunAndMatch<InlineExhaustivePass>(text, true); } +TEST_F(InlineTest, DecorateReturnVariableWithAliasedPointer) { + const std::string text = R"(OpCapability Int64 + OpCapability VariablePointers + OpCapability PhysicalStorageBufferAddresses + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_variable_pointers" + OpExtension "SPV_KHR_physical_storage_buffer" + 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 +; CHECK: OpDecorate %22 AliasedPointer + %void = OpTypeVoid + %8 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer__struct_5 PhysicalStorageBuffer + %_struct_3 = OpTypeStruct %int %_ptr_PhysicalStorageBuffer__struct_5 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int + %_struct_5 = OpTypeStruct %int %int + %11 = OpTypeFunction %_ptr_PhysicalStorageBuffer_int %_ptr_PhysicalStorageBuffer__struct_5 +%_ptr_PhysicalStorageBuffer__struct_5 = OpTypePointer PhysicalStorageBuffer %_struct_5 +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 + %1 = OpFunction %void None %8 + %13 = OpLabel + %14 = OpVariable %_ptr_Function__struct_3 Function + %15 = OpLoad %_struct_3 %14 + %16 = OpCompositeExtract %_ptr_PhysicalStorageBuffer__struct_5 %15 1 + %17 = OpFunctionCall %_ptr_PhysicalStorageBuffer_int %18 %16 + OpReturn + OpFunctionEnd + %18 = OpFunction %_ptr_PhysicalStorageBuffer_int None %11 + %6 = OpFunctionParameter %_ptr_PhysicalStorageBuffer__struct_5 + %19 = OpLabel + %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %6 %int_0 + OpReturnValue %20 + 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/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp index dd4b6f6..08da367 100644 --- a/test/opt/inst_bindless_check_test.cpp +++ b/test/opt/inst_bindless_check_test.cpp
@@ -27,504 +27,18 @@ using InstBindlessTest = PassTest<::testing::Test>; -static const std::string kOutputDecorations = R"( -; CHECK: OpDecorate [[output_buffer_type:%inst_bindless_OutputBuffer]] Block -; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0 -; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4 -; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7 -; CHECK: OpDecorate [[output_buffer_var]] Binding 0 +static const std::string kFuncName = "inst_bindless_check_desc"; + +static const std::string kImportDeco = R"( +;CHECK: OpDecorate %)" + kFuncName + R"( LinkageAttributes ")" + + kFuncName + R"(" Import )"; -static const std::string kOutputGlobals = R"( -; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %uint %_runtimearr_uint -; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]] -; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer +static const std::string kImportStub = R"( +;CHECK: %)" + kFuncName + R"( = OpFunction %bool None {{%\w+}} +;CHECK: OpFunctionEnd )"; -static const std::string kStreamWrite6Begin = R"( -; CHECK: %inst_bindless_stream_write_6 = OpFunction %void None {{%\w+}} -; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_6:%\w+]] = OpFunctionParameter %uint -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 -; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_12 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_12 -; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2 -; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_12 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_23 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_1]] -)"; - -static const std::string kStreamWrite6End = R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_2]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_3]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_4]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_5]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_6]] -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturn -; CHECK: OpFunctionEnd -)"; - -// clang-format off -static const std::string kStreamWrite6Frag = kStreamWrite6Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord -; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite6End; - -static const std::string kStreamWrite6Tese = kStreamWrite6Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_2 -; CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord -; CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite6End; - -static const std::string kStreamWrite6Vert = kStreamWrite6Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite6End; - -static const std::string kStreamWrite6Compute = kStreamWrite6Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite6End; - -static const std::string kStreamWrite6Ray = kStreamWrite6Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite6End; -// clang-format on - -static const std::string kStreamWrite7Begin = R"( -; CHECK: %inst_bindless_stream_write_7 = OpFunction %void None {{%\w+}} -; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_6:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_7:%\w+]] = OpFunctionParameter %uint -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 -; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_13 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_13 -; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2 -; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_13 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_23 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_1]] -)"; - -static const std::string kStreamWrite7End = R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_2]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_3]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_4]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_5]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_6]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_12 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_7]] -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturn -; CHECK: OpFunctionEnd -)"; - -// clang-format off -static const std::string kStreamWrite7Frag = kStreamWrite7Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord -; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite7End; - -static const std::string kStreamWrite7Vert = kStreamWrite7Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite7End; -// clang-format on - -static const std::string kInputDecorations = R"( -; CHECK: OpDecorate [[desc_set_struct:%inst_bindless_DescriptorSetData]] Block -; CHECK: OpMemberDecorate [[desc_set_struct]] 0 Offset 0 -; CHECK: OpMemberDecorate [[desc_set_struct]] 1 Offset 4 -; CHECK: OpDecorate [[input_buffer_type:%inst_bindless_InputBuffer]] Block -; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0 -; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7 -; CHECK: OpDecorate [[input_buffer_var]] Binding 1 -)"; - -static const std::string kInputGlobals = R"( -; CHECK: [[desc_set_struct]] = OpTypeStruct %uint %_runtimearr_uint -; CHECK: [[desc_set_ptr:%\w+]] = OpTypePointer PhysicalStorageBuffer [[desc_set_struct]] -; CHECK: [[desc_set_ptr_array:%\w+]] = OpTypeArray [[desc_set_ptr]] %uint_32 -; CHECK: [[input_buffer_type]] = OpTypeStruct [[desc_set_ptr_array]] -; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]] -; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer -)"; - -static const std::string kReadBindingLength = R"( -; CHECK: %inst_bindless_read_binding_length = OpFunction %uint None {{%\w+}} -; CHECK: [[bl_desc_set_idx:%\w+]] = OpFunctionParameter %uint -; CHECK: [[bl_binding_idx:%\w+]] = OpFunctionParameter %uint -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpUGreaterThanEqual %bool [[bl_desc_set_idx]] %uint_32 -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturnValue %uint_0 -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_inst_bindless_DescriptorSetData %inst_bindless_input_buffer %uint_0 [[bl_desc_set_idx]] -; CHECK: {{%\w+}} = OpLoad %_ptr_PhysicalStorageBuffer_inst_bindless_DescriptorSetData {{%\w+}} -; CHECK: {{%\w+}} = OpBitcast %v2uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpIEqual %bool {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpIEqual %bool {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpLogicalAnd %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturnValue %uint_0 -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_PhysicalStorageBuffer_uint {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} Aligned 8 -; CHECK: {{%\w+}} = OpUGreaterThanEqual %bool [[bl_binding_idx]] {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturnValue %uint_0 -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_PhysicalStorageBuffer_uint {{%\w+}} %uint_1 [[bl_binding_idx]] -; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} Aligned 4 -; CHECK: OpReturnValue {{%\w+}} -; CHECK: OpFunctionEnd -)"; - -static const std::string kReadDescInit = R"( -; CHECK: %inst_bindless_read_desc_init = OpFunction %uint None {{%\w+}} -; CHECK: [[di_desc_set_idx:%\w+]] = OpFunctionParameter %uint -; CHECK: [[di_binding_idx:%\w+]] = OpFunctionParameter %uint -; CHECK: [[di_desc_idx:%\w+]] = OpFunctionParameter %uint -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpUGreaterThanEqual %bool [[di_desc_set_idx]] %uint_32 -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturnValue %uint_0 -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_inst_bindless_DescriptorSetData %inst_bindless_input_buffer %uint_0 [[di_desc_set_idx]] -; CHECK: {{%\w+}} = OpLoad %_ptr_PhysicalStorageBuffer_inst_bindless_DescriptorSetData {{%\w+}} -; CHECK: {{%\w+}} = OpBitcast %v2uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpIEqual %bool {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpIEqual %bool {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpLogicalAnd %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturnValue %uint_0 -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_PhysicalStorageBuffer_uint {{%\w+}} %uint_0 -; CHECK: [[di_num_bindings:%\w+]] = OpLoad %uint {{%\w+}} Aligned 8 -; CHECK: {{%\w+}} = OpUGreaterThanEqual %bool [[di_binding_idx]] [[di_num_bindings]] -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturnValue %uint_0 -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpAccessChain %_ptr_PhysicalStorageBuffer_uint {{%\w+}} %uint_1 {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} Aligned 4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_PhysicalStorageBuffer_uint {{%\w+}} %uint_1 {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}} Aligned 4 -; CHECK: OpReturnValue {{%\w+}} -; CHECK: OpFunctionEnd -)"; - -TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) { - // Texture2D g_tColor[128]; - // - // SamplerState g_sAniso; - // - // struct PS_INPUT - // { - // float2 vTextureCoords : TEXCOORD2; - // }; - // - // struct PS_OUTPUT - // { - // float4 vColor : SV_Target0; - // }; - // - // PS_OUTPUT MainPs(PS_INPUT i) - // { - // PS_OUTPUT ps_output; - // - // ps_output.vColor = g_tColor[ 37 ].Sample(g_sAniso, i.vTextureCoords.xy); - // return ps_output; - // } - - const std::string before = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 5 -OpDecorate %g_sAniso DescriptorSet 3 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%8 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%int_37 = OpConstant %int 37 -%15 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_15_uint_128 = OpTypeArray %15 %uint_128 -%_ptr_UniformConstant__arr_15_uint_128 = OpTypePointer UniformConstant %_arr_15_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_15_uint_128 UniformConstant -%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 -%21 = OpTypeSampler -%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 -%g_sAniso = OpVariable %_ptr_UniformConstant_21 UniformConstant -%23 = OpTypeSampledImage %15 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%MainPs = OpFunction %void None %8 -%26 = OpLabel -%27 = OpLoad %v2float %i_vTextureCoords -%28 = OpAccessChain %_ptr_UniformConstant_15 %g_tColor %int_37 -%29 = OpLoad %15 %28 -%30 = OpLoad %21 %g_sAniso -%31 = OpSampledImage %23 %29 %30 -%32 = OpImageSampleImplicitLod %v4float %31 %27 -OpStore %_entryPointOutput_vColor %32 -OpReturn -OpFunctionEnd -)"; - - SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>( - before, before, true, true, 7u, 23u, false, false, false, false, false); -} - -TEST_F(InstBindlessTest, NoInstrumentNonBindless) { - // This test verifies that the pass will correctly not instrument vanilla - // texture sample. - // - // Texture2D g_tColor; - // - // SamplerState g_sAniso; - // - // struct PS_INPUT - // { - // float2 vTextureCoords : TEXCOORD2; - // }; - // - // struct PS_OUTPUT - // { - // float4 vColor : SV_Target0; - // }; - // - // PS_OUTPUT MainPs(PS_INPUT i) - // { - // PS_OUTPUT ps_output; - // ps_output.vColor = - // g_tColor.Sample(g_sAniso, i.vTextureCoords.xy); - // return ps_output; - // } - - const std::string whole_file = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 6 -OpDecorate %g_tColor Binding 4 -OpDecorate %g_sAniso DescriptorSet 6 -OpDecorate %g_sAniso Binding 4 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -%void = OpTypeVoid -%8 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%12 = OpTypeImage %float 2D 0 0 0 1 Unknown -%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 -%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant -%14 = OpTypeSampler -%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 -%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant -%16 = OpTypeSampledImage %12 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%MainPs = OpFunction %void None %8 -%19 = OpLabel -%20 = OpLoad %v2float %i_vTextureCoords -%21 = OpLoad %12 %g_tColor -%22 = OpLoad %14 %g_sAniso -%23 = OpSampledImage %16 %21 %22 -%24 = OpImageSampleImplicitLod %v4float %23 %20 -OpStore %_entryPointOutput_vColor %24 -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<InstBindlessCheckPass>(whole_file, whole_file, true, - true, 7u, 23u, false, false, - false, false, false); -} - TEST_F(InstBindlessTest, Simple) { // Texture2D g_tColor[128]; // @@ -555,11 +69,11 @@ const std::string entry = R"( OpCapability Shader -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 )"; @@ -580,10 +94,10 @@ OpDecorate %PerViewConstantBuffer_t Block OpDecorate %g_sAniso DescriptorSet 0 OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %_entryPointOutput_vColor Location 0)" ++ kImportDeco + +R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord )"; const std::string consts_types_vars = R"( @@ -613,15 +127,10 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -; CHECK: %bool = OpTypeBool -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %MainPs = OpFunction %void None %10 @@ -635,32 +144,30 @@ %36 = OpSampledImage %26 %34 %35 %37 = OpImageSampleImplicitLod %v4float %36 %30 OpStore %_entryPointOutput_vColor %37 -; CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30 -; CHECK-NOT: OpStore %_entryPointOutput_vColor %37 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} %uint_128 -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %16 %33 -; CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35 -; CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_56 %uint_0 %uint_3 %uint_0 %32 %uint_128 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpStore %_entryPointOutput_vColor [[phi_result]] +;CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %37 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_57 {{%\w+}} %uint_3 %uint_0 %32 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %16 %33 +;CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %30 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %_entryPointOutput_vColor [[phi_result]] OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Frag; + // clang-format on SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>( - entry + names_annots + consts_types_vars + main_func + output_func, true, - 7u, 23u, false, false, false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>( + entry + names_annots + consts_types_vars + kImportStub + main_func, true, + 23u); } TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { @@ -697,11 +204,11 @@ // clang-format off const std::string defs = R"( OpCapability Shader -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -720,9 +227,8 @@ OpDecorate %g_sAniso DescriptorSet 3 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -750,20 +256,14 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -; CHECK: %bool = OpTypeBool -; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on - const std::string main_func = - R"(%MainPs = OpFunction %void None %10 + const std::string main_func = R"( +%MainPs = OpFunction %void None %10 %30 = OpLabel %31 = OpLoad %v2float %i_vTextureCoords %32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 @@ -773,20 +273,24 @@ %36 = OpLoad %25 %g_sAniso %37 = OpSampledImage %27 %35 %36 %38 = OpImageSampleImplicitLod %v4float %37 %31 -; CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31 -; CHECK: %48 = OpULessThan %bool %33 %uint_128 -; CHECK: OpSelectionMerge %49 None -; CHECK: OpBranchConditional %48 %50 %51 -; CHECK: %50 = OpLabel -; CHECK: %52 = OpLoad %17 %34 -; CHECK: %53 = OpSampledImage %27 %52 %36 -; CHECK: %54 = OpImageSampleImplicitLod %v4float %53 %31 -; CHECK: OpBranch %49 -; CHECK: %51 = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_58 %uint_0 %uint_3 %uint_4 %33 %uint_128 -; CHECK: OpBranch %49 -; CHECK: %49 = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float %54 %50 [[null_v4float]] %51 +;CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_60 {{%\w+}} %uint_3 %uint_4 %33 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %17 %34 +;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} %39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 %40 = OpLoad %uint %39 %41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 @@ -794,33 +298,35 @@ %43 = OpSampledImage %27 %42 %36 %44 = OpImageSampleImplicitLod %v4float %43 %31 %45 = OpFAdd %v4float %38 %44 -; CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31 -; CHECK-NOT: %45 = OpFAdd %v4float %38 %44 -; CHECK: {{%\w+}} = OpULessThan %bool %40 %uint_128 -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %17 %41 -; CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36 -; CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_64 %uint_0 %uint_3 %uint_4 %40 %uint_128 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %45 = OpFAdd %v4float {{%\w+}} {{%\w+}} +;CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31 +;CHECK-NOT: %45 = OpFAdd %v4float %38 %44 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_66 {{%\w+}} %uint_3 %uint_4 %40 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %17 %41 +;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: %45 = OpFAdd %v4float {{%\w+}} {{%\w+}} OpStore %_entryPointOutput_vColor %45 OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Frag; + // clang-format on SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func, - true, 7u, 23u, false, false, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstrumentOpImage) { @@ -832,11 +338,11 @@ const std::string defs = R"( OpCapability Shader OpCapability StorageImageReadWithoutFormat -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -852,9 +358,8 @@ OpDecorate %PerViewConstantBuffer_t Block OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -878,17 +383,10 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2int Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -; CHECK: uint_0 = OpConstant %uint 0 -; CHECK: bool = OpTypeBool -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %MainPs = OpFunction %void None %3 @@ -901,32 +399,34 @@ %75 = OpImage %20 %66 %71 = OpImageRead %v4float %75 %53 OpStore %_entryPointOutput_vColor %71 -; CHECK-NOT: %71 = OpImageRead %v4float %75 %53 -; CHECK-NOT: OpStore %_entryPointOutput_vColor %71 -; CHECK: %78 = OpULessThan %bool %64 %uint_128 -; CHECK: OpSelectionMerge %79 None -; CHECK: OpBranchConditional %78 %80 %81 -; CHECK: %80 = OpLabel -; CHECK: %82 = OpLoad %39 %65 -; CHECK: %83 = OpImage %20 %82 -; CHECK: %84 = OpImageRead %v4float %83 %53 -; CHECK: OpBranch %79 -; CHECK: %81 = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_3 %uint_9 %64 %uint_128 -; CHECK: OpBranch %79 -; CHECK: %79 = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float %84 %80 [[null_v4float]] %81 -; CHECK: OpStore %_entryPointOutput_vColor {{%\w+}} +;CHECK-NOT: %71 = OpImageRead %v4float %75 %53 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %71 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_3 %uint_9 %64 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %39 %65 +;CHECK: {{%\w+}} = OpImage %20 {{%\w+}} +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %53 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %_entryPointOutput_vColor {{%\w+}} OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Frag; + // clang-format on SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func, - true, 7u, 23u, false, false, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstrumentSampledImage) { @@ -937,11 +437,11 @@ // clang-format off const std::string defs = R"( OpCapability Shader -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -957,8 +457,8 @@ OpDecorate %PerViewConstantBuffer_t Block OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -982,17 +482,10 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -; CHECK: uint_0 = OpConstant %uint 0 -; CHECK: bool = OpTypeBool -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %MainPs = OpFunction %void None %3 @@ -1004,31 +497,33 @@ %66 = OpLoad %39 %65 %71 = OpImageSampleImplicitLod %v4float %66 %53 OpStore %_entryPointOutput_vColor %71 -; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %66 %53 -; CHECK-NOT: OpStore %_entryPointOutput_vColor %71 -; CHECK: %74 = OpULessThan %bool %64 %uint_128 -; CHECK: OpSelectionMerge %75 None -; CHECK: OpBranchConditional %74 %76 %77 -; CHECK: %76 = OpLabel -; CHECK: %78 = OpLoad %39 %65 -; CHECK: %79 = OpImageSampleImplicitLod %v4float %78 %53 -; CHECK: OpBranch %75 -; CHECK: %77 = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_49 %uint_0 %uint_4 %uint_11 %64 %uint_128 -; CHECK: OpBranch %75 -; CHECK: %75 = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float %79 %76 [[null_v4float]] %77 -; CHECK: OpStore %_entryPointOutput_vColor {{%\w+}} +;CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %66 %53 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %71 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_50 {{%\w+}} %uint_4 %uint_11 %64 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %39 %65 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %53 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %_entryPointOutput_vColor {{%\w+}} OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Frag; + // clang-format on SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func, - true, 7u, 23u, false, false, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstrumentImageWrite) { @@ -1040,11 +535,11 @@ const std::string defs = R"( OpCapability Shader OpCapability StorageImageWriteWithoutFormat -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -1060,9 +555,8 @@ OpDecorate %PerViewConstantBuffer_t Block OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1087,13 +581,10 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2int Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input )"; - // clang-format on const std::string main_func = R"( %MainPs = OpFunction %void None %3 @@ -1105,30 +596,33 @@ %66 = OpLoad %20 %65 OpImageWrite %66 %53 %80 OpStore %_entryPointOutput_vColor %80 -; CHECK-NOT: OpImageWrite %66 %53 %80 -; CHECK-NOT: OpStore %_entryPointOutput_vColor %80 -; CHECK: %35 = OpULessThan %bool %30 %uint_128 -; CHECK: OpSelectionMerge %36 None -; CHECK: OpBranchConditional %35 %37 %38 -; CHECK: %37 = OpLabel -; CHECK: %39 = OpLoad %16 %31 -; CHECK: OpImageWrite %39 %28 %19 -; CHECK: OpBranch %36 -; CHECK: %38 = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_30 %uint_2 %30 %uint_128 -; CHECK: OpBranch %36 -; CHECK: %36 = OpLabel -; CHECK: OpStore %_entryPointOutput_vColor %19 +;CHECK-NOT: OpImageWrite %66 %53 %80 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %80 +;CHECK: %32 = OpLoad %16 %31 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_30 %uint_2 %30 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %16 %31 +;CHECK: OpImageWrite {{%\w+}} %28 %19 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %_entryPointOutput_vColor %19 OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Frag; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func, - true, 7u, 23u, false, false, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstrumentVertexSimple) { @@ -1140,7 +634,7 @@ const std::string defs = R"( OpCapability Shader OpCapability Sampled1D -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" %_ %coords2D @@ -1159,10 +653,9 @@ OpMemberName %foo 0 "g_idx" OpName %__0 "" OpName %coords2D "coords2D" -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex -; CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex +;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex OpMemberDecorate %gl_PerVertex 0 BuiltIn Position OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance @@ -1205,16 +698,11 @@ %v2float = OpTypeVector %float 2 %_ptr_Input_v2float = OpTypePointer Input %v2float %coords2D = OpVariable %_ptr_Input_v2float Input -; CHECK: %bool = OpTypeBool -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint -; CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input -; CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input +;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -1232,34 +720,51 @@ %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 OpStore %40 %38 -; CHECK-NOT: %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 -; CHECK-NOT: %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 -; CHECK-NOT: OpStore %40 %38 -; CHECK: %46 = OpULessThan %bool %37 %uint_128 -; CHECK: OpSelectionMerge %47 None -; CHECK: OpBranchConditional %46 %48 %49 -; CHECK: %48 = OpLabel -; CHECK: %50 = OpLoad %25 %38 -; CHECK: %51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41 -; CHECK: OpBranch %47 -; CHECK: %49 = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %37 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_74 %uint_0 %uint_2 %uint_13 {{%\w+}} %uint_128 -; CHECK: OpBranch %47 -; CHECK: %47 = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float %51 %48 [[null_v4float]] %49 -; CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 -; CHECK: OpStore %43 {{%\w+}} +;CHECK-NOT: %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 +;CHECK-NOT: %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +;CHECK-NOT: OpStore %40 %38 +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_70 {{%\w+}} %uint_7 %uint_5 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %int {{%\w+}} +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %int {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D {{%\w+}} +;CHECK: {{%\w+}} = OpLoad {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %float %coords1D +;CHECK: {{%\w+}} = OpLoad %float %lod +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_75 {{%\w+}} %uint_2 %uint_13 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %25 %38 +;CHECK: {{%\w+}} = OpImageSampleExplicitLod %v4float {{%\w+}} %40 Lod %41 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +;CHECK: OpStore %43 {{%\w+}} OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Vert; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func, - true, 7u, 23u, false, false, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstrumentTeseSimple) { @@ -1284,10 +789,11 @@ const std::string defs = R"( OpCapability Tessellation +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint TessellationEvaluation %main "main" %_ -; CHECK: OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord +;CHECK: OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord OpExecutionMode %main Triangles OpExecutionMode %main SpacingEqual OpExecutionMode %main VertexOrderCw @@ -1319,10 +825,9 @@ OpDecorate %ufoo Block OpDecorate %uniform_index_buffer DescriptorSet 9 OpDecorate %uniform_index_buffer Binding 2 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId -; CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +;CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1346,53 +851,322 @@ %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float %_ptr_Output_v4float = OpTypePointer Output %v4float -; CHECK: %bool = OpTypeBool -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint -; CHECK: %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input -; CHECK: %v3float = OpTypeVector %float 3 -; CHECK: %_ptr_Input_v3float = OpTypePointer Input %v3float -; CHECK: %gl_TessCoord = OpVariable %_ptr_Input_v3float Input -; CHECK: %v3uint = OpTypeVector %uint 3 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +;CHECK: %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input +;CHECK: %v3float = OpTypeVector %float 3 +;CHECK: %_ptr_Input_v3float = OpTypePointer Input %v3float +;CHECK: %gl_TessCoord = OpVariable %_ptr_Input_v3float Input +;CHECK: %v3uint = OpTypeVector %uint 3 +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on - const std::string main_func = R"( + const std::string main_func = + R"( %main = OpFunction %void None %3 %5 = OpLabel %25 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 %26 = OpLoad %uint %25 %28 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %26 %int_0 %29 = OpLoad %v4float %28 -; CHECK-NOT: %29 = OpLoad %v4float %28 -; CHECK: %34 = OpULessThan %bool %28 %uint_11 -; CHECK: OpSelectionMerge %35 None -; CHECK: OpBranchConditional %34 %36 %37 -; CHECK: %36 = OpLabel -; CHECK: %38 = OpLoad %v4float %29 -; CHECK: OpBranch %35 -; CHECK: %37 = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_63 %uint_0 %uint_9 %uint_1 %28 %uint_11 -; CHECK: OpBranch %35 -; CHECK: %35 = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float %38 %36 [[null_v4float]] %37 +;CHECK-NOT: %29 = OpLoad %v4float %28 +;CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID +;CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord +;CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_2 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_62 {{%\w+}} %uint_9 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %27 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} %31 = OpAccessChain %_ptr_Output_v4float %_ %int_0 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: OpStore %31 {{%\w+}} +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID +;CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord +;CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_2 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_64 {{%\w+}} %uint_9 %uint_1 {{%\w+}} {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %v4float %29 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: %31 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +;CHECK: OpStore %31 [[phi_result]] OpReturn OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Tese; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func, - true, 7u, 23u, false, false, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); +} + +TEST_F(InstBindlessTest, InstrumentTesc) { + // This test verifies that the pass will correctly instrument tessellation + // control shader + // + // clang-format off + // + // #version 450 + // layout(vertices = 3) out; + // layout(set = 0, binding = 0) uniform texture1D _77; + // layout(set = 0, binding = 1) uniform sampler _78; + + // layout(location = 1) flat in int _3[]; + // layout(location = 0) out vec4 _5[3]; + + // void main() + // { + // float param; + // if (_3[gl_InvocationID] == 0) + // { + // param = 0.0234375; + // } + // else + // { + // param = 1.0156199932098388671875; + // } + // _5[gl_InvocationID] = textureLod(sampler1D(_77, _78), param, 0.0); + // vec4 _203; + // if (gl_InvocationID == 0) + // { + // _203 = gl_in[0].gl_Position; + // } + // else + // { + // _203 = gl_in[2].gl_Position; + // } + // gl_out[gl_InvocationID].gl_Position = _203; + // gl_TessLevelInner[0] = 2.7999999523162841796875; + // gl_TessLevelInner[1] = 2.7999999523162841796875; + // gl_TessLevelOuter[0] = 2.7999999523162841796875; + // gl_TessLevelOuter[1] = 2.7999999523162841796875; + // gl_TessLevelOuter[2] = 2.7999999523162841796875; + // gl_TessLevelOuter[3] = 2.7999999523162841796875; + // } + // + // clang-format on + // + // + + // clang-format off + const std::string defs = R"( +OpCapability Tessellation +OpCapability Sampled1D +;CHECK: OpCapability Linkage +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpExtension "SPV_KHR_physical_storage_buffer" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +;CHECK: OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint TessellationControl %main "main" %_3 %gl_InvocationID %_5 %gl_in %gl_out %gl_TessLevelInner %gl_TessLevelOuter +;CHECK: OpEntryPoint TessellationControl %main "main" %_3 %gl_InvocationID %_5 %gl_in %gl_out %gl_TessLevelInner %gl_TessLevelOuter %gl_PrimitiveID +OpExecutionMode %main OutputVertices 3 +OpSource GLSL 450 +OpName %main "main" +OpName %_3 "_3" +OpName %gl_InvocationID "gl_InvocationID" +OpName %param "param" +OpName %_5 "_5" +OpName %_77 "_77" +OpName %_78 "_78" +OpName %_203 "_203" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %gl_in "gl_in" +OpName %gl_PerVertex_0 "gl_PerVertex" +OpMemberName %gl_PerVertex_0 0 "gl_Position" +OpMemberName %gl_PerVertex_0 1 "gl_PointSize" +OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex_0 3 "gl_CullDistance" +OpName %gl_out "gl_out" +OpName %gl_TessLevelInner "gl_TessLevelInner" +OpName %gl_TessLevelOuter "gl_TessLevelOuter" +OpDecorate %_3 Flat +OpDecorate %_3 Location 1 +OpDecorate %gl_InvocationID BuiltIn InvocationId +OpDecorate %_5 Location 0 +OpDecorate %_77 DescriptorSet 0 +OpDecorate %_77 Binding 0 +OpDecorate %_78 DescriptorSet 0 +OpDecorate %_78 Binding 1 +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex_0 Block +OpDecorate %gl_TessLevelInner Patch +OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner +OpDecorate %gl_TessLevelOuter Patch +OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_int_uint_32 = OpTypeArray %int %uint_32 +%_ptr_Input__arr_int_uint_32 = OpTypePointer Input %_arr_int_uint_32 +%_3 = OpVariable %_ptr_Input__arr_int_uint_32 Input +%_ptr_Input_int = OpTypePointer Input %int +%gl_InvocationID = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%bool = OpTypeBool +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0_0234375 = OpConstant %float 0.0234375 +%float_1_01561999 = OpConstant %float 1.01561999 +%v4float = OpTypeVector %float 4 +%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 +%_5 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%34 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_34 = OpTypePointer UniformConstant %34 +%_77 = OpVariable %_ptr_UniformConstant_34 UniformConstant +%38 = OpTypeSampler +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %38 +%_78 = OpVariable %_ptr_UniformConstant_38 UniformConstant +%42 = OpTypeSampledImage %34 +%float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32 +%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32 +%gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%int_2 = OpConstant %int 2 +%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3 +%_ptr_Output__arr_gl_PerVertex_0_uint_3 = OpTypePointer Output %_arr_gl_PerVertex_0_uint_3 +%gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_0_uint_3 Output +%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 +%float_2_79999995 = OpConstant %float 2.79999995 +%_ptr_Output_float = OpTypePointer Output %float +%int_1 = OpConstant %int 1 +%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 +%int_3 = OpConstant %int 3 +)"; + + const std::string main_func = + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%param = OpVariable %_ptr_Function_float Function +%_203 = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %int %gl_InvocationID +%15 = OpAccessChain %_ptr_Input_int %_3 %14 +%16 = OpLoad %int %15 +%19 = OpIEqual %bool %16 %int_0 +OpSelectionMerge %21 None +OpBranchConditional %19 %20 %26 +%20 = OpLabel +;CHECK-NOT: %15 = OpAccessChain %_ptr_Input_int %_3 %14 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %int %gl_InvocationID +;CHECK: {{%\w+}} = OpAccessChain %_ptr_Input_int %_3 {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %int {{%\w+}} +;CHECK: {{%\w+}} = OpIEqual %bool {{%\w+}} %int_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +OpStore %param %float_0_0234375 +OpBranch %21 +%26 = OpLabel +OpStore %param %float_1_01561999 +OpBranch %21 +%21 = OpLabel +%33 = OpLoad %int %gl_InvocationID +%37 = OpLoad %34 %_77 +%41 = OpLoad %38 %_78 +%43 = OpSampledImage %42 %37 %41 +%44 = OpLoad %float %param +;CHECK: {{%\w+}} = OpLoad %int %gl_InvocationID +;CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_1 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %inst_bindless_check_desc %uint_23 %uint_129 {{%\w+}} %uint_0 %uint_0 %uint_0 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +%46 = OpImageSampleExplicitLod %v4float %43 %44 Lod %float_0 +%48 = OpAccessChain %_ptr_Output_v4float %_5 %33 +OpStore %48 %46 +;CHECK-NOT: %48 = OpAccessChain %_ptr_Output_v4float %_5 %33 +;CHECK-NOT: OpStore %48 %46 +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: [[access_chain:%\w+]] = OpAccessChain %_ptr_Output_v4float %_5 {{%\w+}} +;CHECK: OpStore [[access_chain]] [[phi_result]] +%49 = OpLoad %int %gl_InvocationID +%50 = OpIEqual %bool %49 %int_0 +OpSelectionMerge %52 None +OpBranchConditional %50 %51 %64 +%51 = OpLabel +%62 = OpAccessChain %_ptr_Input_v4float %gl_in %int_0 %int_0 +%63 = OpLoad %v4float %62 +OpStore %_203 %63 +OpBranch %52 +%64 = OpLabel +%66 = OpAccessChain %_ptr_Input_v4float %gl_in %int_2 %int_0 +%67 = OpLoad %v4float %66 +OpStore %_203 %67 +OpBranch %52 +%52 = OpLabel +%72 = OpLoad %int %gl_InvocationID +%73 = OpLoad %v4float %_203 +%74 = OpAccessChain %_ptr_Output_v4float %gl_out %72 %int_0 +OpStore %74 %73 +%81 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_0 +OpStore %81 %float_2_79999995 +%83 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_1 +OpStore %83 %float_2_79999995 +%88 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_0 +OpStore %88 %float_2_79999995 +%89 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_1 +OpStore %89 %float_2_79999995 +%90 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_2 +OpStore %90 %float_2_79999995 +%92 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_3 +OpStore %92 %float_2_79999995 +OpReturn +OpFunctionEnd +)"; + // clang-format on + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, MultipleDebugFunctions) { @@ -1402,11 +1176,11 @@ // clang-format off const std::string defs = R"( OpCapability Shader -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %2 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft %1 = OpString "foo5.frag" OpSource HLSL 500 %1 @@ -1435,9 +1209,8 @@ OpDecorate %g_sAniso Binding 3 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %4 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1471,16 +1244,10 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -; CHECK: %bool = OpTypeBool -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string func1 = R"( %MainPs = OpFunction %void None %4 @@ -1515,41 +1282,43 @@ %43 = OpAccessChain %_ptr_Function_v2float %i %int_0 %44 = OpLoad %v2float %43 %45 = OpImageSampleImplicitLod %v4float %41 %44 -; CHECK-NOT: %45 = OpImageSampleImplicitLod %v4float %41 %44 -; CHECK: OpNoLine -; CHECK: %62 = OpULessThan %bool %50 %uint_128 -; CHECK: OpSelectionMerge %63 None -; CHECK: OpBranchConditional %62 %64 %65 -; CHECK: %64 = OpLabel -; CHECK: %66 = OpLoad %27 %51 -; CHECK: %67 = OpSampledImage %37 %66 %53 -; CHECK: OpLine %5 24 0 -; CHECK: %68 = OpImageSampleImplicitLod %v4float %67 %56 -; CHECK: OpNoLine -; CHECK: OpBranch %63 -; CHECK: %65 = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_109 %uint_0 %uint_1 %uint_2 %50 %uint_128 -; CHECK: OpBranch %63 -; CHECK: %63 = OpLabel -; CHECK: [[phi_result:%\w+]] = OpPhi %v4float %68 %64 [[null_v4float]] %65 -; CHECK: OpLine %5 24 0 +;CHECK-NOT: %45 = OpImageSampleImplicitLod %v4float %41 %44 +;CHECK: {{%\w+}} = OpLoad %v2float {{%\w+}} +;CHECK: OpNoLine +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_128 {{%\w+}} %uint_1 %uint_2 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %27 {{%\w+}} +;CHECK: {{%\w+}} = OpSampledImage %37 {{%\w+}} {{%\w+}} +;CHECK: OpLine %5 24 0 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} {{%\w+}} +;CHECK: OpNoLine +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} %47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 OpStore %47 %45 -; CHECK-NOT: OpStore %47 %45 -; CHECK: [[store_loc:%\w+]] = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 -; CHECK: OpStore [[store_loc]] [[phi_result]] +;CHECK-NOT: OpStore %47 %45 +;CHECK: [[store_loc:%\w+]] = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 +;CHECK: OpStore [[store_loc]] [[phi_result]] OpLine %1 25 0 %48 = OpLoad %PS_OUTPUT %ps_output OpReturnValue %48 OpFunctionEnd )"; - - const std::string output_func = kStreamWrite6Frag; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch<InstBindlessCheckPass>( - defs + func1 + func2 + output_func, true, 7u, 23u, false, false, false, - false, false); + defs + kImportStub + func1 + func2, true, 23u); } TEST_F(InstBindlessTest, RuntimeArray) { @@ -1561,12 +1330,12 @@ const std::string defs = R"( OpCapability Shader OpCapability RuntimeDescriptorArray +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -1585,9 +1354,8 @@ OpDecorate %g_sAniso Binding 3 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1614,13 +1382,10 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %MainPs = OpFunction %void None %3 @@ -1634,47 +1399,34 @@ %68 = OpSampledImage %39 %66 %67 %71 = OpImageSampleImplicitLod %v4float %68 %53 OpStore %_entryPointOutput_vColor %71 -; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53 -; CHECK-NOT: OpStore %_entryPointOutput_vColor %71 -; CHECK: [[length_result:%\w+]] = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_1 %uint_2 -; CHECK: {{%\w+}} = OpULessThan %bool %32 [[length_result]] -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %16 %33 -; CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35 -; CHECK: [[state_result:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %32 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 [[state_result]] -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %16 %33 -; CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35 -; CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_59 %uint_1 %uint_1 %uint_2 %32 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_1:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_59 %uint_0 %uint_1 %uint_2 %32 {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_2:%\w+]] = OpPhi %v4float [[phi_result_1]] {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpStore %_entryPointOutput_vColor [[phi_result_2]] +;CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %71 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_60 {{%\w+}} %uint_1 %uint_2 %32 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %16 %33 +;CHECK: {{%\w+}} = OpSampledImage %26 {{%\w+}} %35 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %30 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result_1:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %_entryPointOutput_vColor [[phi_result_1]] OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Frag + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) { @@ -1688,11 +1440,11 @@ // clang-format off const std::string defs = R"( OpCapability Shader -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpCapability Linkage %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor -; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" @@ -1706,9 +1458,8 @@ OpDecorate %g_sAniso Binding 2 OpDecorate %i_vTextureCoords Location 0 OpDecorate %_entryPointOutput_vColor Location 0 -; check: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; check: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %8 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1725,13 +1476,8 @@ %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %MainPs = OpFunction %void None %8 @@ -1742,46 +1488,38 @@ %23 = OpSampledImage %16 %21 %22 %24 = OpImageSampleImplicitLod %v4float %23 %20 OpStore %_entryPointOutput_vColor %24 -; CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20 -; CHECK-NOT: OpStore %_entryPointOutput_vColor %24 -; CHECK: [[state_result:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 [[state_result]] -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %12 %g_tColor -; CHECK: {{%\w+}} = OpSampledImage %16 {{%\w+}} %22 -; CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_39 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpStore %_entryPointOutput_vColor [[phi_result]] +;CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %24 +;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_40 {{%\w+}} %uint_1 %uint_2 %uint_0 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional [[check_result]] {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %12 %g_tColor +;CHECK: {{%\w+}} = OpSampledImage %16 {{%\w+}} %22 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %_entryPointOutput_vColor [[phi_result]] OpReturn OpFunctionEnd )"; - - const std::string new_funcs = kReadDescInit + kStreamWrite6Frag; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, SPV14AddToEntryPoint) { const std::string text = R"( -; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] -; CHECK: OpDecorate [[v1]] DescriptorSet 7 -; CHECK: OpDecorate [[v2]] DescriptorSet 7 -; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer -; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer OpCapability Shader OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var +;CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} %gl_FragCoord OpExecutionMode %foo OriginUpperLeft OpDecorate %image_var DescriptorSet 4 OpDecorate %image_var Binding 1 @@ -1824,23 +1562,19 @@ )"; SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, SPV14AddToEntryPoints) { const std::string text = R"( -; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] -; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] -; CHECK: OpDecorate [[v1]] DescriptorSet 7 -; CHECK: OpDecorate [[v2]] DescriptorSet 7 -; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer -; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer OpCapability Shader +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var +;CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} %gl_FragCoord OpEntryPoint Fragment %foo "bar" %gid %image_var %sampler_var +;CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} %gl_FragCoord OpExecutionMode %foo OriginUpperLeft OpDecorate %image_var DescriptorSet 3 OpDecorate %image_var Binding 2 @@ -1883,8 +1617,7 @@ )"; SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true, - false, false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) { @@ -1907,12 +1640,12 @@ OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability UniformBufferArrayNonUniformIndexing +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii -; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -1932,12 +1665,10 @@ OpDecorate %nu_ii NonUniform OpDecorate %16 NonUniform OpDecorate %20 NonUniform -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + R"( -; CHECK: OpDecorate {{%\w+}} NonUniform -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -; CHECK: OpDecorate {{%\w+}} NonUniform +;CHECK: OpDecorate {{%\w+}} NonUniform +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +;CHECK: OpDecorate {{%\w+}} NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -1952,14 +1683,12 @@ %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %v4float = OpTypeVector %float 4 -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_float:%\w+]] = OpConstantNull %float +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %v4float = OpTypeVector %float 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_float:%\w+]] = OpConstantNull %float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -1968,46 +1697,33 @@ %19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0 %20 = OpLoad %float %19 OpStore %b %20 -; CHECK-NOT: %20 = OpLoad %float %19 -; CHECK-NOT: OpStore %b %20 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_6 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool %7 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_6 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %float %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_1 %uint_6 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_1:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[bitcast_result:%\w+]] = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_0 %uint_6 %uint_3 [[bitcast_result]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpStore %b [[phi_result_2]] +;CHECK-NOT: %20 = OpLoad %float %19 +;CHECK-NOT: OpStore %b %20 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint %7 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_6 %uint_3 {{%\w+}} {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %float %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %b [[phi_result]] OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Frag + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) { @@ -2030,12 +1746,12 @@ OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability StorageBufferArrayNonUniformIndexing +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii -; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2055,9 +1771,8 @@ OpDecorate %nu_ii NonUniform OpDecorate %16 NonUniform OpDecorate %20 NonUniform -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2072,16 +1787,13 @@ %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float -; CHECK: %uint = OpTypeInt 32 0 -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %v4float = OpTypeVector %float 4 -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_float:%\w+]] = OpConstantNull %float +;CHECK: %uint = OpTypeInt 32 0 +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %v4float = OpTypeVector %float 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_float:%\w+]] = OpConstantNull %float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2090,46 +1802,33 @@ %19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 %20 = OpLoad %float %19 OpStore %b %20 -; CHECK-NOT: %20 = OpLoad %float %19 -; CHECK-NOT: OpStore %b %20 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_7 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool %7 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[bitcast_result_1:%\w+]] = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_7 %uint_3 [[bitcast_result_1]] -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %float %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_1 %uint_7 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_1:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[bitcast_result_2:%\w+]] = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_0 %uint_7 %uint_3 [[bitcast_result_2]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpStore %b [[phi_result_2]] +;CHECK-NOT: %20 = OpLoad %float %19 +;CHECK-NOT: OpStore %b %20 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint %7 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_7 %uint_3 {{%\w+}} {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %float %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %b [[phi_result]] OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Frag + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) { @@ -2141,12 +1840,12 @@ OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability StorageBufferArrayNonUniformIndexing +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii -; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2166,12 +1865,10 @@ OpDecorate %nu_ii NonUniform OpDecorate %16 NonUniform OpDecorate %20 NonUniform -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + R"( -; CHECK: OpDecorate {{%\w+}} NonUniform -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -; CHECK: OpDecorate {{%\w+}} NonUniform +;CHECK: OpDecorate {{%\w+}} NonUniform +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +;CHECK: OpDecorate {{%\w+}} NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2186,14 +1883,12 @@ %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %v4float = OpTypeVector %float 4 -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_float:%\w+]] = OpConstantNull %float +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %v4float = OpTypeVector %float 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_float:%\w+]] = OpConstantNull %float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2202,46 +1897,33 @@ %19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 %20 = OpLoad %float %19 OpStore %b %20 -; CHECK-NOT: %20 = OpLoad %float %19 -; CHECK-NOT: OpStore %b %20 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_0 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool %7 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %float %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_1 %uint_0 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_1:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[bitcast_result:%\w+]] = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_0 %uint_0 %uint_3 [[bitcast_result]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpStore %b [[phi_result_2]] +;CHECK-NOT: %20 = OpLoad %float %19 +;CHECK-NOT: OpStore %b %20 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint %7 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_0 %uint_3 {{%\w+}} {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %float %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %b {{%\w+}} OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Frag + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstInitLoadUBOScalar) { @@ -2259,12 +1941,12 @@ // clang-format off const std::string defs = R"( OpCapability Shader +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b -; CHECK: OpEntryPoint Fragment %main "main" %b %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %b %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2278,9 +1960,8 @@ OpDecorate %uname Block OpDecorate %uniformBuffer DescriptorSet 7 OpDecorate %uniformBuffer Binding 3 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2292,18 +1973,15 @@ %int = OpTypeInt 32 1 %int_0 = OpConstant %int 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -; CHECK: %int = OpTypeInt 32 1 -; CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float -; CHECK: %uint = OpTypeInt 32 0 -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %v4float = OpTypeVector %float 4 -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_float:%\w+]] = OpConstantNull %float +;CHECK: %int = OpTypeInt 32 1 +;CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float +;CHECK: %uint = OpTypeInt 32 0 +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %v4float = OpTypeVector %float 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_float:%\w+]] = OpConstantNull %float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2311,31 +1989,32 @@ %15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 %16 = OpLoad %float %15 OpStore %b %16 -; CHECK-NOT: %16 = OpLoad %float %15 -; CHECK-NOT: OpStore %b %16 -; CHECK: [[check_result:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_7 %uint_3 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 [[check_result]] -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %float %15 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_32 %uint_1 %uint_7 %uint_3 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpStore %b [[phi_result]] +;CHECK-NOT: %16 = OpLoad %float %15 +;CHECK-NOT: OpStore %b %16 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_33 {{%\w+}} %uint_7 %uint_3 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional [[check_result]] {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %float %15 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %b [[phi_result]] OpReturn OpFunctionEnd )"; - - const std::string new_funcs = kReadDescInit + kStreamWrite6Frag; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) { @@ -2357,12 +2036,12 @@ OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability StorageBufferArrayNonUniformIndexing +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %nu_ii %b -; CHECK: OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2381,9 +2060,8 @@ OpDecorate %nu_ii NonUniform OpDecorate %14 NonUniform OpDecorate %b Location 1 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2398,11 +2076,9 @@ %_ptr_Input_float = OpTypePointer Input %float %b = OpVariable %_ptr_Input_float Input %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2411,42 +2087,30 @@ %18 = OpLoad %float %b %20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0 OpStore %20 %18 -; CHECK-NOT: OpStore %20 %18 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_5 %uint_4 -; CHECK: {{%\w+}} = OpULessThan %bool %7 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_5 %uint_4 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %20 %19 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_1 %uint_5 %uint_4 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_45 %uint_0 %uint_5 %uint_4 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: OpStore %20 %18 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint %7 +;CHECK: [[check_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_5 %uint_4 {{%\w+}} {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional [[check_result]] {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %20 %19 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Frag + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) { @@ -2468,12 +2132,12 @@ OpCapability Shader OpCapability ShaderNonUniform OpCapability UniformBufferArrayNonUniformIndexing +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %b %nu_ii -; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2493,11 +2157,9 @@ OpDecorate %nu_ii NonUniform OpDecorate %18 NonUniform OpDecorate %22 NonUniform -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -)" + kInputDecorations + R"( -; CHECK: OpDecorate [[load_result:%\w+]] NonUniform +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +;CHECK: OpDecorate [[load_result:%\w+]] NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 @@ -2514,14 +2176,10 @@ %nu_ii = OpVariable %_ptr_Input_int Input %int_0 = OpConstant %int 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kOutputGlobals + R"( -; CHECK: %v4float = OpTypeVector %float 4 -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 -; CHECK: [[null_float:%\w+]] = OpConstantNull %float -)" + kInputGlobals; - // clang-format on +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_float:%\w+]] = OpConstantNull %float +)"; const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2530,44 +2188,33 @@ %21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0 %22 = OpLoad %float %21 OpStore %b %22 -; CHECK-NOT: %22 = OpLoad %float %21 -; CHECK-NOT: OpStore %b %22 -; CHECK: {{%\w+}} = OpULessThan %bool %7 %uint_128 -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[load_result]] = OpLoad %float %22 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_46 %uint_1 %uint_1 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_1:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpBitcast %uint %7 -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_46 %uint_0 %uint_1 %uint_3 {{%\w+}} %uint_128 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] {{%\w+}} [[null_float]] {{%\w+}} -; CHECK: OpStore %b [[phi_result_2]] +;CHECK-NOT: %22 = OpLoad %float %21 +;CHECK-NOT: OpStore %b %22 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint %7 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_47 {{%\w+}} %uint_1 %uint_3 {{%\w+}} {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %float %22 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %b {{%\w+}} OpReturn OpFunctionEnd )"; - - const std::string new_funcs = kStreamWrite6Frag + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -2593,12 +2240,12 @@ const std::string defs = R"( OpCapability Shader OpCapability RuntimeDescriptorArray +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" -; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID OpExecutionMode %main LocalSize 1 1 1 OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -2616,9 +2263,8 @@ OpDecorate %images DescriptorSet 2 OpDecorate %images Binding 1 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -2640,14 +2286,12 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: %v3uint = OpTypeVector %uint 3 -; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint -; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: %v3uint = OpTypeVector %uint 3 +;CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +;CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2660,70 +2304,64 @@ %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_2 %uint_0 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_47 %uint_1 %uint_2 %uint_0 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_1:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_2 %uint_1 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_2 %uint_1 [[phi_result_1]] -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_50 %uint_1 %uint_2 %uint_1 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result_2:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_50 %uint_0 %uint_2 %uint_1 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float [[phi_result_2]] {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: [[desc_state_result:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_2 %uint_0 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 [[desc_state_result]] -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_53 %uint_1 %uint_2 %uint_0 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_48 {{%\w+}} %uint_2 %uint_0 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_51 {{%\w+}} %uint_2 %uint_1 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}} +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_54 {{%\w+}} %uint_2 %uint_0 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %31 {{%\w+}} +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Compute + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -2748,13 +2386,13 @@ const std::string defs = R"( OpCapability RuntimeDescriptorArray OpCapability RayTracingNV +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint RayGenerationNV %main "main" -; CHECK: OpEntryPoint RayGenerationNV %main "main" [[launch_id:%\w+]] +;CHECK: OpEntryPoint RayGenerationNV %main "main" [[launch_id:%\w+]] OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -2772,9 +2410,8 @@ OpDecorate %images DescriptorSet 3 OpDecorate %images Binding 5 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV +)" + kImportDeco + R"( +;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -2796,11 +2433,9 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2813,70 +2448,64 @@ %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_3 %uint_1 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_48 %uint_1 %uint_3 %uint_1 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_3 %uint_5 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_3 %uint_5 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_1 %uint_3 %uint_5 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_3 %uint_5 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_3 %uint_1 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %31 %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_54 %uint_1 %uint_3 %uint_1 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5313 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_3 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} +;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5313 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_3 %uint_5 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}} +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5313 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_3 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore {{%\w+}} {{%\w+}} +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Ray + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -2901,13 +2530,13 @@ const std::string defs = R"( OpCapability RuntimeDescriptorArray OpCapability RayTracingNV +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint IntersectionNV %main "main" -; CHECK: OpEntryPoint IntersectionNV %main "main" [[launch_id:%\w+]] +;CHECK: OpEntryPoint IntersectionNV %main "main" [[launch_id:%\w+]] OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -2925,9 +2554,8 @@ OpDecorate %images DescriptorSet 5 OpDecorate %images Binding 3 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV +)" + kImportDeco + R"( +;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -2949,12 +2577,10 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -2967,70 +2593,63 @@ %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_5 %uint_1 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_48 %uint_1 %uint_5 %uint_1 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_5 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_5 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_1 %uint_5 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_5 %uint_3 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_5 %uint_1 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %31 %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_54 %uint_1 %uint_5 %uint_1 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5314 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_5 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} +;CHECK: {{%\w+}} = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5314 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_5 %uint_3 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 {{%\w+}} +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: {{%\w+}} = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5314 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_5 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %31 {{%\w+}} +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Ray + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -3055,13 +2674,13 @@ const std::string defs = R"( OpCapability RuntimeDescriptorArray OpCapability RayTracingNV +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint AnyHitNV %main "main" -; CHECK: OpEntryPoint AnyHitNV %main "main" [[launch_id:%\w+]] +;CHECK: OpEntryPoint AnyHitNV %main "main" [[launch_id:%\w+]] OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -3079,9 +2698,8 @@ OpDecorate %images DescriptorSet 2 OpDecorate %images Binding 3 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV +)" + kImportDeco + R"( +;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -3103,12 +2721,10 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -3121,70 +2737,70 @@ %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_2 %uint_1 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_48 %uint_1 %uint_2 %uint_1 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_2 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_2 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_1 %uint_2 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_2 %uint_3 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_2 %uint_1 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %31 %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_54 %uint_1 %uint_2 %uint_1 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: %20 = OpLoad %uint %19 +;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +;CHECK-NOT: %23 = OpLoad %13 %22 +;CHECK-NOT: %27 = OpImageRead %v4float %23 %25 +;CHECK-NOT: %29 = OpCompositeExtract %float %27 0 +;CHECK-NOT: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5315 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_2 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} +;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]] +;CHECK: %28 = OpLoad %13 %27 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5315 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_2 %uint_3 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 %27 +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5315 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_2 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %31 %30 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Ray + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -3209,13 +2825,13 @@ const std::string defs = R"( OpCapability RuntimeDescriptorArray OpCapability RayTracingNV +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint ClosestHitNV %main "main" -; CHECK: OpEntryPoint ClosestHitNV %main "main" [[launch_id:%\w+]] +;CHECK: OpEntryPoint ClosestHitNV %main "main" [[launch_id:%\w+]] OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -3233,9 +2849,8 @@ OpDecorate %images DescriptorSet 1 OpDecorate %images Binding 3 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV +)" + kImportDeco + R"( +;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -3257,12 +2872,10 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -3275,70 +2888,70 @@ %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_48 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]] -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_1 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_1 %uint_1 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_1 %uint_3 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %31 %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_54 %uint_1 %uint_1 %uint_2 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: %20 = OpLoad %uint %19 +;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +;CHECK-NOT: %23 = OpLoad %13 %22 +;CHECK-NOT: %27 = OpImageRead %v4float %23 %25 +;CHECK-NOT: %29 = OpCompositeExtract %float %27 0 +;CHECK-NOT: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5316 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} +;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]] +;CHECK: %28 = OpLoad %13 %27 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5316 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 %27 +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5316 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %31 %30 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Ray + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -3363,13 +2976,13 @@ const std::string defs = R"( OpCapability RuntimeDescriptorArray OpCapability RayTracingNV +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint MissNV %main "main" -; CHECK: OpEntryPoint MissNV %main "main" [[launch_id:%\w+]] +;CHECK: OpEntryPoint MissNV %main "main" [[launch_id:%\w+]] OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -3387,9 +3000,8 @@ OpDecorate %images DescriptorSet 1 OpDecorate %images Binding 3 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV +)" + kImportDeco + R"( +;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -3411,12 +3023,10 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[launch_id]] = OpVariable %_ptr_Input_v3uint Input +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -3429,70 +3039,67 @@ %29 = OpCompositeExtract %float %27 0 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_48 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]] -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_1 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_1 %uint_1 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_1 %uint_3 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %31 %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_54 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: %20 = OpLoad %uint %19 +;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +;CHECK-NOT: %27 = OpImageRead %v4float %23 %25 +;CHECK-NOT: %29 = OpCompositeExtract %float %27 0 +;CHECK-NOT OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5317 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} +;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images [[phi_result]] +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5317 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 %27 +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5317 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %31 %30 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Ray + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, @@ -3517,13 +3124,13 @@ const std::string defs = R"( OpCapability RuntimeDescriptorArray OpCapability RayTracingNV +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_NV_ray_tracing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint CallableNV %main "main" -; CHECK: OpEntryPoint CallableNV %main "main" [[launch_id:%\w+]] +;CHECK: OpEntryPoint CallableNV %main "main" [[launch_id:%\w+]] OpSource GLSL 460 OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_NV_ray_tracing" @@ -3541,9 +3148,8 @@ OpDecorate %images DescriptorSet 1 OpDecorate %images Binding 3 OpDecorate %images NonWritable -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -; CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV +)" + kImportDeco + R"( +;CHECK: OpDecorate [[launch_id]] BuiltIn LaunchIdNV %void = OpTypeVoid %3 = OpTypeFunction %void %uint = OpTypeInt 32 0 @@ -3565,11 +3171,9 @@ %v4float = OpTypeVector %float 4 %uint_0 = OpConstant %uint 0 %_ptr_Uniform_float = OpTypePointer Uniform %float -)" + kInputGlobals + kOutputGlobals + R"( -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -; CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_uint:%\w+]] = OpConstantNull %uint +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -3580,72 +3184,71 @@ %23 = OpLoad %13 %22 %27 = OpImageRead %v4float %23 %25 %29 = OpCompositeExtract %float %27 0 +;CHECK-NOT: %20 = OpLoad %uint %19 +;CHECK-NOT: %22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5318 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %25 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} +;CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} +;CHECK-NOT: %23 = OpLoad %13 %22 +;CHECK-NOT: %27 = OpImageRead %v4float %23 %25 +;CHECK-NOT: %29 = OpCompositeExtract %float %27 0 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5318 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 %27 +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 +;CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 OpStore %31 %29 -; CHECK-NOT: OpStore %31 %29 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %uint %25 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_48 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %uint {{%\w+}} {{%\w+}} [[null_uint]] {{%\w+}} -; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images {{%\w+}} -; CHECK: %28 = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_binding_length %uint_1 %uint_3 -; CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_3 {{%\w+}} -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %27 -; CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %20 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_1 %uint_1 %uint_3 {{%\w+}} %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_51 %uint_0 %uint_1 %uint_3 {{%\w+}} {{%\w+}} -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %30 = OpCompositeExtract %float {{%\w+}} 0 -; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpStore %31 %30 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_54 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel +;CHECK-NOT: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +;CHECK-NOT: OpStore %31 %29 +;CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5318 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_55 {{%\w+}} %uint_1 %uint_2 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %31 %30 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string new_funcs = - kReadBindingLength + kStreamWrite6Ray + kReadDescInit; + // clang-format on // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) { @@ -3680,12 +3283,12 @@ OpCapability Shader OpCapability ShaderNonUniformEXT OpCapability SampledImageArrayNonUniformIndexingEXT +;CHECK: OpCapability Linkage OpExtension "SPV_EXT_descriptor_indexing" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %inTexcoord %outColor -; CHECK: OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_EXT_nonuniform_qualifier" @@ -3715,12 +3318,11 @@ OpDecorate %uniforms DescriptorSet 1 OpDecorate %uniforms Binding 0 OpDecorate %outColor Location 0 -; CHECK: OpDecorate %63 NonUniform -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -)" + kInputDecorations + R"( -; CHECK: OpDecorate [[desc_state_result:%\w+]] NonUniform +;CHECK: OpDecorate {{%\w+}} NonUniform +;CHECK: OpDecorate {{%\w+}} NonUniform +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +;CHECK: OpDecorate [[desc_state_result:%\w+]] NonUniform %void = OpTypeVoid %3 = OpTypeFunction %void %int = OpTypeInt 32 1 @@ -3752,12 +3354,9 @@ %_ptr_Output_v4float = OpTypePointer Output %v4float %outColor = OpVariable %_ptr_Output_v4float Output %float_0 = OpConstant %float 0 -)" + kOutputGlobals + R"( -; CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -)" + kInputGlobals + R"( -; CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float )"; - // clang-format on const std::string main_func = R"( %main = OpFunction %void None %3 @@ -3774,6 +3373,26 @@ %32 = OpLoad %v2float %inTexcoord %34 = OpImageSampleImplicitLod %v4float %28 %32 %36 = OpCompositeExtract %float %34 0 +;CHECK-NOT: %34 = OpImageSampleImplicitLod %v4float %28 %32 +;CHECK-NOT: %36 = OpCompositeExtract %float %34 0 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpBitcast %uint %19 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_80 {{%\w+}} %uint_1 %uint_3 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 %21 +;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %26 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %32 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} OpStore %x %36 %39 = OpLoad %13 %uniformTex %40 = OpLoad %23 %uniformSampler @@ -3782,38 +3401,48 @@ %47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0 %48 = OpLoad %v2float %47 %49 = OpFMul %v2float %42 %48 +;CHECK-NOT: %48 = OpLoad %v2float %47 +;CHECK-NOT: %49 = OpFMul %v2float %42 %48 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_88 {{%\w+}} %uint_1 %uint_0 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %v2float %47 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} +;CHECK: %49 = OpFMul %v2float %42 [[phi_result]] %50 = OpImageSampleImplicitLod %v4float %41 %49 %51 = OpCompositeExtract %float %50 0 -; CHECK-NOT: %51 = OpCompositeExtract %float %50 0 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_0 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %v2float %47 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_87 %uint_1 %uint_1 %uint_0 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} -; CHECK: %49 = OpFMul %v2float %42 {{%\w+}} -; CHECK: {{%\w+}} = OpSampledImage %27 %39 %40 -; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 %uint_0 -; CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpLoad %13 %uniformTex -; CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %40 -; CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %49 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_89 %uint_1 %uint_1 %uint_2 %uint_0 %uint_0 -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -; CHECK: %51 = OpCompositeExtract %float {{%\w+}} 0 +OpStore %y %51 +;CHECK-NOT: %50 = OpImageSampleImplicitLod %v4float %41 %49 +;CHECK-NOT: %51 = OpCompositeExtract %float %50 0 +;CHECK: {{%\w+}} = OpSampledImage %27 %39 %40 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_90 {{%\w+}} %uint_1 %uint_2 %uint_0 %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %13 %uniformTex +;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %40 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %49 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: %51 = OpCompositeExtract %float {{%\w+}} 0 OpStore %y %51 %54 = OpLoad %float %x %55 = OpLoad %float %y @@ -3822,13 +3451,11 @@ OpReturn OpFunctionEnd )"; - - const std::string new_funcs = kStreamWrite6Frag + kReadDescInit; + // clang-format on SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs, - true, 7u, 23u, true, true, false, - false, false); + SinglePassRunAndMatch<InstBindlessCheckPass>(defs + kImportStub + main_func, + true, 23u); } TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { @@ -3870,143 +3497,145 @@ // clang-format off const std::string text = R"( - OpCapability Shader -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %MainPs OriginUpperLeft - OpSource HLSL 500 - OpName %MainPs "MainPs" - OpName %PerViewPushConst_t "PerViewPushConst_t" - OpMemberName %PerViewPushConst_t 0 "g_B" - OpName %_ "" - OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" - OpMemberName %PerViewConstantBuffer_t 0 "g_TexOff0" - OpMemberName %PerViewConstantBuffer_t 1 "g_TexOff1" - OpName %__0 "" - OpName %g_tColor "g_tColor" - OpName %g_sAniso "g_sAniso" - OpName %i_vTextureCoords "i.vTextureCoords" - OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" - OpMemberDecorate %PerViewPushConst_t 0 Offset 0 - OpDecorate %PerViewPushConst_t Block - OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 - OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 8 - OpDecorate %PerViewConstantBuffer_t Block - OpDecorate %__0 DescriptorSet 0 - OpDecorate %__0 Binding 1 - OpDecorate %g_tColor DescriptorSet 0 - OpDecorate %g_tColor Binding 0 - OpDecorate %g_sAniso DescriptorSet 0 - OpDecorate %g_sAniso Binding 2 - OpDecorate %i_vTextureCoords Location 0 - OpDecorate %_entryPointOutput_vColor Location 0 - ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( - ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v2float = OpTypeVector %float 2 - %v4float = OpTypeVector %float 4 - %uint = OpTypeInt 32 0 +OpCapability Shader +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %PerViewPushConst_t "PerViewPushConst_t" +OpMemberName %PerViewPushConst_t 0 "g_B" +OpName %_ "" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_TexOff0" +OpMemberName %PerViewConstantBuffer_t 1 "g_TexOff1" +OpName %__0 "" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpMemberDecorate %PerViewPushConst_t 0 Offset 0 +OpDecorate %PerViewPushConst_t Block +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 8 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %__0 DescriptorSet 0 +OpDecorate %__0 Binding 1 +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 2 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 %PerViewPushConst_t = OpTypeStruct %uint %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t - %_ = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 +%_ = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 %_ptr_PushConstant_uint = OpTypePointer PushConstant %uint - %bool = OpTypeBool - %uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 %PerViewConstantBuffer_t = OpTypeStruct %v2float %v2float %_ptr_Uniform_PerViewConstantBuffer_t = OpTypePointer Uniform %PerViewConstantBuffer_t - %__0 = OpVariable %_ptr_Uniform_PerViewConstantBuffer_t Uniform +%__0 = OpVariable %_ptr_Uniform_PerViewConstantBuffer_t Uniform %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float - %int_1 = OpConstant %int 1 - %49 = OpTypeImage %float 2D 0 0 0 1 Unknown +%int_1 = OpConstant %int 1 +%49 = OpTypeImage %float 2D 0 0 0 1 Unknown %_ptr_UniformConstant_49 = OpTypePointer UniformConstant %49 - %g_tColor = OpVariable %_ptr_UniformConstant_49 UniformConstant - %53 = OpTypeSampler +%g_tColor = OpVariable %_ptr_UniformConstant_49 UniformConstant +%53 = OpTypeSampler %_ptr_UniformConstant_53 = OpTypePointer UniformConstant %53 - %g_sAniso = OpVariable %_ptr_UniformConstant_53 UniformConstant - %57 = OpTypeSampledImage %49 +%g_sAniso = OpVariable %_ptr_UniformConstant_53 UniformConstant +%57 = OpTypeSampledImage %49 %_ptr_Input_v2float = OpTypePointer Input %v2float %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output - )" + kInputGlobals + kOutputGlobals + R"( - ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float - ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input - ;CHECK: %v4uint = OpTypeVector %uint 4 - ;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float - %MainPs = OpFunction %void None %3 - %5 = OpLabel - ;CHECK: [[desc_state_result:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_1 %uint_0 - ;CHECK: OpBranch %117 - ;CHECK: %117 = OpLabel - ;CHECK: OpBranch %116 - ;CHECK: %116 = OpLabel - %69 = OpLoad %v2float %i_vTextureCoords - %82 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 - %83 = OpLoad %uint %82 - %84 = OpINotEqual %bool %83 %uint_0 - OpSelectionMerge %91 None - OpBranchConditional %84 %85 %88 - %85 = OpLabel - %86 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_0 - %87 = OpLoad %v2float %86 - ;CHECK-NOT: %87 = OpLoad %v2float %86 - ;CHECK: %119 = OpIAdd %uint %uint_0 %uint_7 - ;CHECK: {{%\w+}} = OpULessThan %bool %119 [[desc_state_result]] - ;CHECK: OpSelectionMerge {{%\w+}} None - ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} - ;CHECK: {{%\w+}} = OpLabel - ;CHECK: {{%\w+}} = OpLoad %v2float %86 - ;CHECK: OpBranch {{%\w+}} - ;CHECK: {{%\w+}} = OpLabel - ;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_71 %uint_4 %uint_0 %uint_1 %uint_0 %119 {{%\w+}} - ;CHECK: OpBranch {{%\w+}} - ;CHECK: {{%\w+}} = OpLabel +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float + )" + kImportStub + R"( +%MainPs = OpFunction %void None %3 +%5 = OpLabel +%69 = OpLoad %v2float %i_vTextureCoords +%82 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%83 = OpLoad %uint %82 +%84 = OpINotEqual %bool %83 %uint_0 +OpSelectionMerge %91 None +OpBranchConditional %84 %85 %88 +%85 = OpLabel +%86 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_0 +%87 = OpLoad %v2float %86 +;CHECK-NOT: %87 = OpLoad %v2float %86 +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 %uint_7 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[desc_state_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_72 {{%\w+}} %uint_0 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional [[desc_state_result]] {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %v2float %86 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} - OpBranch %91 - %88 = OpLabel - %89 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1 - %90 = OpLoad %v2float %89 - ;CHECK-NOT: %90 = OpLoad %v2float %89 - ;CHECK: {{%\w+}} = OpIAdd %uint %uint_8 %uint_7 - ;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} - ;CHECK: OpSelectionMerge {{%\w+}} None - ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} - ;CHECK: {{%\w+}} = OpLabel - ;CHECK: {{%\w+}} = OpLoad %v2float %89 - ;CHECK: OpBranch {{%\w+}} - ;CHECK: {{%\w+}} = OpLabel - ;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_75 %uint_4 %uint_0 %uint_1 %uint_0 {{%\w+}} {{%\w+}} - ;CHECK: OpBranch {{%\w+}} - ;CHECK: {{%\w+}} = OpLabel - ;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} - OpBranch %91 - %91 = OpLabel - %115 = OpPhi %v2float %87 %85 %90 %88 - ;CHECK-NOT: %115 = OpPhi %v2float %87 %85 %90 %88 - ;CHECK: %115 = OpPhi %v2float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} - %95 = OpFAdd %v2float %69 %115 - %96 = OpLoad %49 %g_tColor - %97 = OpLoad %53 %g_sAniso - %98 = OpSampledImage %57 %96 %97 - %100 = OpImageSampleImplicitLod %v4float %98 %95 - OpStore %_entryPointOutput_vColor %100 - OpReturn - OpFunctionEnd -)" + kReadDescInit + kStreamWrite7Frag; +OpBranch %91 +%88 = OpLabel +%89 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1 +%90 = OpLoad %v2float %89 +;CHECK-NOT: %90 = OpLoad %v2float %89 +;CHECK: {{%\w+}} = OpIAdd %uint %uint_8 %uint_7 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_76 {{%\w+}} %uint_0 %uint_1 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %v2float %89 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} +OpBranch %91 +%91 = OpLabel +%115 = OpPhi %v2float %87 %85 %90 %88 +;CHECK-NOT: %115 = OpPhi %v2float %87 %85 %90 %88 +;CHECK: %115 = OpPhi %v2float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +%95 = OpFAdd %v2float %69 %115 +%96 = OpLoad %49 %g_tColor +%97 = OpLoad %53 %g_sAniso +%98 = OpSampledImage %57 %96 %97 +%100 = OpImageSampleImplicitLod %v4float %98 %95 +OpStore %_entryPointOutput_vColor %100 +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { @@ -4047,132 +3676,129 @@ // clang-format off const std::string text = R"( - OpCapability Shader -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor - OpExecutionMode %MainPs OriginUpperLeft - OpSource HLSL 500 - OpName %MainPs "MainPs" - OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" - OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" - OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" - OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" - OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" - OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" - OpName %_ "" - OpName %PerViewPushConst_t "PerViewPushConst_t" - OpMemberName %PerViewPushConst_t 0 "g_c" - OpName %__0 "" - OpName %g_tColor "g_tColor" - OpName %g_sAniso "g_sAniso" - OpName %i_vTextureCoords "i.vTextureCoords" - OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 - OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 - OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 - OpDecorate %_BindlessFastEnvMapCB_PS_t Block - OpDecorate %_ DescriptorSet 0 - OpDecorate %_ Binding 2 - OpMemberDecorate %PerViewPushConst_t 0 Offset 0 - OpDecorate %PerViewPushConst_t Block - OpDecorate %g_tColor DescriptorSet 0 - OpDecorate %g_tColor Binding 0 - OpDecorate %g_sAniso DescriptorSet 0 - OpDecorate %g_sAniso Binding 1 - OpDecorate %i_vTextureCoords Location 0 - OpDecorate %_entryPointOutput_vColor Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v2float = OpTypeVector %float 2 - %v4float = OpTypeVector %float 4 - %v3float = OpTypeVector %float 3 +OpCapability Shader +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" +OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" +OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" +OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" +OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" +OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" +OpName %_ "" +OpName %PerViewPushConst_t "PerViewPushConst_t" +OpMemberName %PerViewPushConst_t 0 "g_c" +OpName %__0 "" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 +OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 +OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 +OpDecorate %_BindlessFastEnvMapCB_PS_t Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 2 +OpMemberDecorate %PerViewPushConst_t 0 Offset 0 +OpDecorate %PerViewPushConst_t Block +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 1 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%v3float = OpTypeVector %float 3 %mat4v3float = OpTypeMatrix %v3float 4 %PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float - %uint = OpTypeInt 32 0 - %uint_128 = OpConstant %uint 128 +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128 %_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 %_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t - %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 +%_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 %PerViewPushConst_t = OpTypeStruct %uint %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t - %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant %_ptr_PushConstant_uint = OpTypePointer PushConstant %uint - %int_2 = OpConstant %int 2 +%int_2 = OpConstant %int 2 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float - %46 = OpTypeImage %float 2D 0 0 0 1 Unknown +%46 = OpTypeImage %float 2D 0 0 0 1 Unknown %_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 - %g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant - %50 = OpTypeSampler +%g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant +%50 = OpTypeSampler %_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 - %g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant - %54 = OpTypeSampledImage %46 +%g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant +%54 = OpTypeSampledImage %46 %_ptr_Input_v2float = OpTypePointer Input %v2float %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kInputGlobals + kOutputGlobals + R"( -;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -;CHECK: %v4uint = OpTypeVector %uint 4 -;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float - %MainPs = OpFunction %void None %3 - %5 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_2 %uint_0 -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel - %66 = OpLoad %v2float %i_vTextureCoords - %79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0 - %80 = OpLoad %uint %79 - %81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2 - %82 = OpLoad %v2float %81 -;CHECK-NOT: %82 = OpLoad %v2float %81 +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float +)" + kImportStub + R"( +%MainPs = OpFunction %void None %3 +%5 = OpLabel +%66 = OpLoad %v2float %i_vTextureCoords +%79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0 +%80 = OpLoad %uint %79 +%81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2 +%82 = OpLoad %v2float %81 +;CHECK-NOT: %82 = OpLoad %v2float %81 ;CHECK: {{%\w+}} = OpIMul %uint %uint_80 %80 ;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_64 ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_79 {{%\w+}} %uint_0 %uint_2 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpLoad %v2float %81 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_78 %uint_4 %uint_0 %uint_2 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} - %86 = OpFAdd %v2float %66 %82 -;CHECK-NOT: %86 = OpFAdd %v2float %66 %82 -;CHECK: %86 = OpFAdd %v2float %66 {{%\w+}} - %87 = OpLoad %46 %g_tColor - %88 = OpLoad %50 %g_sAniso - %89 = OpSampledImage %54 %87 %88 - %91 = OpImageSampleImplicitLod %v4float %89 %86 - OpStore %_entryPointOutput_vColor %91 - OpReturn - OpFunctionEnd -)" + kReadDescInit + kStreamWrite7Frag; +%86 = OpFAdd %v2float %66 %82 +;CHECK-NOT: %86 = OpFAdd %v2float %66 %82 +;CHECK: %86 = OpFAdd %v2float %66 {{%\w+}} +%87 = OpLoad %46 %g_tColor +%88 = OpLoad %50 %g_sAniso +%89 = OpSampledImage %54 %87 %88 +%91 = OpImageSampleImplicitLod %v4float %89 %86 +OpStore %_entryPointOutput_vColor %91 +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { @@ -4183,129 +3809,131 @@ // clang-format off const std::string text = R"( - OpCapability Shader -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %MainPs OriginUpperLeft - OpSource HLSL 500 - OpName %MainPs "MainPs" - OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" - OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" - OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" - OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" - OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" - OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" - OpName %_ "" - OpName %PerViewPushConst_t "PerViewPushConst_t" - OpMemberName %PerViewPushConst_t 0 "g_c" - OpName %__0 "" - OpName %g_tColor "g_tColor" - OpName %g_sAniso "g_sAniso" - OpName %i_vTextureCoords "i.vTextureCoords" - OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 - OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 - OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 - OpDecorate %_BindlessFastEnvMapCB_PS_t Block - OpDecorate %_ DescriptorSet 0 - OpDecorate %_ Binding 2 - OpMemberDecorate %PerViewPushConst_t 0 Offset 0 - OpDecorate %PerViewPushConst_t Block - OpDecorate %g_tColor DescriptorSet 0 - OpDecorate %g_tColor Binding 0 - OpDecorate %g_sAniso DescriptorSet 0 - OpDecorate %g_sAniso Binding 1 - OpDecorate %i_vTextureCoords Location 0 - OpDecorate %_entryPointOutput_vColor Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v2float = OpTypeVector %float 2 - %v4float = OpTypeVector %float 4 - %v3float = OpTypeVector %float 3 +OpCapability Shader +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" +OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" +OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" +OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" +OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" +OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" +OpName %_ "" +OpName %PerViewPushConst_t "PerViewPushConst_t" +OpMemberName %PerViewPushConst_t 0 "g_c" +OpName %__0 "" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 +OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 +OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 +OpDecorate %_BindlessFastEnvMapCB_PS_t Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 2 +OpMemberDecorate %PerViewPushConst_t 0 Offset 0 +OpDecorate %PerViewPushConst_t Block +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 1 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%v3float = OpTypeVector %float 3 %mat4v3float = OpTypeMatrix %v3float 4 %PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float - %uint = OpTypeInt 32 0 - %uint_128 = OpConstant %uint 128 +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128 %_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 %_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t - %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 +%_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 %PerViewPushConst_t = OpTypeStruct %uint %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t - %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant %_ptr_PushConstant_uint = OpTypePointer PushConstant %uint - %int_2 = OpConstant %int 2 +%int_2 = OpConstant %int 2 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float - %46 = OpTypeImage %float 2D 0 0 0 1 Unknown +%46 = OpTypeImage %float 2D 0 0 0 1 Unknown %_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 - %g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant - %50 = OpTypeSampler +%g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant +%50 = OpTypeSampler %_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 - %g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant - %54 = OpTypeSampledImage %46 +%g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant +%54 = OpTypeSampledImage %46 %_ptr_Input_v2float = OpTypePointer Input %v2float %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kInputGlobals + kOutputGlobals + R"( +;CHECK: %v4uint = OpTypeVector %uint 4 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -;CHECK: %v4uint = OpTypeVector %uint 4 ;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float ;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float - %MainPs = OpFunction %void None %3 - %5 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_2 %uint_0 -;CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_0 %uint_0 -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel - %66 = OpLoad %v2float %i_vTextureCoords - %79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0 - %80 = OpLoad %uint %79 - %81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2 - %82 = OpLoad %v2float %81 - %86 = OpFAdd %v2float %66 %82 +)" + kImportStub + R"( +%MainPs = OpFunction %void None %3 +%5 = OpLabel +%66 = OpLoad %v2float %i_vTextureCoords +%79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0 +%80 = OpLoad %uint %79 +%81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2 +%82 = OpLoad %v2float %81 +%86 = OpFAdd %v2float %66 %82 ;CHECK-NOT: %82 = OpLoad %v2float %81 ;CHECK-NOT: %86 = OpFAdd %v2float %66 %82 ;CHECK: {{%\w+}} = OpIMul %uint %uint_80 %80 ;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_64 ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_79 {{%\w+}} %uint_0 %uint_2 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpLoad %v2float %81 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_78 %uint_4 %uint_0 %uint_2 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} ;CHECK: %86 = OpFAdd %v2float %66 {{%\w+}} - %87 = OpLoad %46 %g_tColor - %88 = OpLoad %50 %g_sAniso - %89 = OpSampledImage %54 %87 %88 - %91 = OpImageSampleImplicitLod %v4float %89 %86 - OpStore %_entryPointOutput_vColor %91 +%87 = OpLoad %46 %g_tColor +%88 = OpLoad %50 %g_sAniso +%89 = OpSampledImage %54 %87 %88 +%91 = OpImageSampleImplicitLod %v4float %89 %86 +OpStore %_entryPointOutput_vColor %91 ;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86 ;CHECK-NOT: OpStore %_entryPointOutput_vColor %91 -;CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_84 {{%\w+}} %uint_0 %uint_0 %uint_0 %uint_0 ;CHECK: OpSelectionMerge {{%\w+}} None ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel @@ -4314,20 +3942,18 @@ ;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %86 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0 %uint_0 %uint_0 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} ;CHECK: OpStore %_entryPointOutput_vColor {{%\w+}} - OpReturn - OpFunctionEnd -)" + kReadDescInit + kStreamWrite7Frag; +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true, - true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { @@ -4338,124 +3964,107 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability Int16 - OpCapability StoragePushConstant16 -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_output_buffer %gl_FragCoord %inst_bindless_input_buffer - OpExecutionMode %MainPs OriginUpperLeft - OpSource HLSL 500 - OpName %MainPs "MainPs" - OpName %g_tColor "g_tColor" - OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" - OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" - OpName %_ "" - OpName %g_sAniso "g_sAniso" - OpName %i_vTextureCoords "i.vTextureCoords" - OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" - OpDecorate %g_tColor DescriptorSet 1 - OpDecorate %g_tColor Binding 2 - OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 - OpDecorate %PerViewConstantBuffer_t Block - OpDecorate %g_sAniso DescriptorSet 1 - OpDecorate %g_sAniso Binding 2 - OpDecorate %i_vTextureCoords Location 0 - OpDecorate %_entryPointOutput_vColor Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -)" + kInputDecorations + R"( - %void = OpTypeVoid - %10 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v2float = OpTypeVector %float 2 - %v4float = OpTypeVector %float 4 - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 - %16 = OpTypeImage %float 2D 0 0 0 1 Unknown - %uint = OpTypeInt 32 0 - %uint_128 = OpConstant %uint 128 +OpCapability Shader +OpCapability Int16 +OpCapability StoragePushConstant16 +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 1 +OpDecorate %g_tColor Binding 2 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 1 +OpDecorate %g_sAniso Binding 2 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 %_arr_16_uint_128 = OpTypeArray %16 %uint_128 %_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 - %g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant - %ushort = OpTypeInt 16 0 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant +%ushort = OpTypeInt 16 0 %PerViewConstantBuffer_t = OpTypeStruct %ushort %_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t - %_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant %_ptr_PushConstant_ushort = OpTypePointer PushConstant %ushort %_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 - %25 = OpTypeSampler +%25 = OpTypeSampler %_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 - %g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant - %27 = OpTypeSampledImage %16 +%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant +%27 = OpTypeSampledImage %16 %_ptr_Input_v2float = OpTypePointer Input %v2float %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kOutputGlobals + R"( -;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float -)" + kInputGlobals + R"( - %MainPs = OpFunction %void None %10 - %30 = OpLabel -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: OpBranch %39 -;CHECK: %39 = OpLabel - %31 = OpLoad %v2float %i_vTextureCoords - %32 = OpAccessChain %_ptr_PushConstant_ushort %_ %int_0 - %33 = OpLoad %ushort %32 - %34 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %33 - %35 = OpLoad %16 %34 - %36 = OpLoad %25 %g_sAniso - %37 = OpSampledImage %27 %35 %36 - %38 = OpImageSampleImplicitLod %v4float %37 %31 - OpStore %_entryPointOutput_vColor %38 -;CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31 -;CHECK-NOT: OpStore %_entryPointOutput_vColor %38 -;CHECK: %41 = OpUConvert %uint %33 -;CHECK: {{%\w+}} = OpULessThan %bool %41 %uint_128 -;CHECK: OpSelectionMerge {{%\w+}} None -;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpLoad %16 %34 -;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36 -;CHECK: {{%\w+}} = OpUConvert %uint %33 -;CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_1 %uint_2 {{%\w+}} -;CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}} -;CHECK: OpSelectionMerge {{%\w+}} None -;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpLoad %16 %34 -;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36 -;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31 -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpUConvert %uint %33 -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_60 %uint_1 %uint_1 %uint_2 {{%\w+}} %uint_0 -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: [[phi_result_1:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} -;CHECK: OpBranch %44 -;CHECK: %46 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_6 %uint_60 %uint_0 %uint_1 %uint_2 %41 %uint_128 -;CHECK: OpBranch %44 -;CHECK: %44 = OpLabel -;CHECK: [[phi_result_2:%\w+]] = OpPhi %v4float [[phi_result_1]] {{%\w+}} [[null_v4float]] %46 -;CHECK: OpStore %_entryPointOutput_vColor [[phi_result_2]] - OpReturn - OpFunctionEnd -)" + kStreamWrite6Frag + kReadDescInit; +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +)" + kImportStub + R"( +%MainPs = OpFunction %void None %10 +%30 = OpLabel +;CHECK: OpBranch %39 +;CHECK: %39 = OpLabel +%31 = OpLoad %v2float %i_vTextureCoords +%32 = OpAccessChain %_ptr_PushConstant_ushort %_ %int_0 +%33 = OpLoad %ushort %32 +%34 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %33 +%35 = OpLoad %16 %34 +%36 = OpLoad %25 %g_sAniso +%37 = OpSampledImage %27 %35 %36 +%38 = OpImageSampleImplicitLod %v4float %37 %31 +OpStore %_entryPointOutput_vColor %38 +;CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %38 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpUConvert %uint %33 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_61 {{%\w+}} %uint_1 %uint_2 {{%\w+}} %uint_0 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %16 %34 +;CHECK: {{%\w+}} = OpSampledImage %27 {{%\w+}} %36 +;CHECK: {{%\w+}} = OpImageSampleImplicitLod %v4float {{%\w+}} %31 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %_entryPointOutput_vColor [[phi_result]] +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, true, true, - false, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { @@ -4496,136 +4105,133 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability Int16 - OpCapability StoragePushConstant16 -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor -;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %MainPs OriginUpperLeft - OpSource HLSL 500 - OpName %MainPs "MainPs" - OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" - OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" - OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" - OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" - OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" - OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" - OpName %_ "" - OpName %PerViewPushConst_t "PerViewPushConst_t" - OpMemberName %PerViewPushConst_t 0 "g_c" - OpName %__0 "" - OpName %g_tColor "g_tColor" - OpName %g_sAniso "g_sAniso" - OpName %i_vTextureCoords "i.vTextureCoords" - OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 - OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 - OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 - OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 - OpDecorate %_BindlessFastEnvMapCB_PS_t Block - OpDecorate %_ DescriptorSet 0 - OpDecorate %_ Binding 0 - OpMemberDecorate %PerViewPushConst_t 0 Offset 0 - OpDecorate %PerViewPushConst_t Block - OpDecorate %g_tColor DescriptorSet 0 - OpDecorate %g_tColor Binding 0 - OpDecorate %g_sAniso DescriptorSet 0 - OpDecorate %g_sAniso Binding 0 - OpDecorate %i_vTextureCoords Location 0 - OpDecorate %_entryPointOutput_vColor Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %14 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v2float = OpTypeVector %float 2 - %v4float = OpTypeVector %float 4 - %v3float = OpTypeVector %float 3 +OpCapability Shader +OpCapability Int16 +OpCapability StoragePushConstant16 +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" +OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" +OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" +OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" +OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" +OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" +OpName %_ "" +OpName %PerViewPushConst_t "PerViewPushConst_t" +OpMemberName %PerViewPushConst_t 0 "g_c" +OpName %__0 "" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 +OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 +OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 +OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 +OpDecorate %_BindlessFastEnvMapCB_PS_t Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpMemberDecorate %PerViewPushConst_t 0 Offset 0 +OpDecorate %PerViewPushConst_t Block +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%14 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%v3float = OpTypeVector %float 3 %mat4v3float = OpTypeMatrix %v3float 4 %PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float - %uint = OpTypeInt 32 0 - %uint_128 = OpConstant %uint 128 +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128 %_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 %_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t - %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 - %ushort = OpTypeInt 16 0 +%_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%ushort = OpTypeInt 16 0 %PerViewPushConst_t = OpTypeStruct %ushort %_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t - %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant %_ptr_PushConstant_ushort = OpTypePointer PushConstant %ushort - %int_2 = OpConstant %int 2 +%int_2 = OpConstant %int 2 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float - %30 = OpTypeImage %float 2D 0 0 0 1 Unknown +%30 = OpTypeImage %float 2D 0 0 0 1 Unknown %_ptr_UniformConstant_30 = OpTypePointer UniformConstant %30 - %g_tColor = OpVariable %_ptr_UniformConstant_30 UniformConstant - %32 = OpTypeSampler +%g_tColor = OpVariable %_ptr_UniformConstant_30 UniformConstant +%32 = OpTypeSampler %_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 - %g_sAniso = OpVariable %_ptr_UniformConstant_32 UniformConstant - %34 = OpTypeSampledImage %30 +%g_sAniso = OpVariable %_ptr_UniformConstant_32 UniformConstant +%34 = OpTypeSampledImage %30 %_ptr_Input_v2float = OpTypePointer Input %v2float %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -)" + kInputGlobals + kOutputGlobals + R"( -;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float - %MainPs = OpFunction %void None %14 - %37 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_0 %uint_0 -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel - %38 = OpLoad %v2float %i_vTextureCoords - %39 = OpAccessChain %_ptr_PushConstant_ushort %__0 %int_0 - %40 = OpLoad %ushort %39 - %41 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %40 %int_2 - %42 = OpLoad %v2float %41 - %43 = OpFAdd %v2float %38 %42 -;CHECK-NOT: %42 = OpLoad %v2float %41 -;CHECK-NOT: %43 = OpFAdd %v2float %38 %42 +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float +)" + kImportStub + R"( +%MainPs = OpFunction %void None %14 +%37 = OpLabel +%38 = OpLoad %v2float %i_vTextureCoords +%39 = OpAccessChain %_ptr_PushConstant_ushort %__0 %int_0 +%40 = OpLoad %ushort %39 +%41 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %40 %int_2 +%42 = OpLoad %v2float %41 +%43 = OpFAdd %v2float %38 %42 +;CHECK-NOT: %42 = OpLoad %v2float %41 +;CHECK-NOT: %43 = OpFAdd %v2float %38 %42 ;CHECK: {{%\w+}} = OpUConvert %uint %40 ;CHECK: {{%\w+}} = OpIMul %uint %uint_80 {{%\w+}} ;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_64 ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_82 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpLoad %v2float %41 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_81 %uint_4 %uint_0 %uint_0 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: {{%\w+}} = OpPhi %v2float {{%\w+}} {{%\w+}} [[null_v2float]] {{%\w+}} ;CHECK: %43 = OpFAdd %v2float %38 {{%\w+}} - %44 = OpLoad %30 %g_tColor - %45 = OpLoad %32 %g_sAniso - %46 = OpSampledImage %34 %44 %45 - %47 = OpImageSampleImplicitLod %v4float %46 %43 - OpStore %_entryPointOutput_vColor %47 - OpReturn - OpFunctionEnd - )" + kReadDescInit + kStreamWrite7Frag; +%44 = OpLoad %30 %g_tColor +%45 = OpLoad %32 %g_sAniso +%46 = OpSampledImage %34 %44 %45 +%47 = OpImageSampleImplicitLod %v4float %46 %43 +OpStore %_entryPointOutput_vColor %47 +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { @@ -4649,102 +4255,98 @@ // clang-format off std::string text = R"( - OpCapability Shader -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position -;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex - OpSource GLSL 450 - OpSourceExtension "GL_EXT_scalar_block_layout" - OpName %main "main" - OpName %v_vtxResult "v_vtxResult" - OpName %Block "Block" - OpMemberName %Block 0 "var" - OpName %_ "" - OpName %a_position "a_position" - OpDecorate %v_vtxResult RelaxedPrecision - OpDecorate %v_vtxResult Location 0 - OpMemberDecorate %Block 0 RowMajor - OpMemberDecorate %Block 0 RelaxedPrecision - OpMemberDecorate %Block 0 Offset 0 - OpMemberDecorate %Block 0 MatrixStride 16 - OpDecorate %Block Block - OpDecorate %_ DescriptorSet 0 - OpDecorate %_ Binding 0 - OpDecorate %21 RelaxedPrecision -;CHECK-NOT: OpDecorate %21 RelaxedPrecision +OpCapability Shader +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position +;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %gl_VertexIndex %gl_InstanceIndex +OpSource GLSL 450 +OpSourceExtension "GL_EXT_scalar_block_layout" +OpName %main "main" +OpName %v_vtxResult "v_vtxResult" +OpName %Block "Block" +OpMemberName %Block 0 "var" +OpName %_ "" +OpName %a_position "a_position" +OpDecorate %v_vtxResult RelaxedPrecision +OpDecorate %v_vtxResult Location 0 +OpMemberDecorate %Block 0 RowMajor +OpMemberDecorate %Block 0 RelaxedPrecision +OpMemberDecorate %Block 0 Offset 0 +OpMemberDecorate %Block 0 MatrixStride 16 +OpDecorate %Block Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %21 RelaxedPrecision +;CHECK-NOT: OpDecorate %21 RelaxedPrecision ;CHECK: OpDecorate %v_vtxResult RelaxedPrecision ;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision - OpDecorate %a_position Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + R"( -;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision -)" + kOutputDecorations + R"( +OpDecorate %a_position Location 0 +)" + kImportDeco + R"( ;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex ;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 +;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 %_ptr_Output_float = OpTypePointer Output %float %v_vtxResult = OpVariable %_ptr_Output_float Output - %v2float = OpTypeVector %float 2 +%v2float = OpTypeVector %float 2 %mat4v2float = OpTypeMatrix %v2float 4 - %Block = OpTypeStruct %mat4v2float +%Block = OpTypeStruct %mat4v2float %_ptr_Uniform_Block = OpTypePointer Uniform %Block - %_ = OpVariable %_ptr_Uniform_Block Uniform - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 - %int_2 = OpConstant %int 2 - %uint = OpTypeInt 32 0 - %uint_1 = OpConstant %uint 1 +%_ = OpVariable %_ptr_Uniform_Block Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 %_ptr_Uniform_float = OpTypePointer Uniform %float - %v4float = OpTypeVector %float 4 +%v4float = OpTypeVector %float 4 %_ptr_Input_v4float = OpTypePointer Input %v4float - %a_position = OpVariable %_ptr_Input_v4float Input -)" + kInputGlobals + kOutputGlobals + R"( -;CHECK:%_ptr_Input_uint = OpTypePointer Input %uint -;CHECK:%gl_VertexIndex = OpVariable %_ptr_Input_uint Input -;CHECK:%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +%a_position = OpVariable %_ptr_Input_v4float Input +;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input +;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input ;CHECK: [[null_float:%\w+]] = OpConstantNull %float - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: [[desc_state:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_0 %uint_0 +)" + kImportStub + R"( +%main = OpFunction %void None %3 +%5 = OpLabel ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel - %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 - %21 = OpLoad %float %20 -;CHECK-NOT: %21 = OpLoad %float %20 +%20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 +%21 = OpLoad %float %20 +;CHECK-NOT: %21 = OpLoad %float %20 ;CHECK: {{%\w+}} = OpIMul %uint %uint_4 %int_2 ;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} ;CHECK: {{%\w+}} = OpIMul %uint %uint_16 %uint_1 ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} [[desc_state]] +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[desc_state:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None -;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpBranchConditional [[desc_state]] {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[load_result]] = OpLoad %float %20 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_45 %uint_4 %uint_0 %uint_0 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[phi_result]] = OpPhi %float [[load_result]] {{%\w+}} [[null_float]] {{%\w+}} - OpStore %v_vtxResult %21 -;CHECK-NOT: OpStore %v_vtxResult %21 -;CHECK: OpStore %v_vtxResult [[phi_result]] - OpReturn - OpFunctionEnd - )" + kReadDescInit + kStreamWrite7Vert; +OpStore %v_vtxResult %21 +;CHECK-NOT: OpStore %v_vtxResult %21$ +;CHECK: OpStore %v_vtxResult [[phi_result]] +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { @@ -4768,103 +4370,99 @@ // clang-format off const std::string text = R"( - OpCapability Shader -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position -;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex - OpSource GLSL 450 - OpSourceExtension "GL_EXT_scalar_block_layout" - OpName %main "main" - OpName %v_vtxResult "v_vtxResult" - OpName %Block "Block" - OpMemberName %Block 0 "var" - OpName %_ "" - OpName %a_position "a_position" - OpDecorate %v_vtxResult RelaxedPrecision - OpDecorate %v_vtxResult Location 0 - OpMemberDecorate %Block 0 ColMajor - OpMemberDecorate %Block 0 RelaxedPrecision - OpMemberDecorate %Block 0 Offset 0 - OpMemberDecorate %Block 0 MatrixStride 8 - OpDecorate %Block Block - OpDecorate %_ DescriptorSet 0 - OpDecorate %_ Binding 0 - OpDecorate %21 RelaxedPrecision -;CHECK-NOT: OpDecorate %21 RelaxedPrecision +OpCapability Shader +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position +;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %gl_VertexIndex %gl_InstanceIndex +OpSource GLSL 450 +OpSourceExtension "GL_EXT_scalar_block_layout" +OpName %main "main" +OpName %v_vtxResult "v_vtxResult" +OpName %Block "Block" +OpMemberName %Block 0 "var" +OpName %_ "" +OpName %a_position "a_position" +OpDecorate %v_vtxResult RelaxedPrecision +OpDecorate %v_vtxResult Location 0 +OpMemberDecorate %Block 0 ColMajor +OpMemberDecorate %Block 0 RelaxedPrecision +OpMemberDecorate %Block 0 Offset 0 +OpMemberDecorate %Block 0 MatrixStride 8 +OpDecorate %Block Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %21 RelaxedPrecision +;CHECK-NOT: OpDecorate %21 RelaxedPrecision ;CHECK: OpDecorate %v_vtxResult RelaxedPrecision ;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision - OpDecorate %a_position Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + R"( -;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision -)" + kOutputDecorations + R"( +OpDecorate %a_position Location 0 +)" + kImportDeco + R"( ;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex ;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 +;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 %_ptr_Output_float = OpTypePointer Output %float %v_vtxResult = OpVariable %_ptr_Output_float Output - %v2float = OpTypeVector %float 2 +%v2float = OpTypeVector %float 2 %mat4v2float = OpTypeMatrix %v2float 4 - %Block = OpTypeStruct %mat4v2float +%Block = OpTypeStruct %mat4v2float %_ptr_Uniform_Block = OpTypePointer Uniform %Block - %_ = OpVariable %_ptr_Uniform_Block Uniform - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 - %int_2 = OpConstant %int 2 - %uint = OpTypeInt 32 0 - %uint_1 = OpConstant %uint 1 +%_ = OpVariable %_ptr_Uniform_Block Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 %_ptr_Uniform_float = OpTypePointer Uniform %float - %v4float = OpTypeVector %float 4 +%v4float = OpTypeVector %float 4 %_ptr_Input_v4float = OpTypePointer Input %v4float - %a_position = OpVariable %_ptr_Input_v4float Input -)" + kInputGlobals + kOutputGlobals + R"( -;CHECK:%_ptr_Input_uint = OpTypePointer Input %uint -;CHECK:%gl_VertexIndex = OpVariable %_ptr_Input_uint Input -;CHECK:%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +%a_position = OpVariable %_ptr_Input_v4float Input +;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input +;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input ;CHECK: [[null_float:%\w+]] = OpConstantNull %float +)" + kImportStub + R"( %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: [[desc_state:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_0 %uint_0 -;CHECK: OpBranch %26 -;CHECK: %26 = OpLabel -;CHECK: OpBranch %25 -;CHECK: %25 = OpLabel - %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 - %21 = OpLoad %float %20 -;CHECK-NOT: %21 = OpLoad %float %20 -;CHECK: %29 = OpIMul %uint %uint_8 %int_2 -;CHECK: %30 = OpIAdd %uint %uint_0 %29 -;CHECK: %32 = OpIMul %uint %uint_4 %uint_1 -;CHECK: %33 = OpIAdd %uint %30 %32 -;CHECK: %35 = OpIAdd %uint %33 %uint_3 -;CHECK: {{%\w+}} = OpULessThan %bool %35 [[desc_state]] +%5 = OpLabel +%20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 +%21 = OpLoad %float %20 +;CHECK-NOT: %21 = OpLoad %float %20 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpIMul %uint %uint_8 %int_2 +;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} +;CHECK: {{%\w+}} = OpIMul %uint %uint_4 %uint_1 +;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[desc_state:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_46 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None -;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpBranchConditional [[desc_state]] {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK:[[load_result]] = OpLoad %float %20 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_45 %uint_4 %uint_0 %uint_0 %uint_0 %35 {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[phi_result]] = OpPhi %float [[load_result]] {{%\w+}} [[null_float]] {{%\w+}} - OpStore %v_vtxResult %21 -;CHECK-NOT: OpStore %v_vtxResult %21 +OpStore %v_vtxResult %21 +;CHECK-NOT: OpStore %v_vtxResult %21$ ;CHECK: OpStore %v_vtxResult [[phi_result]] - OpReturn - OpFunctionEnd - )" + kReadDescInit + kStreamWrite7Vert; +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); ValidatorOptions()->uniform_buffer_standard_layout = true; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { @@ -4888,109 +4486,103 @@ // clang-format off const std::string text = R"( - OpCapability Shader -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position -;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex - OpSource GLSL 450 - OpSourceExtension "GL_EXT_scalar_block_layout" - OpName %main "main" - OpName %v_vtxResult "v_vtxResult" - OpName %Block "Block" - OpMemberName %Block 0 "var" - OpName %_ "" - OpName %a_position "a_position" - OpDecorate %v_vtxResult Location 0 - OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 32 - OpDecorate %_arr__arr_mat2v2float_uint_4_uint_3 ArrayStride 128 - OpMemberDecorate %Block 0 RowMajor - OpMemberDecorate %Block 0 RelaxedPrecision - OpMemberDecorate %Block 0 Offset 0 - OpMemberDecorate %Block 0 MatrixStride 16 - OpDecorate %Block Block - OpDecorate %_ DescriptorSet 3 - OpDecorate %_ Binding 7 - OpDecorate %26 RelaxedPrecision -;CHECK-NOT: OpDecorate %26 RelaxedPrecision +OpCapability Shader +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position +;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %gl_VertexIndex %gl_InstanceIndex +OpSource GLSL 450 +OpSourceExtension "GL_EXT_scalar_block_layout" +OpName %main "main" +OpName %v_vtxResult "v_vtxResult" +OpName %Block "Block" +OpMemberName %Block 0 "var" +OpName %_ "" +OpName %a_position "a_position" +OpDecorate %v_vtxResult Location 0 +OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 32 +OpDecorate %_arr__arr_mat2v2float_uint_4_uint_3 ArrayStride 128 +OpMemberDecorate %Block 0 RowMajor +OpMemberDecorate %Block 0 RelaxedPrecision +OpMemberDecorate %Block 0 Offset 0 +OpMemberDecorate %Block 0 MatrixStride 16 +OpDecorate %Block Block +OpDecorate %_ DescriptorSet 3 +OpDecorate %_ Binding 7 +OpDecorate %26 RelaxedPrecision +;CHECK-NOT: OpDecorate %26 RelaxedPrecision ;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision - OpDecorate %a_position Location 0 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kInputDecorations + R"( -;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision -)" + kOutputDecorations + R"( +OpDecorate %a_position Location 0 +)" + kImportDeco + R"( ;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex ;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v2float = OpTypeVector %float 2 +;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 %_ptr_Output_v2float = OpTypePointer Output %v2float %v_vtxResult = OpVariable %_ptr_Output_v2float Output %mat2v2float = OpTypeMatrix %v2float 2 - %uint = OpTypeInt 32 0 - %uint_4 = OpConstant %uint 4 +%uint = OpTypeInt 32 0 +%uint_4 = OpConstant %uint 4 %_arr_mat2v2float_uint_4 = OpTypeArray %mat2v2float %uint_4 - %uint_3 = OpConstant %uint 3 +%uint_3 = OpConstant %uint 3 %_arr__arr_mat2v2float_uint_4_uint_3 = OpTypeArray %_arr_mat2v2float_uint_4 %uint_3 - %Block = OpTypeStruct %_arr__arr_mat2v2float_uint_4_uint_3 +%Block = OpTypeStruct %_arr__arr_mat2v2float_uint_4_uint_3 %_ptr_Uniform_Block = OpTypePointer Uniform %Block - %_ = OpVariable %_ptr_Uniform_Block Uniform - %int = OpTypeInt 32 1 - %int_0 = OpConstant %int 0 - %int_2 = OpConstant %int 2 - %int_3 = OpConstant %int 3 - %int_1 = OpConstant %int 1 +%_ = OpVariable %_ptr_Uniform_Block Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_1 = OpConstant %int 1 %_ptr_Uniform_v2float = OpTypePointer Uniform %v2float - %v4float = OpTypeVector %float 4 +%v4float = OpTypeVector %float 4 %_ptr_Input_v4float = OpTypePointer Input %v4float - %a_position = OpVariable %_ptr_Input_v4float Input -)" + kInputGlobals + kOutputGlobals + R"( -;CHECK:%_ptr_Input_uint = OpTypePointer Input %uint -;CHECK:%gl_VertexIndex = OpVariable %_ptr_Input_uint Input -;CHECK:%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +%a_position = OpVariable %_ptr_Input_v4float Input +;CHECK: %_ptr_Input_uint = OpTypePointer Input %uint +;CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input +;CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input ;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: [[desc_state:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_3 %uint_7 %uint_0 -;CHECK: OpBranch %31 -;CHECK: %31 = OpLabel -;CHECK: OpBranch %30 -;CHECK: %30 = OpLabel - %25 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %int_2 %int_3 %int_1 - %26 = OpLoad %v2float %25 - OpStore %v_vtxResult %26 +)" + kImportStub + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %int_2 %int_3 %int_1 +;CHECK: {{%\w+}} = OpIMul %uint %uint_128 %int_2 +;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} +;CHECK: {{%\w+}} = OpIMul %uint %uint_32 %int_3 +;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpIMul %uint %uint_4 %int_1 +;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_19 +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[desc_state:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_52 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}} +%26 = OpLoad %v2float %25 +OpStore %v_vtxResult %26 ;CHECK-NOT: %26 = OpLoad %v2float %25 -;CHECK-NOT: OpStore %v_vtxResult %26 -;CHECK: %34 = OpIMul %uint %uint_128 %int_2 -;CHECK: %35 = OpIAdd %uint %uint_0 %34 -;CHECK: %37 = OpIMul %uint %uint_32 %int_3 -;CHECK: %38 = OpIAdd %uint %35 %37 -;CHECK: %40 = OpIMul %uint %uint_4 %int_1 -;CHECK: %41 = OpIAdd %uint %38 %40 -;CHECK: %43 = OpIAdd %uint %41 %uint_19 -;CHECK: {{%\w+}} = OpULessThan %bool %43 [[desc_state]] +;CHECK-NOT: OpStore %v_vtxResult %26 ;CHECK: OpSelectionMerge {{%\w+}} None -;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpBranchConditional [[desc_state]] {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[load_result]] = OpLoad %v2float %25 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_51 %uint_4 %uint_3 %uint_7 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[phi_result]] = OpPhi %v2float [[load_result]] {{%\w+}} [[null_v2float]] {{%\w+}} ;CHECK: OpStore %v_vtxResult [[phi_result]] - OpReturn - OpFunctionEnd - )" + kReadDescInit + kStreamWrite7Vert; +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, false, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, ImageBufferOOBRead) { @@ -5007,86 +4599,76 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability ImageBuffer -;CHECK: OpCapability ImageQuery -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %x %s %ii - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 450 - OpName %main "main" - OpName %x "x" - OpName %s "s" - OpName %ii "ii" - OpDecorate %x Location 11 - OpDecorate %s DescriptorSet 3 - OpDecorate %s Binding 7 - OpDecorate %s NonWritable - OpDecorate %ii Flat - OpDecorate %ii Location 13 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 - %_ptr_Output_v4float = OpTypePointer Output %v4float - %x = OpVariable %_ptr_Output_v4float Output - %10 = OpTypeImage %float Buffer 0 0 0 2 R32f - %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 - %s = OpVariable %_ptr_UniformConstant_10 UniformConstant - %int = OpTypeInt 32 1 - %_ptr_Input_int = OpTypePointer Input %int - %ii = OpVariable %_ptr_Input_int Input -;CHECK: %uint = OpTypeInt 32 0 -;CHECK: %bool = OpTypeBool -;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +OpCapability Shader +OpCapability ImageBuffer +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %x %s %ii +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %x "x" +OpName %s "s" +OpName %ii "ii" +OpDecorate %x Location 11 +OpDecorate %s DescriptorSet 3 +OpDecorate %s Binding 7 +OpDecorate %s NonWritable +OpDecorate %ii Flat +OpDecorate %ii Location 13 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%x = OpVariable %_ptr_Output_v4float Output +%10 = OpTypeImage %float Buffer 0 0 0 2 R32f +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 +%s = OpVariable %_ptr_UniformConstant_10 UniformConstant +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%ii = OpVariable %_ptr_Input_int Input +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: OpBranch %21 -;CHECK: %21 = OpLabel -;CHECK: OpBranch %20 -;CHECK: %20 = OpLabel -;CHECK: OpBranch %19 -;CHECK: %19 = OpLabel - %13 = OpLoad %10 %s - %17 = OpLoad %int %ii - %18 = OpImageRead %v4float %13 %17 - OpStore %x %18 -;CHECK-NOT: %18 = OpImageRead %v4float %13 %17 -;CHECK-NOT: OpStore %x %18 -;CHECK: %23 = OpBitcast %uint %17 -;CHECK: %25 = OpImageQuerySize %uint %13 -;CHECK: %27 = OpULessThan %bool %23 %25 -;CHECK: OpSelectionMerge %29 None -;CHECK: OpBranchConditional %27 %30 %31 -;CHECK: %30 = OpLabel -;CHECK: %32 = OpLoad %10 %s -;CHECK: %33 = OpImageRead %v4float %32 %17 -;CHECK: OpBranch %29 -;CHECK: %31 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_33 %uint_7 %uint_3 %uint_7 %uint_0 %23 %25 -;CHECK: OpBranch %29 -;CHECK: %29 = OpLabel -;CHECK: [[phi_result:%\w+]] = OpPhi %v4float %33 %30 [[null_v4float]] %31 -;CHECK: OpStore %x [[phi_result]] - OpReturn - OpFunctionEnd - )" + kStreamWrite7Frag; +%main = OpFunction %void None %3 +%5 = OpLabel +;CHECK: OpBranch %19 +;CHECK: %19 = OpLabel +%13 = OpLoad %10 %s +%17 = OpLoad %int %ii +%18 = OpImageRead %v4float %13 %17 +OpStore %x %18 +;CHECK-NOT: %18 = OpImageRead %v4float %13 %17 +;CHECK-NOT: OpStore %x %18 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_34 {{%\w+}} %uint_3 %uint_7 %uint_0 %22 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %10 %s +;CHECK: {{%\w+}} = OpImageRead %v4float {{%\w+}} %17 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %x [[phi_result]] +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, true, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, ImageBufferOOBWrite) { @@ -5103,85 +4685,75 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability ImageBuffer -;CHECK: OpCapability ImageQuery -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %s %ii %x -;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 450 - OpName %main "main" - OpName %s "s" - OpName %ii "ii" - OpName %x "x" - OpDecorate %s DescriptorSet 3 - OpDecorate %s Binding 7 - OpDecorate %s NonReadable - OpDecorate %ii Flat - OpDecorate %ii Location 13 - OpDecorate %x Location 11 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %7 = OpTypeImage %float Buffer 0 0 0 2 R32f - %_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 - %s = OpVariable %_ptr_UniformConstant_7 UniformConstant - %int = OpTypeInt 32 1 - %_ptr_Input_int = OpTypePointer Input %int - %ii = OpVariable %_ptr_Input_int Input - %v4float = OpTypeVector %float 4 - %_ptr_Output_v4float = OpTypePointer Output %v4float - %x = OpVariable %_ptr_Output_v4float Output -;CHECK: %uint = OpTypeInt 32 0 -;CHECK: %bool = OpTypeBool -;CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint %uint -;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -;CHECK: %v4uint = OpTypeVector %uint 4 - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: OpBranch %21 -;CHECK: %21 = OpLabel -;CHECK: OpBranch %20 -;CHECK: %20 = OpLabel -;CHECK: OpBranch %19 -;CHECK: %19 = OpLabel - %10 = OpLoad %7 %s - %14 = OpLoad %int %ii - %18 = OpLoad %v4float %x - OpImageWrite %10 %14 %18 -;CHECK-NOT: OpImageWrite %10 %14 %18 -;CHECK: %23 = OpBitcast %uint %14 -;CHECK: %25 = OpImageQuerySize %uint %10 -;CHECK: %27 = OpULessThan %bool %23 %25 -;CHECK: OpSelectionMerge %29 None -;CHECK: OpBranchConditional %27 %30 %31 -;CHECK: %30 = OpLabel -;CHECK: %32 = OpLoad %7 %s -;CHECK: OpImageWrite %32 %14 %18 -;CHECK: OpBranch %29 -;CHECK: %31 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_34 %uint_7 %uint_3 %uint_7 %uint_0 %23 %25 -;CHECK: OpBranch %29 -;CHECK: %29 = OpLabel - OpReturn - OpFunctionEnd - )" + kStreamWrite7Frag; +OpCapability Shader +OpCapability ImageBuffer +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %s %ii %x +;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %s "s" +OpName %ii "ii" +OpName %x "x" +OpDecorate %s DescriptorSet 3 +OpDecorate %s Binding 7 +OpDecorate %s NonReadable +OpDecorate %ii Flat +OpDecorate %ii Location 13 +OpDecorate %x Location 11 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%7 = OpTypeImage %float Buffer 0 0 0 2 R32f +%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 +%s = OpVariable %_ptr_UniformConstant_7 UniformConstant +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%ii = OpVariable %_ptr_Input_int Input +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%x = OpVariable %_ptr_Output_v4float Output +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +)" + kImportStub + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: %19 = OpLabel +%10 = OpLoad %7 %s +%14 = OpLoad %int %ii +%18 = OpLoad %v4float %x +OpImageWrite %10 %14 %18 +;CHECK-NOT: OpImageWrite %10 %14 %18 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_35 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %7 %s +;CHECK: OpImageWrite {{%\w+}} %14 %18 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, true, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, TextureBufferOOBFetch) { @@ -5198,86 +4770,75 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability SampledBuffer -;CHECK: OpCapability ImageQuery -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %x %s %ii -;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 450 - OpName %main "main" - OpName %x "x" - OpName %s "s" - OpName %ii "ii" - OpDecorate %x Location 11 - OpDecorate %s DescriptorSet 3 - OpDecorate %s Binding 7 - OpDecorate %ii Flat - OpDecorate %ii Location 13 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 - %_ptr_Output_v4float = OpTypePointer Output %v4float - %x = OpVariable %_ptr_Output_v4float Output - %10 = OpTypeImage %float Buffer 0 0 0 1 Unknown - %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 - %s = OpVariable %_ptr_UniformConstant_10 UniformConstant - %int = OpTypeInt 32 1 - %_ptr_Input_int = OpTypePointer Input %int - %ii = OpVariable %_ptr_Input_int Input -;CHECK: %uint = OpTypeInt 32 0 -;CHECK: %bool = OpTypeBool -;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +OpCapability Shader +OpCapability SampledBuffer +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %x %s %ii +;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %x "x" +OpName %s "s" +OpName %ii "ii" +OpDecorate %x Location 11 +OpDecorate %s DescriptorSet 3 +OpDecorate %s Binding 7 +OpDecorate %ii Flat +OpDecorate %ii Location 13 +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%x = OpVariable %_ptr_Output_v4float Output +%10 = OpTypeImage %float Buffer 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 +%s = OpVariable %_ptr_UniformConstant_10 UniformConstant +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%ii = OpVariable %_ptr_Input_int Input +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: OpBranch %21 -;CHECK: %21 = OpLabel -;CHECK: OpBranch %20 -;CHECK: %20 = OpLabel -;CHECK: OpBranch %19 -;CHECK: %19 = OpLabel - %13 = OpLoad %10 %s - %17 = OpLoad %int %ii - %18 = OpImageFetch %v4float %13 %17 - OpStore %x %18 -;CHECK-NOT: %18 = OpImageFetch %v4float %13 %17 -;CHECK-NOT: OpStore %x %18 -;CHECK: %23 = OpBitcast %uint %17 -;CHECK: %25 = OpImageQuerySize %uint %13 -;CHECK: %27 = OpULessThan %bool %23 %25 -;CHECK: OpSelectionMerge %29 None -;CHECK: OpBranchConditional %27 %30 %31 -;CHECK: %30 = OpLabel -;CHECK: %32 = OpLoad %10 %s -;CHECK: %33 = OpImageFetch %v4float %32 %17 -;CHECK: OpBranch %29 -;CHECK: %31 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_32 %uint_6 %uint_3 %uint_7 %uint_0 %23 %25 -;CHECK: OpBranch %29 -;CHECK: %29 = OpLabel -;CHECK: [[phi_result:%\w+]] = OpPhi %v4float %33 %30 [[null_v4float]] %31 -;CHECK: OpStore %x [[phi_result]] - OpReturn - OpFunctionEnd - )" + kStreamWrite7Frag; +%main = OpFunction %void None %3 +%5 = OpLabel +;CHECK: OpBranch %19 +;CHECK: %19 = OpLabel +%13 = OpLoad %10 %s +%17 = OpLoad %int %ii +%18 = OpImageFetch %v4float %13 %17 +OpStore %x %18 +;CHECK-NOT: %18 = OpImageFetch %v4float %13 %17 +;CHECK-NOT: OpStore %x %18 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_33 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %10 %s +;CHECK: {{%\w+}} = OpImageFetch %v4float {{%\w+}} %17 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %x [[phi_result]] +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, true, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { @@ -5294,89 +4855,80 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability SampledBuffer -;CHECK: OpCapability ImageQuery -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %x %s %ii -;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 450 - OpName %main "main" - OpName %x "x" - OpName %s "s" - OpName %ii "ii" - OpDecorate %x Location 11 - OpDecorate %s DescriptorSet 3 - OpDecorate %s Binding 7 - OpDecorate %ii Flat - OpDecorate %ii Location 13 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 - %_ptr_Output_v4float = OpTypePointer Output %v4float - %x = OpVariable %_ptr_Output_v4float Output - %10 = OpTypeImage %float Buffer 0 0 0 1 Unknown - %11 = OpTypeSampledImage %10 - %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 - %s = OpVariable %_ptr_UniformConstant_11 UniformConstant - %int = OpTypeInt 32 1 - %_ptr_Input_int = OpTypePointer Input %int - %ii = OpVariable %_ptr_Input_int Input -;CHECK: %uint = OpTypeInt 32 0 -;CHECK: %bool = OpTypeBool -;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +OpCapability Shader +OpCapability SampledBuffer +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %x %s %ii +;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %x "x" +OpName %s "s" +OpName %ii "ii" +OpDecorate %x Location 11 +OpDecorate %s DescriptorSet 3 +OpDecorate %s Binding 7 +OpDecorate %ii Flat +OpDecorate %ii Location 13 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%x = OpVariable %_ptr_Output_v4float Output +%10 = OpTypeImage %float Buffer 0 0 0 1 Unknown +%11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 +%s = OpVariable %_ptr_UniformConstant_11 UniformConstant +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%ii = OpVariable %_ptr_Input_int Input +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: OpBranch %23 -;CHECK: %23 = OpLabel -;CHECK: OpBranch %22 -;CHECK: %22 = OpLabel -;CHECK: OpBranch %21 -;CHECK: %21 = OpLabel - %14 = OpLoad %11 %s - %18 = OpLoad %int %ii - %19 = OpImage %10 %14 - %20 = OpImageFetch %v4float %19 %18 - OpStore %x %20 -;CHECK-NOT: %20 = OpImageFetch %v4float %19 %18 -;CHECK-NOT: OpStore %x %20 -;CHECK: %25 = OpBitcast %uint %18 -;CHECK: %27 = OpImageQuerySize %uint %19 -;CHECK: %29 = OpULessThan %bool %25 %27 -;CHECK: OpSelectionMerge %31 None -;CHECK: OpBranchConditional %29 %32 %33 -;CHECK: %32 = OpLabel -;CHECK: %34 = OpLoad %11 %s -;CHECK: %35 = OpImage %10 %34 -;CHECK: %36 = OpImageFetch %v4float %35 %18 -;CHECK: OpBranch %31 -;CHECK: %33 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_34 %uint_6 %uint_3 %uint_7 %uint_0 %25 %27 -;CHECK: OpBranch %31 -;CHECK: %31 = OpLabel -;CHECK: [[phi_result:%\w+]] = OpPhi %v4float %36 %32 [[null_v4float]] %33 -;CHECK: OpStore %x [[phi_result]] - OpReturn - OpFunctionEnd - )" + kStreamWrite7Frag; +)" + kImportStub + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +;CHECK: OpBranch %21 +;CHECK: %21 = OpLabel +%14 = OpLoad %11 %s +%18 = OpLoad %int %ii +%19 = OpImage %10 %14 +%20 = OpImageFetch %v4float %19 %18 +OpStore %x %20 +;CHECK-NOT: %20 = OpImageFetch %v4float %19 %18 +;CHECK-NOT: OpStore %x %20 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_35 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %11 %s +;CHECK: {{%\w+}} = OpImage %10 {{%\w+}} +;CHECK: {{%\w+}} = OpImageFetch %v4float {{%\w+}} {{%\w+}} +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %x [[phi_result]] +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, true, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { @@ -5394,98 +4946,86 @@ // clang-format off const std::string text = R"( - OpCapability Shader - OpCapability SampledBuffer -;CHECK: OpCapability ImageQuery -;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %x %tBuf %s %ii -;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %inst_bindless_output_buffer %gl_FragCoord - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 450 - OpName %main "main" - OpName %x "x" - OpName %tBuf "tBuf" - OpName %s "s" - OpName %ii "ii" - OpDecorate %x Location 11 - OpDecorate %tBuf DescriptorSet 3 - OpDecorate %tBuf Binding 7 - OpDecorate %s DescriptorSet 3 - OpDecorate %s Binding 8 - OpDecorate %ii Flat - OpDecorate %ii Location 13 -;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 - %_ptr_Output_v4float = OpTypePointer Output %v4float - %x = OpVariable %_ptr_Output_v4float Output - %10 = OpTypeImage %float Buffer 0 0 0 1 Unknown - %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 - %tBuf = OpVariable %_ptr_UniformConstant_10 UniformConstant - %14 = OpTypeSampler - %_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 - %s = OpVariable %_ptr_UniformConstant_14 UniformConstant - %18 = OpTypeSampledImage %10 - %int = OpTypeInt 32 1 - %_ptr_Input_int = OpTypePointer Input %int - %ii = OpVariable %_ptr_Input_int Input -;CHECK: %uint = OpTypeInt 32 0 -;CHECK: %bool = OpTypeBool -;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float - %main = OpFunction %void None %3 - %5 = OpLabel -;CHECK: OpBranch %28 -;CHECK: %28 = OpLabel -;CHECK: OpBranch %27 -;CHECK: %27 = OpLabel -;CHECK: OpBranch %26 -;CHECK: %26 = OpLabel - %13 = OpLoad %10 %tBuf - %17 = OpLoad %14 %s - %19 = OpSampledImage %18 %13 %17 - %23 = OpLoad %int %ii - %24 = OpImage %10 %19 - %25 = OpImageFetch %v4float %24 %23 - OpStore %x %25 -;CHECK-NOT: %25 = OpImageFetch %v4float %24 %23 -;CHECK-NOT: OpStore %x %25 -;CHECK: %30 = OpBitcast %uint %23 -;CHECK: %32 = OpImageQuerySize %uint %24 -;CHECK: %34 = OpULessThan %bool %30 %32 -;CHECK: OpSelectionMerge %36 None -;CHECK: OpBranchConditional %34 %37 %38 -;CHECK: %37 = OpLabel -;CHECK: %39 = OpLoad %10 %tBuf -;CHECK: %40 = OpSampledImage %18 %39 %17 -;CHECK: %41 = OpImage %10 %40 -;CHECK: %42 = OpImageFetch %v4float %41 %23 -;CHECK: OpBranch %36 -;CHECK: %38 = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_42 %uint_6 %uint_3 %uint_7 %uint_0 %30 %32 -;CHECK: OpBranch %36 -;CHECK: %36 = OpLabel -;CHECK: [[phi_result:%\w+]] = OpPhi %v4float %42 %37 [[null_v4float]] %38 -;CHECK: OpStore %x [[phi_result]] - OpReturn - OpFunctionEnd - )" + kStreamWrite7Frag; +OpCapability Shader +OpCapability SampledBuffer +;CHECK: OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %x %tBuf %s %ii +;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %x "x" +OpName %tBuf "tBuf" +OpName %s "s" +OpName %ii "ii" +OpDecorate %x Location 11 +OpDecorate %tBuf DescriptorSet 3 +OpDecorate %tBuf Binding 7 +OpDecorate %s DescriptorSet 3 +OpDecorate %s Binding 8 +OpDecorate %ii Flat +OpDecorate %ii Location 13 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%x = OpVariable %_ptr_Output_v4float Output +%10 = OpTypeImage %float Buffer 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 +%tBuf = OpVariable %_ptr_UniformConstant_10 UniformConstant +%14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%s = OpVariable %_ptr_UniformConstant_14 UniformConstant +%18 = OpTypeSampledImage %10 +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%ii = OpVariable %_ptr_Input_int Input +;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: [[null_v4float:%\w+]] = OpConstantNull %v4float +%main = OpFunction %void None %3 +%5 = OpLabel +%13 = OpLoad %10 %tBuf +%17 = OpLoad %14 %s +%19 = OpSampledImage %18 %13 %17 +%23 = OpLoad %int %ii +%24 = OpImage %10 %19 +%25 = OpImageFetch %v4float %24 %23 +OpStore %x %25 +;CHECK-NOT: %25 = OpImageFetch %v4float %24 %23 +;CHECK-NOT: OpStore %x %25 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_43 {{%\w+}} %uint_3 %uint_7 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %10 %tBuf +;CHECK: {{%\w+}} = OpSampledImage %18 {{%\w+}} %17 +;CHECK: {{%\w+}} = OpImage %10 {{%\w+}} +;CHECK: {{%\w+}} = OpImageFetch %v4float {{%\w+}} %23 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v4float {{%\w+}} {{%\w+}} [[null_v4float]] {{%\w+}} +;CHECK: OpStore %x [[phi_result]] +OpReturn +OpFunctionEnd +)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, - false, true, true, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } TEST_F(InstBindlessTest, DeviceBufferAddressOOB) { @@ -5509,11 +5049,13 @@ const std::string text = R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses +;CHECK: OpCapability Linkage ;CHECK: OpCapability Int64 +OpExtension "SPV_KHR_physical_storage_buffer" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Vertex %main "main" %u_info -;CHECK: OpEntryPoint Vertex %main "main" %u_info %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex +;CHECK: OpEntryPoint Vertex %main "main" %u_info %gl_VertexIndex %gl_InstanceIndex OpSource GLSL 450 OpSourceExtension "GL_EXT_buffer_reference" OpName %main "main" @@ -5531,9 +5073,8 @@ OpMemberDecorate %bufStruct 0 Offset 0 OpDecorate %bufStruct Block OpDecorate %u_info DescriptorSet 0 -OpDecorate %u_info Binding 0)" - + kInputDecorations + kOutputDecorations + -R"(%void = OpTypeVoid +OpDecorate %u_info Binding 0 +%void = OpTypeVoid %3 = OpTypeFunction %void %int = OpTypeInt 32 1 %_ptr_Function_int = OpTypePointer Function %int @@ -5552,18 +5093,11 @@ %bool = OpTypeBool %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct %int_n559035791 = OpConstant %int -559035791 -%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int)" - + kInputGlobals + kOutputGlobals + -R"(%main = OpFunction %void None %3 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +)" + kImportStub + R"( +%main = OpFunction %void None %3 %5 = OpLabel %i = OpVariable %_ptr_Function_int Function -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: [[desc_state:%\w+]] = OpFunctionCall %uint %inst_bindless_read_desc_init %uint_0 %uint_0 %uint_0 -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel -;CHECK: OpBranch {{%\w+}} -;CHECK: {{%\w+}} = OpLabel OpStore %i %int_0 OpBranch %10 %10 = OpLabel @@ -5572,37 +5106,41 @@ %14 = OpLabel %15 = OpLoad %int %i %26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1 -%27 = OpLoad %int %26 -%29 = OpSLessThan %bool %15 %27 -;CHECK-NOT: %27 = OpLoad %int %26 -;CHECK-NOT: %29 = OpSLessThan %bool %15 %27 ;CHECK: {{%\w+}} = OpIAdd %uint %uint_8 %uint_3 -;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_56 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[load_result:%\w+]] = OpLoad %int %26 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_54 %uint_4 %uint_0 %uint_0 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[phi_result:%\w+]] = OpPhi %int [[load_result]] {{%\w+}} {{%\w+}} {{%\w+}} -;CHECK: {{%\w+}} = OpSLessThan %bool %15 [[phi_result]] +%27 = OpLoad %int %26 +%29 = OpSLessThan %bool %15 %27 +;CHECK-NOT: %27 = OpLoad %int %26 +;CHECK-NOT: %29 = OpSLessThan %bool %15 %27 +;CHECK: %29 = OpSLessThan %bool %15 [[phi_result]] OpBranchConditional %29 %11 %12 %11 = OpLabel %31 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 %32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31 ;CHECK-NOT: %32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31 ;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 %uint_7 -;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_61 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}} ;CHECK: OpSelectionMerge {{%\w+}} None ;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpLabel ;CHECK: [[load_result_2:%\w+]] = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31 ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel -;CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_7 %uint_59 %uint_4 %uint_0 %uint_0 %uint_0 {{%\w+}} {{%\w+}} ;CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_bufStruct {{%\w+}} ;CHECK: OpBranch {{%\w+}} ;CHECK: {{%\w+}} = OpLabel @@ -5620,14 +5158,144 @@ OpBranch %10 %12 = OpLabel OpReturn -OpFunctionEnd)" - + kReadDescInit + kStreamWrite7Vert; +OpFunctionEnd)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false, true, - true, true, true); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); +} + +TEST_F(InstBindlessTest, VertexIndexOOB) { + // #version 450 + // layout(std140, binding = 0) uniform foo { uint tex_index[1]; } + // uniform_index_buffer; layout(location = 0) out flat uint index; vec2 + // vertices[3]; void main() { + // vertices[0] = vec2(-1.0, -1.0); + // vertices[1] = vec2( 1.0, -1.0); + // vertices[2] = vec2( 0.0, 1.0); + // gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0); + // index = uniform_index_buffer.tex_index[0]; + // } + // clang-format off + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %vertices %_ %gl_VertexIndex %index %uniform_index_buffer +OpSource GLSL 450 +OpName %main "main" +OpName %vertices "vertices" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %gl_VertexIndex "gl_VertexIndex" +OpName %index "index" +OpName %foo "foo" +OpMemberName %foo 0 "tex_index" +OpName %uniform_index_buffer "uniform_index_buffer" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpDecorate %gl_VertexIndex BuiltIn VertexIndex +OpDecorate %index Flat +OpDecorate %index Location 0 +OpDecorate %_arr_uint_uint_1 ArrayStride 16 +OpMemberDecorate %foo 0 Offset 0 +OpDecorate %foo Block +OpDecorate %uniform_index_buffer DescriptorSet 0 +OpDecorate %uniform_index_buffer Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3 +%_ptr_Private__arr_v2float_uint_3 = OpTypePointer Private %_arr_v2float_uint_3 +%vertices = OpVariable %_ptr_Private__arr_v2float_uint_3 Private +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float_n1 = OpConstant %float -1 +%16 = OpConstantComposite %v2float %float_n1 %float_n1 +%_ptr_Private_v2float = OpTypePointer Private %v2float +%int_1 = OpConstant %int 1 +%float_1 = OpConstant %float 1 +%21 = OpConstantComposite %v2float %float_1 %float_n1 +%int_2 = OpConstant %int 2 +%float_0 = OpConstant %float 0 +%25 = OpConstantComposite %v2float %float_0 %float_1 +%v4float = OpTypeVector %float 4 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input +%int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Output_uint = OpTypePointer Output %uint +%index = OpVariable %_ptr_Output_uint Output +%_arr_uint_uint_1 = OpTypeArray %uint %uint_1 +%foo = OpTypeStruct %_arr_uint_uint_1 +%_ptr_Uniform_foo = OpTypePointer Uniform %foo +%uniform_index_buffer = OpVariable %_ptr_Uniform_foo Uniform +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +)" + kImportStub + R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%18 = OpAccessChain %_ptr_Private_v2float %vertices %int_0 +OpStore %18 %16 +%22 = OpAccessChain %_ptr_Private_v2float %vertices %int_1 +OpStore %22 %21 +%26 = OpAccessChain %_ptr_Private_v2float %vertices %int_2 +OpStore %26 %25 +%35 = OpLoad %int %gl_VertexIndex +%37 = OpSMod %int %35 %int_3 +%38 = OpAccessChain %_ptr_Private_v2float %vertices %37 +%39 = OpLoad %v2float %38 +%40 = OpCompositeExtract %float %39 0 +%41 = OpCompositeExtract %float %39 1 +%42 = OpCompositeConstruct %v4float %40 %41 %float_0 %float_1 +%44 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %44 %42 +%52 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 %int_0 +%53 = OpLoad %uint %52 +;CHECK-NOT: %53 = OpLoad %uint %52 +;CHECK: {{%\w+}} = OpIMul %uint %uint_16 %int_0 +;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 {{%\w+}} +;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 +;CHECK: {{%\w+}} = OpLoad %int %gl_VertexIndex +;CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_87 {{%\w+}} %uint_0 %uint_0 %uint_0 {{%\w+}} +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %uint %52 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %index [[phi_result]] +OpStore %index %53 +;CHECK-NOT: OpStore %index %53 +OpReturn +;CHECK: OpReturn +OpFunctionEnd)"; + // clang-format on + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 23u); } // TODO(greg-lunarg): Add tests to verify handling of these cases:
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp index 80e6b7a..72d3438 100644 --- a/test/opt/inst_buff_addr_check_test.cpp +++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -26,161 +26,14 @@ namespace opt { namespace { -static const std::string kOutputDecorations = R"( -; CHECK: OpDecorate [[output_buffer_type:%inst_buff_addr_OutputBuffer]] Block -; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0 -; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4 -; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7 -; CHECK: OpDecorate [[output_buffer_var]] Binding 0 +static const std::string kFuncName = "inst_buff_addr_search_and_test"; +static const std::string kImportDeco = R"( +;CHECK: OpDecorate %)" + kFuncName + R"( LinkageAttributes ")" + + kFuncName + R"(" Import )"; - -static const std::string kOutputGlobals = R"( -; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %uint %_runtimearr_uint -; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]] -; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer -)"; - -static const std::string kStreamWrite4Begin = R"( -; CHECK: {{%\w+}} = OpFunction %void None {{%\w+}} -; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 -; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 -; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2 -; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} -; CHECK: OpSelectionMerge {{%\w+}} None -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_10 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_23 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_1]] -)"; - -static const std::string kStreamWrite4End = R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_2]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_3]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_4]] -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpReturn -; CHECK: OpFunctionEnd -)"; - -// clang-format off -static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord -; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite4End; - -static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite4End; -// clang-format on - -// clang-format off -static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"( -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -)" + kStreamWrite4End; -// clang-format on - -static const std::string kInputDecorations = R"( -; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block -; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0 -; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7 -; CHECK: OpDecorate [[input_buffer_var]] Binding 2 -)"; - -static const std::string kInputGlobals = R"( -; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_ulong -; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]] -; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer -)"; - -static const std::string kSearchAndTest = R"( -; CHECK: {{%\w+}} = OpFunction %bool None {{%\w+}} -; CHECK: [[param_1:%\w+]] = OpFunctionParameter %ulong -; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint -; CHECK: {{%\w+}} = OpLabel -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpPhi %uint %uint_1 {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: OpLoopMerge {{%\w+}} {{%\w+}} None -; CHECK: OpBranch {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} -; CHECK: {{%\w+}} = OpUGreaterThan %bool {{%\w+}} [[param_1]] -; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} -; CHECK: {{%\w+}} = OpISub %ulong [[param_1]] {{%\w+}} -; CHECK: {{%\w+}} = OpUConvert %ulong [[param_2]] -; CHECK: {{%\w+}} = OpIAdd %ulong {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 %uint_0 -; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} -; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}} -; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} -; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} -; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} -; CHECK: OpReturnValue {{%\w+}} -; CHECK: OpFunctionEnd +static const std::string kImportStub = R"( +;CHECK: %)" + kFuncName + R"( = OpFunction %bool None {{%\w+}} +;CHECK: OpFunctionEnd )"; // clang-format on @@ -209,13 +62,13 @@ const std::string defs = R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses -; CHECK: OpCapability Int64 +;CHECK: OpCapability Int64 OpExtension "SPV_EXT_physical_storage_buffer" -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %main "main" -; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID OpExecutionMode %main LocalSize 1 1 1 OpSource GLSL 450 OpSourceExtension "GL_EXT_buffer_reference" @@ -240,11 +93,8 @@ OpDecorate %bufStruct Block OpDecorate %u_info DescriptorSet 0 OpDecorate %u_info Binding 0 -; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 -)" + kInputDecorations + R"( -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId )"; const std::string globals = R"( @@ -265,19 +115,11 @@ %int_1 = OpConstant %int 1 %int_3239 = OpConstant %int 3239 %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int -; CHECK: %ulong = OpTypeInt 64 0 -; CHECK: %bool = OpTypeBool -; CHECK: %28 = OpTypeFunction %bool %ulong %uint -; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong -)" + kInputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong -; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint -; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint -)" + kOutputGlobals + R"( -; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -; CHECK: %v3uint = OpTypeVector %uint 3 -; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint -; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +;CHECK: %ulong = OpTypeInt 64 0 +;CHECK: %bool = OpTypeBool +;CHECK: %v3uint = OpTypeVector %uint 3 +;CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint +;CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input )"; // clang-format off @@ -287,36 +129,35 @@ %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 -; CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 -; CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 -; CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 -; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 -; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20 -; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1 -; CHECK: %24 = OpConvertPtrToU %ulong %22 -; CHECK: %61 = OpFunctionCall %bool %inst_buff_addr_search_and_test %24 %uint_4 -; CHECK: OpSelectionMerge %62 None -; CHECK: OpBranchConditional %61 %63 %64 -; CHECK: %63 = OpLabel +;CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +;CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 +;CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 +;CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +;CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20 +;CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1 +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %22 +;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} {{%\w+}} %uint_4 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpStore %22 %int_3239 Aligned 16 -; CHECK: OpStore %22 %int_3239 Aligned 16 -; CHECK: OpBranch %62 -; CHECK: %64 = OpLabel -; CHECK: %65 = OpUConvert %uint %24 -; CHECK: %67 = OpShiftRightLogical %ulong %24 %uint_32 -; CHECK: %68 = OpUConvert %uint %67 -; CHECK: %124 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_48 %uint_2 %65 %68 -; CHECK: OpBranch %62 -; CHECK: %62 = OpLabel +;CHECK: OpStore %22 %int_3239 Aligned 16 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute; - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch<InstBuffAddrCheckPass>( - defs + decorates + globals + main_func + output_funcs, true, 7u, 23u); + defs + decorates + globals + kImportStub + main_func, true, 23u); } TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { @@ -346,7 +187,7 @@ const std::string defs = R"( OpCapability Shader OpCapability PhysicalStorageBufferAddresses -; CHECK: OpCapability Int64 +;CHECK: OpCapability Int64 OpExtension "SPV_EXT_physical_storage_buffer" OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" @@ -356,7 +197,7 @@ OpSource GLSL 450 OpSourceExtension "GL_EXT_buffer_reference" OpName %main "main" -; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID OpName %blockType "blockType" OpMemberName %blockType 0 "x" OpMemberName %blockType 1 "next" @@ -374,13 +215,9 @@ OpDecorate %rootBlock Block OpDecorate %r DescriptorSet 0 OpDecorate %r Binding 0 -; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 -)" + kInputDecorations + R"( -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId )"; - // clang-format on const std::string globals = R"( %void = OpTypeVoid @@ -398,7 +235,7 @@ %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType %int_531 = OpConstant %int 531 %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int -)" + kInputGlobals + kOutputGlobals; +)"; const std::string main_func = R"( %main = OpFunction %void None %3 @@ -409,48 +246,49 @@ %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 OpStore %26 %int_531 Aligned 16 -; CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 -; CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 -; CHECK: %30 = OpConvertPtrToU %ulong %21 -; CHECK: %67 = OpFunctionCall %bool %inst_buff_addr_search_and_test %30 %uint_8 -; CHECK: OpSelectionMerge %68 None -; CHECK: OpBranchConditional %67 %69 %70 -; CHECK: %69 = OpLabel -; CHECK: %71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 -; CHECK: OpBranch %68 -; CHECK: %70 = OpLabel -; CHECK: %72 = OpUConvert %uint %30 -; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32 -; CHECK: %75 = OpUConvert %uint %74 -; CHECK: %131 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_44 %uint_2 %72 %75 -; CHECK: %133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132 -; CHECK: OpBranch %68 -; CHECK: %68 = OpLabel -; CHECK: %134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70 -; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0 -; CHECK: %135 = OpConvertPtrToU %ulong %26 -; CHECK: %136 = OpFunctionCall %bool %inst_buff_addr_search_and_test %135 %uint_4 -; CHECK: OpSelectionMerge %137 None -; CHECK: OpBranchConditional %136 %138 %139 -; CHECK: %138 = OpLabel -; CHECK: OpStore %26 %int_531 Aligned 16 -; CHECK: OpBranch %137 -; CHECK: %139 = OpLabel -; CHECK: %140 = OpUConvert %uint %135 -; CHECK: %141 = OpShiftRightLogical %ulong %135 %uint_32 -; CHECK: %142 = OpUConvert %uint %141 -; CHECK: %144 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_46 %uint_2 %140 %142 -; CHECK: OpBranch %137 -; CHECK: %137 = OpLabel +;CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +;CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %21 +;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_45 {{%\w+}} {{%\w+}} %uint_8 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %52 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpPhi %_ptr_PhysicalStorageBuffer_blockType {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int {{%\w+}} %int_0 +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %26 +;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_47 {{%\w+}} {{%\w+}} %uint_4 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %26 %int_531 Aligned 16 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpReturn OpFunctionEnd )"; - - const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute; + // clang-format on SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch<InstBuffAddrCheckPass>( - defs + decorates + globals + main_func + output_funcs, true, 7u, 23u); + defs + decorates + globals + kImportStub + main_func, true, 23u); } TEST_F(InstBuffAddrTest, StructLoad) { @@ -477,11 +315,11 @@ OpCapability Shader OpCapability Int64 OpCapability PhysicalStorageBufferAddresses -; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" -; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_FragCoord +;CHECK: OpEntryPoint Fragment %main "main" %gl_FragCoord OpExecutionMode %main OriginUpperLeft OpSource GLSL 450 OpSourceExtension "GL_ARB_gpu_shader_int64" @@ -500,11 +338,8 @@ OpMemberDecorate %Test_0 0 Offset 0 OpMemberDecorate %TestBuffer 0 Offset 0 OpDecorate %TestBuffer Block -; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 -)" + kInputDecorations + R"( -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord )"; const std::string globals = R"( @@ -521,50 +356,176 @@ %int_0 = OpConstant %int 0 %_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0 %ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704 -; CHECK: %47 = OpTypeFunction %bool %ulong %uint -)" + kInputGlobals + R"( -; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint -)" + kOutputGlobals + R"( -; CHECK: %143 = OpConstantNull %Test_0 +;CHECK: {{%\w+}} = OpConstantNull %Test_0 )"; - // clang-format on - const std::string main_func = - R"( + const std::string main_func = R"( %main = OpFunction %void None %3 %5 = OpLabel %37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704 %38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0 %39 = OpLoad %Test_0 %38 Aligned 16 -; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16 -; CHECK: %43 = OpConvertPtrToU %ulong %38 -; CHECK: %80 = OpFunctionCall %bool %inst_buff_addr_search_and_test %43 %uint_4 -; CHECK: OpSelectionMerge %81 None -; CHECK: OpBranchConditional %80 %82 %83 -; CHECK: %82 = OpLabel -; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16 -; CHECK: OpBranch %81 -; CHECK: %83 = OpLabel -; CHECK: %85 = OpUConvert %uint %43 -; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32 -; CHECK: %88 = OpUConvert %uint %87 -; CHECK: %142 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_37 %uint_2 %85 %88 -; CHECK: OpBranch %81 -; CHECK: %81 = OpLabel -; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83 +;CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16 +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %38 +;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord +;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_38 {{%\w+}} {{%\w+}} %uint_4 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %Test_0 %38 Aligned 16 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} %40 = OpCopyLogical %Test %39 -; CHECK-NOT: %40 = OpCopyLogical %Test %39 -; CHECK: %40 = OpCopyLogical %Test %144 +;CHECK-NOT: %40 = OpCopyLogical %Test %39 +;CHECK: %40 = OpCopyLogical %Test [[phi_result]] OpReturn OpFunctionEnd )"; + // clang-format on - const std::string output_funcs = kSearchAndTest + kStreamWrite4Frag; - - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch<InstBuffAddrCheckPass>( - defs + decorates + globals + main_func + output_funcs, true); + defs + decorates + globals + kImportStub + main_func, true); +} + +TEST_F(InstBuffAddrTest, PaddedStructLoad) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // #extension GL_ARB_gpu_shader_int64 : enable + // struct Test { + // uvec3 pad_1; // Offset 0 Size 12 + // double pad_2; // Offset 16 Size 8 (alignment requirement) + // float a; // Offset 24 Size 4 + // }; // Total Size 28 + // + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer + // TestBuffer { Test test; }; + // + // Test GetTest(uint64_t ptr) { + // return TestBuffer(ptr).test; + // } + // + // void main() { + // GetTest(0xe0000000); + // } + + const std::string defs = + R"( +OpCapability Shader +OpCapability Float64 +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpSourceExtension "GL_ARB_gpu_shader_int64" +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %Test "Test" +OpMemberName %Test 0 "pad_1" +OpMemberName %Test 1 "pad_2" +OpMemberName %Test 2 "a" +OpName %GetTest_u641_ "GetTest(u641;" +OpName %ptr "ptr" +OpName %Test_0 "Test" +OpMemberName %Test_0 0 "pad_1" +OpMemberName %Test_0 1 "pad_2" +OpMemberName %Test_0 2 "a" +OpName %TestBuffer "TestBuffer" +OpMemberName %TestBuffer 0 "test" +OpName %param "param" +)"; + + // clang-format off + const std::string decorates = R"( +OpDecorate %TestBuffer Block +OpMemberDecorate %Test_0 0 Offset 0 +OpMemberDecorate %Test_0 1 Offset 16 +OpMemberDecorate %Test_0 2 Offset 24 +OpMemberDecorate %TestBuffer 0 Offset 0 +)" + kImportDeco + R"( +;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex +;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex +)"; + + const std::string globals = R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%uint = OpTypeInt 32 0 +%v3uint = OpTypeVector %uint 3 +%double = OpTypeFloat 64 +%float = OpTypeFloat 32 +%Test = OpTypeStruct %v3uint %double %float +%13 = OpTypeFunction %Test %_ptr_Function_ulong +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer +%Test_0 = OpTypeStruct %v3uint %double %float +%TestBuffer = OpTypeStruct %Test_0 +%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0 +%_ptr_Function_Test = OpTypePointer Function %Test +%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704 +;CHECK: {{%\w+}} = OpConstantNull %Test_0 +)"; + + const std::string main_func = R"( +%main = OpFunction %void None %3 +%5 = OpLabel +%param = OpVariable %_ptr_Function_ulong Function +OpStore %param %ulong_18446744073172680704 +%35 = OpFunctionCall %Test %GetTest_u641_ %param +OpReturn +OpFunctionEnd +%GetTest_u641_ = OpFunction %Test None %13 +%ptr = OpFunctionParameter %_ptr_Function_ulong +%16 = OpLabel +%28 = OpVariable %_ptr_Function_Test Function +%17 = OpLoad %ulong %ptr +%21 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %17 +%25 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %21 %int_0 +%26 = OpLoad %Test_0 %25 Aligned 16 +%29 = OpCopyLogical %Test %26 +;CHECK-NOT: %30 = OpLoad %Test %28 +;CHECK-NOT: %26 = OpLoad %Test_0 %25 Aligned 16 +;CHECK-NOT: %29 = OpCopyLogical %Test %26 +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %25 +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_28 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %Test_0 %25 Aligned 16 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: %29 = OpCopyLogical %Test [[phi_result]] +OpStore %28 %29 +%30 = OpLoad %Test %28 +OpReturnValue %30 +OpFunctionEnd +)"; + // clang-format on + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<InstBuffAddrCheckPass>( + defs + decorates + globals + kImportStub + main_func, true); } TEST_F(InstBuffAddrTest, DeviceBufferAddressOOB) { @@ -591,7 +552,7 @@ %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Vertex %main "main" %u_info -;CHECK: OpEntryPoint Vertex %main "main" %u_info %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_VertexIndex %gl_InstanceIndex +;CHECK: OpEntryPoint Vertex %main "main" %u_info %gl_VertexIndex %gl_InstanceIndex OpSource GLSL 450 OpSourceExtension "GL_EXT_buffer_reference" OpName %main "main" @@ -609,7 +570,8 @@ OpMemberDecorate %bufStruct 0 Offset 0 OpDecorate %bufStruct Block OpDecorate %u_info DescriptorSet 0 -OpDecorate %u_info Binding 0)" + kInputDecorations + kOutputDecorations + R"( +OpDecorate %u_info Binding 0 +)" + kImportDeco + R"( %void = OpTypeVoid %3 = OpTypeFunction %void %int = OpTypeInt 32 1 @@ -629,7 +591,8 @@ %bool = OpTypeBool %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct %int_n559035791 = OpConstant %int -559035791 -%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int)" + kInputGlobals + kOutputGlobals + R"( +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +)" + kImportStub + R"( %main = OpFunction %void None %3 %5 = OpLabel %i = OpVariable %_ptr_Function_int Function @@ -649,20 +612,20 @@ %32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31 %33 = OpLoad %int %i %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33 -;CHECK: %41 = OpConvertPtrToU %ulong %36 -;CHECK: %76 = OpFunctionCall %bool %inst_buff_addr_search_and_test %41 %uint_4 -;CHECK: OpSelectionMerge %77 None -;CHECK: OpBranchConditional %76 %78 %79 -;CHECK: %78 = OpLabel OpStore %36 %int_n559035791 Aligned 16 -;CHECK: OpBranch %77 -;CHECK: 79 = OpLabel -;CHECK: 80 = OpUConvert %uint %41 -;CHECK: 82 = OpShiftRightLogical %ulong %41 %uint_32 -;CHECK: 83 = OpUConvert %uint %82 -;CHECK: 134 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_62 %uint_2 %80 %83 -;CHECK: OpBranch %77 -;CHECK: 77 = OpLabel +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %36 +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_4 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpStore %36 %int_n559035791 Aligned 16 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel OpBranch %13 %13 = OpLabel %37 = OpLoad %int %i @@ -671,12 +634,137 @@ OpBranch %10 %12 = OpLabel OpReturn -OpFunctionEnd)" + kSearchAndTest + kStreamWrite4Vert; +OpFunctionEnd)"; // clang-format on - SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 7, 23); + SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23); +} + +TEST_F(InstBuffAddrTest, UVec3ScalarAddressOOB) { + // clang-format off + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // #extension GL_EXT_scalar_block_layout : enable + // layout(buffer_reference, std430, scalar) readonly buffer IndexBuffer + // { + // uvec3 indices[]; + // }; + // layout(set = 0, binding = 0) uniform ufoo { + // IndexBuffer data; + // int nReads; + // } u_info; + // void main() { + // uvec3 readvec; + // for (int i=0; i < u_info.nReads; ++i) { + // readvec = u_info.data.indices[i]; + // } + // } + const std::string text = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Vertex %main "main" %u_info +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpSourceExtension "GL_EXT_scalar_block_layout" +OpName %main "main" +OpName %i "i" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "nReads" +OpName %IndexBuffer "IndexBuffer" +OpMemberName %IndexBuffer 0 "indices" +OpName %u_info "u_info" +OpName %readvec "readvec" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_runtimearr_v3uint ArrayStride 12 +OpMemberDecorate %IndexBuffer 0 NonWritable +OpMemberDecorate %IndexBuffer 0 Offset 0 +OpDecorate %IndexBuffer Block +OpDecorate %u_info DescriptorSet 0 +OpDecorate %u_info Binding 0 +)" + kImportDeco + R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_IndexBuffer PhysicalStorageBuffer +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_IndexBuffer %int +%uint = OpTypeInt 32 0 +%v3uint = OpTypeVector %uint 3 +%_runtimearr_v3uint = OpTypeRuntimeArray %v3uint +%IndexBuffer = OpTypeStruct %_runtimearr_v3uint +%_ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer PhysicalStorageBuffer %IndexBuffer +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_1 = OpConstant %int 1 +%_ptr_Uniform_int = OpTypePointer Uniform %int +%bool = OpTypeBool +)" + kImportStub + R"( +%_ptr_Function_v3uint = OpTypePointer Function %v3uint +%_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_IndexBuffer +%_ptr_PhysicalStorageBuffer_v3uint = OpTypePointer PhysicalStorageBuffer %v3uint +%main = OpFunction %void None %3 +%5 = OpLabel +%i = OpVariable %_ptr_Function_int Function +%readvec = OpVariable %_ptr_Function_v3uint Function +OpStore %i %int_0 +OpBranch %10 +%10 = OpLabel +OpLoopMerge %12 %13 None +OpBranch %14 +%14 = OpLabel +%15 = OpLoad %int %i +%26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1 +%27 = OpLoad %int %26 +%29 = OpSLessThan %bool %15 %27 +OpBranchConditional %29 %11 %12 +%11 = OpLabel +%33 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer %u_info %int_0 +%34 = OpLoad %_ptr_PhysicalStorageBuffer_IndexBuffer %33 +%35 = OpLoad %int %i +%37 = OpAccessChain %_ptr_PhysicalStorageBuffer_v3uint %34 %int_0 %35 +%38 = OpLoad %v3uint %37 Aligned 4 +OpStore %readvec %38 +;CHECK-NOT: %38 = OpLoad %v3uint %37 Aligned 4 +;CHECK-NOT: OpStore %readvec %38 +;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %37 +;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex +;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex +;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0 +;CHECK: [[test_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_67 {{%\w+}} {{%\w+}} %uint_12 +;CHECK: OpSelectionMerge {{%\w+}} None +;CHECK: OpBranchConditional [[test_result]] {{%\w+}} {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: {{%\w+}} = OpLoad %v3uint %37 Aligned 4 +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: OpBranch {{%\w+}} +;CHECK: {{%\w+}} = OpLabel +;CHECK: [[phi_result:%\w+]] = OpPhi %v3uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +;CHECK: OpStore %readvec [[phi_result]] +OpBranch %13 +%13 = OpLabel +%39 = OpLoad %int %i +%40 = OpIAdd %int %39 %int_1 +OpStore %i %40 +OpBranch %10 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ValidatorOptions()->scalar_block_layout = true; + SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23); } } // namespace
diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp index 7dd10d8..24c0bc6 100644 --- a/test/opt/inst_debug_printf_test.cpp +++ b/test/opt/inst_debug_printf_test.cpp
@@ -74,7 +74,7 @@ ; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "MainPs" %3 %4 -; CHECK: OpEntryPoint Fragment %2 "MainPs" %3 %4 %gl_FragCoord +; CHECK: OpEntryPoint Fragment %2 "MainPs" %3 %4 OpExecutionMode %2 OriginUpperLeft %5 = OpString "Color is %vn" )"; @@ -87,10 +87,7 @@ OpDecorate %7 Binding 0 OpDecorate %3 Location 0 OpDecorate %4 Location 0 -; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 -)" + kOutputDecorations + R"( -; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord -)"; +)" + kOutputDecorations; const std::string globals = R"(%void = OpTypeVoid @@ -110,14 +107,11 @@ %_ptr_Output_v4float = OpTypePointer Output %v4float %4 = OpVariable %_ptr_Output_v4float Output ; CHECK: %uint = OpTypeInt 32 0 -; CHECK: [[func_type:%\w+]] = OpTypeFunction %void %uint %uint %uint %uint %uint %uint +; CHECK: [[func_type:%\w+]] = OpTypeFunction %void %uint %uint %uint %uint %uint %uint %uint ; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint )" + kOutputGlobals + R"( ; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ; CHECK: %bool = OpTypeBool -; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float -; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input -; CHECK: %v4uint = OpTypeVector %uint 4 )"; // clang-format on @@ -131,76 +125,64 @@ %25 = OpImageSampleImplicitLod %v4float %24 %21 %26 = OpExtInst %void %1 1 %5 %25 ; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25 -; CHECK: %29 = OpCompositeExtract %float %25 0 -; CHECK: %30 = OpBitcast %uint %29 -; CHECK: %31 = OpCompositeExtract %float %25 1 -; CHECK: %32 = OpBitcast %uint %31 -; CHECK: %33 = OpCompositeExtract %float %25 2 -; CHECK: %34 = OpBitcast %uint %33 -; CHECK: %35 = OpCompositeExtract %float %25 3 -; CHECK: %36 = OpBitcast %uint %35 -; CHECK: %101 = OpFunctionCall %void %inst_printf_stream_write_6 %uint_36 %uint_5 %30 %32 %34 %36 -; CHECK: OpBranch %102 -; CHECK: %102 = OpLabel +; CHECK: {{%\w+}} = OpCompositeExtract %float %25 0 +; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %float %25 1 +; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %float %25 2 +; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +; CHECK: {{%\w+}} = OpCompositeExtract %float %25 3 +; CHECK: {{%\w+}} = OpBitcast %uint {{%\w+}} +; CHECK: {{%\w+}} = OpFunctionCall %void %inst_printf_stream_write_5 %uint_23 %uint_36 %uint_5 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} +; CHECK: OpBranch {{%\w+}} +; CHECK: {{%\w+}} = OpLabel OpStore %4 %25 OpReturn OpFunctionEnd )"; const std::string output_func = R"( -; CHECK: %inst_printf_stream_write_6 = OpFunction %void None [[func_type]] -; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint -; CHECK: [[param_6:%\w+]] = OpFunctionParameter %uint +; CHECK: %inst_printf_stream_write_5 = OpFunction %void None {{%\w+}} +; CHECK: [[sw_shader_id:%\w+]] = OpFunctionParameter %uint +; CHECK: [[sw_inst_idx:%\w+]] = OpFunctionParameter %uint +; CHECK: [[sw_param_1:%\w+]] = OpFunctionParameter %uint +; CHECK: [[sw_param_2:%\w+]] = OpFunctionParameter %uint +; CHECK: [[sw_param_3:%\w+]] = OpFunctionParameter %uint +; CHECK: [[sw_param_4:%\w+]] = OpFunctionParameter %uint +; CHECK: [[sw_param_5:%\w+]] = OpFunctionParameter %uint ; CHECK: {{%\w+}} = OpLabel -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 -; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_12 -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_12 -; CHECK: {{%\w+}} = OpArrayLength %uint %inst_printf_output_buffer 2 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 +; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_8 +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 +; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2 ; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} ; CHECK: OpSelectionMerge {{%\w+}} None ; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} ; CHECK: {{%\w+}} = OpLabel ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_12 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} %uint_8 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_23 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_shader_id]] ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_1]] +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_inst_idx]] ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord -; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_param_1]] ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} -; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_param_2]] ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} {{%\w+}} +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_param_3]] +; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_param_4]] ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_2]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_3]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_4]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_5]] -; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11 -; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_2 {{%\w+}} -; CHECK: OpStore {{%\w+}} [[param_6]] +; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} +; CHECK: OpStore {{%\w+}} [[sw_param_5]] ; CHECK: OpBranch {{%\w+}} ; CHECK: {{%\w+}} = OpLabel ; CHECK: OpReturn
diff --git a/test/opt/invocation_interlock_placement_test.cpp b/test/opt/invocation_interlock_placement_test.cpp new file mode 100644 index 0000000..2c4ff65 --- /dev/null +++ b/test/opt/invocation_interlock_placement_test.cpp
@@ -0,0 +1,613 @@ +// Copyright (c) 2023 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 "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InterlockInvocationPlacementTest = PassTest<::testing::Test>; + +TEST_F(InterlockInvocationPlacementTest, CheckUnchangedIfNotFragment) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + %2 = OpLabel + OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + EXPECT_EQ( + Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false, /* do_validation= */ false))); +} + +TEST_F(InterlockInvocationPlacementTest, CheckUnchangedWithoutCapability) { + const std::string kTest = R"( + OpCapability Shader + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + %2 = OpLabel + OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + EXPECT_EQ( + Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false, /* do_validation= */ false))); +} + +TEST_F(InterlockInvocationPlacementTest, CheckSingleBasicBlock) { + // We're using OpNoLine as a generic standin for any other instruction, to + // test that begin and end aren't moved. + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 +; CHECK: OpLabel + %2 = OpLabel +; CHECK-NEXT: OpNoLine + OpNoLine +; CHECK-NEXT: OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpNoLine + OpNoLine +; CHECK-NEXT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpNoLine + OpNoLine +; CHECK-NEXT: OpReturn + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckFunctionCallExtractionBegin) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %foo = OpFunction %void None %1 +; CHECK: OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT + %2 = OpLabel + OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpReturn +; CHECK: OpFunctionEnd + OpFunctionEnd + %main = OpFunction %void None %1 +; CHECK: OpLabel + %3 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpFunctionCall + %4 = OpFunctionCall %void %foo +; CHECK-NEXT: OpReturn + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckFunctionCallExtractionEnd) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %foo = OpFunction %void None %1 +; CHECK: OpLabel +; CHECK-NOT: OpEndInvocationInterlockEXT + %2 = OpLabel + OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn +; CHECK: OpFunctionEnd + OpFunctionEnd + %main = OpFunction %void None %1 +; CHECK: OpLabel + %3 = OpLabel +; CHECK-NEXT: OpFunctionCall + %4 = OpFunctionCall %void %foo +; CHECK-NEXT: OpEndInvocationInterlockEXT +; CHECK-NEXT: OpReturn + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, + CheckFunctionCallExtractionRepeatedCall) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %foo = OpFunction %void None %1 +; CHECK: OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + %2 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn +; CHECK: OpFunctionEnd + OpFunctionEnd + %main = OpFunction %void None %1 +; CHECK: OpLabel + %3 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpFunctionCall + %4 = OpFunctionCall %void %foo +; CHECK-NEXT: OpFunctionCall + %5 = OpFunctionCall %void %foo +; CHECK-NEXT: OpEndInvocationInterlockEXT +; CHECK-NEXT: OpReturn + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, + CheckFunctionCallExtractionNestedCall) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %1 = OpTypeFunction %void + %foo = OpFunction %void None %1 +; CHECK: OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + %2 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn +; CHECK: OpFunctionEnd + OpFunctionEnd + %bar = OpFunction %void None %1 +; CHECK: OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + %3 = OpLabel + %4 = OpFunctionCall %void %foo + OpReturn +; CHECK: OpFunctionEnd + OpFunctionEnd + %main = OpFunction %void None %1 +; CHECK: OpLabel + %5 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpFunctionCall + %6 = OpFunctionCall %void %bar +; CHECK-NEXT: OpEndInvocationInterlockEXT +; CHECK-NEXT: OpReturn + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckLoopExtraction) { + // Tests that any begin or end instructions in a loop are moved outside of the + // loop. + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + + %2 = OpLabel +; CHECK: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpBranch %3 + + %3 = OpLabel + OpLoopMerge %3 %4 None +; CHECK: OpBranchConditional +; CHECK-NOT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpBranchConditional %true %4 %5 + + %4 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK: OpBranch + OpBranch %3 + +; CHECK-NEXT: OpLabel + %5 = OpLabel +; CHECK-NEXT: OpEndInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckAddBeginToElse) { + // Test that if there is a begin in a single branch of a conditional, begin + // will be added to the other branch. + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + + %2 = OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT + OpSelectionMerge %5 None +; CHECK: OpBranchConditional + OpBranchConditional %true %3 %4 + +; CHECK-NEXT: OpLabel + %3 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpBranch + OpBranch %5 + + %4 = OpLabel +; CHECK: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpBranch + OpBranch %5 + +; CHECK-NEXT: OpLabel + %5 = OpLabel + OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckAddEndToElse) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + + %2 = OpLabel +; CHECK: OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpSelectionMerge %5 None +; CHECK: OpBranchConditional + OpBranchConditional %true %3 %4 + +; CHECK-NEXT: OpLabel + %3 = OpLabel + OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpBranch + OpBranch %5 + + %4 = OpLabel +; CHECK: OpEndInvocationInterlockEXT +; CHECK-NEXT: OpBranch + OpBranch %5 + +; CHECK-NEXT: OpLabel + %5 = OpLabel +; CHECK-NOT: OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckSplitIfWithoutElseBegin) { + // Test that if there is a begin in the then branch of a conditional, and no + // else branch, an else branch with a begin will created. + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + + %2 = OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT + OpSelectionMerge %5 None +; CHECK: OpBranchConditional + OpBranchConditional %true %3 %5 + +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpBranch + +; CHECK-NEXT: OpLabel + %3 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBranch %5 + +; CHECK: OpLabel + %5 = OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckSplitIfWithoutElseEnd) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + + %2 = OpLabel + +; CHECK: OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpSelectionMerge [[merge:%\d+]] + OpSelectionMerge %5 None +; CHECK-NEXT: OpBranchConditional %true [[then:%\d+]] [[else:%\d+]] + OpBranchConditional %true %3 %5 + +; CHECK-NEXT: [[else]] = OpLabel +; CHECK-NEXT: OpEndInvocationInterlockEXT +; CHECK-NEXT: OpBranch [[merge]] + +; CHECK-NEXT: [[then]] = OpLabel + %3 = OpLabel +; CHECK-NEXT: OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpBranch [[merge]] + OpBranch %5 + +; CHECK-NEXT: [[merge]] = OpLabel + %5 = OpLabel +; CHECK-NEXT: OpReturn + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(InterlockInvocationPlacementTest, CheckSplitSwitch) { + // Test that if there is a begin or end in a single branch of a switch, begin + // or end will be added to all the other branches. + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderSampleInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main SampleInterlockOrderedEXT + OpName %main "main" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %1 = OpTypeFunction %void + %main = OpFunction %void None %1 + +; CHECK: OpLabel + %2 = OpLabel +; CHECK-NEXT: OpSelectionMerge [[merge:%\d+]] + OpSelectionMerge %8 None +; CHECK-NEXT: OpSwitch %uint_1 [[default:%\d+]] 0 [[case_0:%\d+]] 1 [[case_1:%\d+]] 2 [[case_2:%\d+]] + OpSwitch %uint_1 %8 0 %4 1 %5 2 %8 + +; CHECK-NEXT: [[case_2]] = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpBranch [[merge]] + +; CHECK-NEXT: [[default]] = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpBranch [[merge]] + +; CHECK-NEXT: [[case_0]] = OpLabel + %4 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpNoLine + OpNoLine +; CHECK-NEXT: OpBranch [[merge]] + OpBranch %8 + +; CHECK-NEXT: [[case_1]] = OpLabel + %5 = OpLabel +; CHECK-NEXT: OpBeginInvocationInterlockEXT +; CHECK-NOT: OpEndInvocationInterlockEXT + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT +; CHECK-NEXT: OpNoLine + OpNoLine +; CHECK-NEXT: OpNoLine + OpNoLine +; CHECK-NEXT: OpBranch [[merge]] + OpBranch %8 + +; CHECK-NEXT: [[merge]] = OpLabel + %8 = OpLabel +; CHECK-NOT: OpBeginInvocationInterlockEXT + OpBeginInvocationInterlockEXT +; CHECK-NEXT: OpEndInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>( + kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp index 1acbefe..621fe8c 100644 --- a/test/opt/ir_context_test.cpp +++ b/test/opt/ir_context_test.cpp
@@ -1173,6 +1173,254 @@ } } +TEST_F(IRContextTest, ReturnsTrueWhenExtensionIsRemoved) { + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 1); + + EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock)); + + EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 0); +} + +TEST_F(IRContextTest, ReturnsFalseWhenExtensionIsNotRemoved) { + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_device_group" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 1); + + EXPECT_FALSE(ctx->RemoveExtension(kSPV_KHR_shader_clock)); + + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 1); +} + +TEST_F(IRContextTest, RemovesExtensionIfLast) { + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_device_group" + OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group)); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 2); + + EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock)); + + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group)); + EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 1); +} + +TEST_F(IRContextTest, RemovesExtensionIfFirst) { + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_shader_clock" + OpExtension "SPV_KHR_device_group" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group)); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 2); + + EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock)); + + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_device_group)); + EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 1); +} + +TEST_F(IRContextTest, RemovesMultipleExtensions) { + const std::string text = R"( + OpCapability Shader + OpExtension "SPV_KHR_shader_clock" + OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 2); + + EXPECT_TRUE(ctx->RemoveExtension(kSPV_KHR_shader_clock)); + + EXPECT_FALSE(ctx->get_feature_mgr()->HasExtension(kSPV_KHR_shader_clock)); + EXPECT_EQ(std::distance(ctx->module()->extension_begin(), + ctx->module()->extension_end()), + 0); +} + +TEST_F(IRContextTest, ReturnsTrueWhenCapabilityIsRemoved) { + const std::string text = R"( + OpCapability Shader + OpCapability ShaderClockKHR + OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE( + ctx->get_feature_mgr()->HasCapability(spv::Capability::ShaderClockKHR)); + EXPECT_EQ(std::distance(ctx->module()->capability_begin(), + ctx->module()->capability_end()), + 2); + + EXPECT_TRUE(ctx->RemoveCapability(spv::Capability::ShaderClockKHR)); + + EXPECT_FALSE( + ctx->get_feature_mgr()->HasCapability(spv::Capability::ShaderClockKHR)); + EXPECT_EQ(std::distance(ctx->module()->capability_begin(), + ctx->module()->capability_end()), + 1); +} + +TEST_F(IRContextTest, ReturnsFalseWhenCapabilityIsNotRemoved) { + const std::string text = R"( + OpCapability Shader + OpCapability DeviceGroup + OpExtension "SPV_KHR_device_group" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE( + ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup)); + EXPECT_EQ(std::distance(ctx->module()->capability_begin(), + ctx->module()->capability_end()), + 2); + + EXPECT_FALSE(ctx->RemoveCapability(spv::Capability::ShaderClockKHR)); + + EXPECT_TRUE( + ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup)); + EXPECT_EQ(std::distance(ctx->module()->capability_begin(), + ctx->module()->capability_end()), + 2); +} + +TEST_F(IRContextTest, RemovesMultipleCapabilities) { + const std::string text = R"( + OpCapability Shader + OpCapability DeviceGroup + OpCapability DeviceGroup + OpExtension "SPV_KHR_device_group" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %9 = OpLabel + OpReturn + OpFunctionEnd)"; + + std::unique_ptr<IRContext> ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE( + ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup)); + EXPECT_EQ(std::distance(ctx->module()->capability_begin(), + ctx->module()->capability_end()), + 3); + + EXPECT_TRUE(ctx->RemoveCapability(spv::Capability::DeviceGroup)); + + EXPECT_FALSE( + ctx->get_feature_mgr()->HasCapability(spv::Capability::DeviceGroup)); + EXPECT_EQ(std::distance(ctx->module()->capability_begin(), + ctx->module()->capability_end()), + 1); +} + INSTANTIATE_TEST_SUITE_P( TestCase, TargetEnvCompareTest, ::testing::Values(
diff --git a/test/opt/modify_maximal_reconvergence_test.cpp b/test/opt/modify_maximal_reconvergence_test.cpp new file mode 100644 index 0000000..bef9237 --- /dev/null +++ b/test/opt/modify_maximal_reconvergence_test.cpp
@@ -0,0 +1,312 @@ +// Copyright (c) 2024 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 "assembly_builder.h" +#include "pass_fixture.h" +#include "pass_utils.h" + +namespace { + +using namespace spvtools; + +using ModifyMaximalReconvergenceTest = opt::PassTest<::testing::Test>; + +TEST_F(ModifyMaximalReconvergenceTest, AddNoEntryPoint) { + const std::string text = R"( +; CHECK-NOT: OpExtension +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, AddSingleEntryPoint) { + const std::string text = R"( +; CHECK: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %main MaximallyReconvergesKHR + +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, AddExtensionExists) { + const std::string text = R"( +; CHECK: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK-NOT: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %main MaximallyReconvergesKHR + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, AddExecutionModeExists) { + const std::string text = R"( +; CHECK: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK-NOT: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %main LocalSize 1 1 1 +; CHECK-NEXT: OpExecutionMode %main MaximallyReconvergesKHR +; CHECK-NOT: OpExecutionMode %main MaximallyReconvergesKHR + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +OpName %main "main" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, AddTwoEntryPoints) { + const std::string text = R"( +; CHECK: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %comp MaximallyReconvergesKHR +; CHECK: OpExecutionMode %frag MaximallyReconvergesKHR + +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %comp "main" +OpEntryPoint Fragment %frag "main" +OpExecutionMode %comp LocalSize 1 1 1 +OpExecutionMode %frag OriginUpperLeft +OpName %comp "comp" +OpName %frag "frag" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%comp = OpFunction %void None %void_fn +%entry1 = OpLabel +OpReturn +OpFunctionEnd +%frag = OpFunction %void None %void_fn +%entry2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, AddTwoEntryPointsOneFunc) { + const std::string text = R"( +; CHECK: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %comp MaximallyReconvergesKHR +; CHECK-NOT: OpExecutionMode %comp MaximallyReconvergesKHR + +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %comp "main1" +OpEntryPoint GLCompute %comp "main2" +OpExecutionMode %comp LocalSize 1 1 1 +OpName %comp "comp" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%comp = OpFunction %void None %void_fn +%entry1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, AddTwoEntryPointsOneExecutionMode) { + const std::string text = R"( +; CHECK: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %comp MaximallyReconvergesKHR +; CHECK-NOT: OpExecutionMode %comp MaximallyReconvergesKHR +; CHECK: OpExecutionMode %frag MaximallyReconvergesKHR +; CHECK-NOT: OpExecutionMode %comp MaximallyReconvergesKHR + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %comp "main" +OpEntryPoint Fragment %frag "main" +OpExecutionMode %comp LocalSize 1 1 1 +OpExecutionMode %frag OriginUpperLeft +OpExecutionMode %comp MaximallyReconvergesKHR +OpName %comp "comp" +OpName %frag "frag" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%comp = OpFunction %void None %void_fn +%entry1 = OpLabel +OpReturn +OpFunctionEnd +%frag = OpFunction %void None %void_fn +%entry2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, true); +} + +TEST_F(ModifyMaximalReconvergenceTest, RemoveNoEntryPoint) { + const std::string text = R"(OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +)"; + + SinglePassRunAndCheck<opt::ModifyMaximalReconvergence>(text, text, false, + true, false); +} + +TEST_F(ModifyMaximalReconvergenceTest, RemoveOnlyExtension) { + const std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %main LocalSize 1 1 1 + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, false); +} + +TEST_F(ModifyMaximalReconvergenceTest, RemoveSingleEntryPoint) { + const std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %main LocalSize 1 1 1 +; CHECK-NOT: OpExecutionMode %main MaximallyReconvergesKHR + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +OpName %main "main" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, false); +} + +TEST_F(ModifyMaximalReconvergenceTest, RemoveTwoEntryPointsOneExecutionMode) { + const std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %comp LocalSize 1 1 1 +; CHECK-NEXT: OpExecutionMode %frag OriginUpperLeft +; CHECK-NOT: OpExecutionMode %comp MaximallyReconvergesKHR + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %comp "main" +OpEntryPoint Fragment %frag "main" +OpExecutionMode %comp LocalSize 1 1 1 +OpExecutionMode %comp MaximallyReconvergesKHR +OpExecutionMode %frag OriginUpperLeft +OpName %comp "comp" +OpName %frag "frag" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%comp = OpFunction %void None %void_fn +%entry1 = OpLabel +OpReturn +OpFunctionEnd +%frag = OpFunction %void None %void_fn +%entry2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, false); +} + +TEST_F(ModifyMaximalReconvergenceTest, RemoveTwoEntryPoints) { + const std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_maximal_reconvergence" +; CHECK: OpExecutionMode %comp LocalSize 1 1 1 +; CHECK-NEXT: OpExecutionMode %frag OriginUpperLeft +; CHECK-NOT: OpExecutionMode {{%\w}} MaximallyReconvergesKHR + +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %comp "main" +OpEntryPoint Fragment %frag "main" +OpExecutionMode %comp LocalSize 1 1 1 +OpExecutionMode %comp MaximallyReconvergesKHR +OpExecutionMode %frag OriginUpperLeft +OpExecutionMode %frag MaximallyReconvergesKHR +OpName %comp "comp" +OpName %frag "frag" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%comp = OpFunction %void None %void_fn +%entry1 = OpLabel +OpReturn +OpFunctionEnd +%frag = OpFunction %void None %void_fn +%entry2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<opt::ModifyMaximalReconvergence>(text, true, false); +} + +} // namespace
diff --git a/test/opt/switch_descriptorset_test.cpp b/test/opt/switch_descriptorset_test.cpp new file mode 100644 index 0000000..f26178f --- /dev/null +++ b/test/opt/switch_descriptorset_test.cpp
@@ -0,0 +1,193 @@ +// Copyright (c) 2023 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. + +// Bindless Check Instrumentation Tests. +// Tests ending with V2 use version 2 record format. + +#include <string> +#include <vector> + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using SwitchDescriptorSetTest = PassTest<::testing::Test>; + +TEST_F(SwitchDescriptorSetTest, Basic) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // + // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct; + // + // layout(set = 7, binding = 7) uniform ufoo { + // bufStruct data; + // uint offset; + // } u_info; + // + // layout(buffer_reference, std140) buffer bufStruct { + // layout(offset = 0) int a[2]; + // layout(offset = 32) int b; + // }; + // + // void main() { + // u_info.data.b = 0xca7; + // } + + const std::string spirv = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_EXT_physical_storage_buffer" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "offset" +OpName %bufStruct "bufStruct" +OpMemberName %bufStruct 0 "a" +OpMemberName %bufStruct 1 "b" +OpName %u_info "u_info" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_arr_int_uint_2 ArrayStride 16 +OpMemberDecorate %bufStruct 0 Offset 0 +OpMemberDecorate %bufStruct 1 Offset 32 +OpDecorate %bufStruct Block +OpDecorate %u_info DescriptorSet 7 +;CHECK: OpDecorate %u_info DescriptorSet 31 +OpDecorate %u_info Binding 7 +;CHECK: OpDecorate %u_info Binding 7 +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer +%uint = OpTypeInt 32 0 +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint +%int = OpTypeInt 32 1 +%uint_2 = OpConstant %uint 2 +%_arr_int_uint_2 = OpTypeArray %int %uint_2 +%bufStruct = OpTypeStruct %_arr_int_uint_2 %int +%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct +%int_1 = OpConstant %int 1 +%int_3239 = OpConstant %int 3239 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +%main = OpFunction %void None %3 +%5 = OpLabel +%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 +%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 +OpReturn +OpFunctionEnd +)"; + // clang-format off + + SinglePassRunAndMatch<SwitchDescriptorSetPass>(spirv, true, 7, 31); +} + + +// Make sure DescriptorSet decorations that don't match the requested number +// are left unchanged. +TEST_F(SwitchDescriptorSetTest, Unchanged) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // + // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct; + // + // layout(set = 11, binding = 7) uniform ufoo { + // bufStruct data; + // uint offset; + // } u_info; + // + // layout(buffer_reference, std140) buffer bufStruct { + // layout(offset = 0) int a[2]; + // layout(offset = 32) int b; + // }; + // + // void main() { + // u_info.data.b = 0xca7; + // } + + const std::string spirv = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_EXT_physical_storage_buffer" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "offset" +OpName %bufStruct "bufStruct" +OpMemberName %bufStruct 0 "a" +OpMemberName %bufStruct 1 "b" +OpName %u_info "u_info" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_arr_int_uint_2 ArrayStride 16 +OpMemberDecorate %bufStruct 0 Offset 0 +OpMemberDecorate %bufStruct 1 Offset 32 +OpDecorate %bufStruct Block +OpDecorate %u_info DescriptorSet 11 +;CHECK: OpDecorate %u_info DescriptorSet 11 +OpDecorate %u_info Binding 7 +;CHECK: OpDecorate %u_info Binding 7 +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer +%uint = OpTypeInt 32 0 +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint +%int = OpTypeInt 32 1 +%uint_2 = OpConstant %uint 2 +%_arr_int_uint_2 = OpTypeArray %int %uint_2 +%bufStruct = OpTypeStruct %_arr_int_uint_2 %int +%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct +%int_1 = OpConstant %int 1 +%int_3239 = OpConstant %int 3239 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +%main = OpFunction %void None %3 +%5 = OpLabel +%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 +%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 +OpReturn +OpFunctionEnd +)"; + // clang-format off + + SinglePassRunAndMatch<SwitchDescriptorSetPass>(spirv, true, 7, 31); +} + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp new file mode 100644 index 0000000..d74ccdf --- /dev/null +++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -0,0 +1,2665 @@ +// Copyright (c) 2023 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 "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using TrimCapabilitiesPassTest = PassTest<::testing::Test>; + +TEST_F(TrimCapabilitiesPassTest, CheckKnownAliasTransformations) { + // Those are expected changes caused by the test process: + // - SPV is assembled. -> capability goes from text to number. + // - SPV is optimized. + // - SPV is disassembled -> capability goes from number to text. + // - CHECK rule compares both text versions. + // Because some capabilities share the same number (aliases), the text + // compared with the CHECK rules depends on which alias is the first on the + // SPIRV-Headers enum. This could change, and we want to easily distinguish + // real failure from alias order change. This test is only here to list known + // alias transformations. If this test breaks, it's not a bug in the + // optimization pass, but just the SPIRV-Headers enum order that has changed. + // If that happens, tests needs to be updated to the correct alias is used in + // the CHECK rule. + const std::string kTest = R"( + OpCapability Linkage + OpCapability StorageUniform16 + OpCapability StorageUniformBufferBlock16 + OpCapability ShaderViewportIndexLayerNV + OpCapability FragmentBarycentricNV + OpCapability ShadingRateNV + OpCapability ShaderNonUniformEXT + OpCapability RuntimeDescriptorArrayEXT + OpCapability InputAttachmentArrayDynamicIndexingEXT + OpCapability UniformTexelBufferArrayDynamicIndexingEXT + OpCapability StorageTexelBufferArrayDynamicIndexingEXT + OpCapability UniformBufferArrayNonUniformIndexingEXT + OpCapability SampledImageArrayNonUniformIndexingEXT + OpCapability StorageBufferArrayNonUniformIndexingEXT + OpCapability StorageImageArrayNonUniformIndexingEXT + OpCapability InputAttachmentArrayNonUniformIndexingEXT + OpCapability UniformTexelBufferArrayNonUniformIndexingEXT + OpCapability StorageTexelBufferArrayNonUniformIndexingEXT + OpCapability VulkanMemoryModelKHR + OpCapability VulkanMemoryModelDeviceScopeKHR + OpCapability PhysicalStorageBufferAddressesEXT + OpCapability DemoteToHelperInvocationEXT + OpCapability DotProductInputAllKHR + OpCapability DotProductInput4x8BitKHR + OpCapability DotProductInput4x8BitPackedKHR + OpCapability DotProductKHR + OpCapability ComputeDerivativeGroupQuadsNV + OpCapability ComputeDerivativeGroupLinearNV +; CHECK: OpCapability Linkage +; CHECK-NOT: OpCapability StorageUniform16 +; CHECK-NOT: OpCapability StorageUniformBufferBlock16 +; CHECK-NOT: OpCapability ShaderViewportIndexLayerNV +; CHECK-NOT: OpCapability FragmentBarycentricNV +; CHECK-NOT: OpCapability ShadingRateNV +; CHECK-NOT: OpCapability ShaderNonUniformEXT +; CHECK-NOT: OpCapability RuntimeDescriptorArrayEXT +; CHECK-NOT: OpCapability InputAttachmentArrayDynamicIndexingEXT +; CHECK-NOT: OpCapability UniformTexelBufferArrayDynamicIndexingEXT +; CHECK-NOT: OpCapability StorageTexelBufferArrayDynamicIndexingEXT +; CHECK-NOT: OpCapability UniformBufferArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability SampledImageArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability StorageBufferArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability StorageImageArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability InputAttachmentArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability UniformTexelBufferArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability StorageTexelBufferArrayNonUniformIndexingEXT +; CHECK-NOT: OpCapability VulkanMemoryModelKHR +; CHECK-NOT: OpCapability VulkanMemoryModelDeviceScopeKHR +; CHECK-NOT: OpCapability PhysicalStorageBufferAddressesEXT +; CHECK-NOT: OpCapability DemoteToHelperInvocationEXT +; CHECK-NOT: OpCapability DotProductInputAllKHR +; CHECK-NOT: OpCapability DotProductInput4x8BitKHR +; CHECK-NOT: OpCapability DotProductInput4x8BitPackedKHR +; CHECK-NOT: OpCapability DotProductKHR +; CHECK-NOT: OpCapability ComputeDerivativeGroupQuadsNV +; CHECK-NOT: OpCapability ComputeDerivativeGroupLinearNV +; CHECK: OpCapability UniformAndStorageBuffer16BitAccess +; CHECK: OpCapability StorageBuffer16BitAccess +; CHECK: OpCapability ShaderViewportIndexLayerEXT +; CHECK: OpCapability FragmentBarycentricKHR +; CHECK: OpCapability FragmentDensityEXT +; CHECK: OpCapability ShaderNonUniform +; CHECK: OpCapability RuntimeDescriptorArray +; CHECK: OpCapability InputAttachmentArrayDynamicIndexing +; CHECK: OpCapability UniformTexelBufferArrayDynamicIndexing +; CHECK: OpCapability StorageTexelBufferArrayDynamicIndexing +; CHECK: OpCapability UniformBufferArrayNonUniformIndexing +; CHECK: OpCapability SampledImageArrayNonUniformIndexing +; CHECK: OpCapability StorageBufferArrayNonUniformIndexing +; CHECK: OpCapability StorageImageArrayNonUniformIndexing +; CHECK: OpCapability InputAttachmentArrayNonUniformIndexing +; CHECK: OpCapability UniformTexelBufferArrayNonUniformIndexing +; CHECK: OpCapability StorageTexelBufferArrayNonUniformIndexing +; CHECK: OpCapability VulkanMemoryModel +; CHECK: OpCapability VulkanMemoryModelDeviceScope +; CHECK: OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability DemoteToHelperInvocation +; CHECK: OpCapability DotProductInputAll +; CHECK: OpCapability DotProductInput4x8Bit +; CHECK: OpCapability DotProductInput4x8BitPacked +; CHECK: OpCapability DotProduct + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = + SinglePassRunAndMatch<EmptyPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, LinkagePreventsChanges) { + const std::string kTest = R"( + OpCapability Linkage + OpCapability ClipDistance + OpCapability CullDistance + OpCapability DemoteToHelperInvocation + OpCapability DeviceGroup + OpCapability DrawParameters + OpCapability Float16 + OpCapability Float64 + OpCapability FragmentBarycentricKHR + OpCapability FragmentFullyCoveredEXT + OpCapability FragmentShadingRateKHR + OpCapability GroupNonUniform + OpCapability GroupNonUniformArithmetic + OpCapability GroupNonUniformBallot + OpCapability GroupNonUniformQuad + OpCapability GroupNonUniformShuffle + OpCapability Image1D + OpCapability ImageBuffer + OpCapability ImageGatherExtended + OpCapability ImageMSArray + OpCapability ImageQuery + OpCapability InputAttachment + OpCapability InputAttachmentArrayNonUniformIndexing + OpCapability Int16 + OpCapability Int64 + OpCapability Int64Atomics + OpCapability Int64ImageEXT + OpCapability MeshShadingNV + OpCapability MinLod + OpCapability MultiView + OpCapability MultiViewport + OpCapability PhysicalStorageBufferAddresses + OpCapability RayQueryKHR + OpCapability RayTracingKHR + OpCapability RayTracingNV + OpCapability RayTraversalPrimitiveCullingKHR + OpCapability RuntimeDescriptorArray + OpCapability SampleMaskPostDepthCoverage + OpCapability SampleRateShading + OpCapability Sampled1D + OpCapability SampledBuffer + OpCapability SampledImageArrayNonUniformIndexing + OpCapability Shader + OpCapability ShaderClockKHR + OpCapability ShaderLayer + OpCapability ShaderNonUniform + OpCapability ShaderViewportIndex + OpCapability ShaderViewportIndexLayerEXT + OpCapability SparseResidency + OpCapability StencilExportEXT + OpCapability StorageImageArrayNonUniformIndexingEXT + OpCapability StorageImageExtendedFormats + OpCapability StorageImageReadWithoutFormat + OpCapability StorageImageWriteWithoutFormat + OpCapability StorageInputOutput16 + OpCapability StoragePushConstant16 + OpCapability StorageTexelBufferArrayNonUniformIndexing + OpCapability StorageUniform16 + OpCapability StorageUniformBufferBlock16 + OpCapability Tessellation + OpCapability UniformTexelBufferArrayNonUniformIndexing + OpCapability VulkanMemoryModel + OpExtension "SPV_EXT_fragment_fully_covered" + OpExtension "SPV_EXT_shader_image_int64" + OpExtension "SPV_EXT_shader_stencil_export" + OpExtension "SPV_EXT_shader_viewport_index_layer" + OpExtension "SPV_KHR_fragment_shader_barycentric" + OpExtension "SPV_KHR_fragment_shading_rate" + OpExtension "SPV_KHR_post_depth_coverage" + OpExtension "SPV_KHR_ray_query" + OpExtension "SPV_KHR_ray_tracing" + OpExtension "SPV_KHR_shader_clock" + OpExtension "SPV_NV_mesh_shader" + OpExtension "SPV_NV_ray_tracing" + OpExtension "SPV_NV_viewport_array2" +; CHECK: OpCapability Linkage +; CHECK: OpCapability ClipDistance +; CHECK: OpCapability CullDistance +; CHECK: OpCapability DemoteToHelperInvocation +; CHECK: OpCapability DeviceGroup +; CHECK: OpCapability DrawParameters +; CHECK: OpCapability Float16 +; CHECK: OpCapability Float64 +; CHECK: OpCapability FragmentBarycentricKHR +; CHECK: OpCapability FragmentFullyCoveredEXT +; CHECK: OpCapability FragmentShadingRateKHR +; CHECK: OpCapability GroupNonUniform +; CHECK: OpCapability GroupNonUniformArithmetic +; CHECK: OpCapability GroupNonUniformBallot +; CHECK: OpCapability GroupNonUniformQuad +; CHECK: OpCapability GroupNonUniformShuffle +; CHECK: OpCapability Image1D +; CHECK: OpCapability ImageBuffer +; CHECK: OpCapability ImageGatherExtended +; CHECK: OpCapability ImageMSArray +; CHECK: OpCapability ImageQuery +; CHECK: OpCapability InputAttachment +; CHECK: OpCapability InputAttachmentArrayNonUniformIndexing +; CHECK: OpCapability Int16 +; CHECK: OpCapability Int64 +; CHECK: OpCapability Int64Atomics +; CHECK: OpCapability Int64ImageEXT +; CHECK: OpCapability MeshShadingNV +; CHECK: OpCapability MinLod +; CHECK: OpCapability MultiView +; CHECK: OpCapability MultiViewport +; CHECK: OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability RayQueryKHR +; CHECK: OpCapability RayTracingKHR +; CHECK: OpCapability RayTracingNV +; CHECK: OpCapability RayTraversalPrimitiveCullingKHR +; CHECK: OpCapability RuntimeDescriptorArray +; CHECK: OpCapability SampleMaskPostDepthCoverage +; CHECK: OpCapability SampleRateShading +; CHECK: OpCapability Sampled1D +; CHECK: OpCapability SampledBuffer +; CHECK: OpCapability SampledImageArrayNonUniformIndexing +; CHECK: OpCapability Shader +; CHECK: OpCapability ShaderClockKHR +; CHECK: OpCapability ShaderLayer +; CHECK: OpCapability ShaderNonUniform +; CHECK: OpCapability ShaderViewportIndex +; CHECK: OpCapability ShaderViewportIndexLayerEXT +; CHECK: OpCapability SparseResidency +; CHECK: OpCapability StencilExportEXT +; CHECK: OpCapability StorageImageArrayNonUniformIndexing +; CHECK: OpCapability StorageImageExtendedFormats +; CHECK: OpCapability StorageImageReadWithoutFormat +; CHECK: OpCapability StorageImageWriteWithoutFormat +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpCapability StoragePushConstant16 +; CHECK: OpCapability StorageTexelBufferArrayNonUniformIndexing +; CHECK: OpCapability Tessellation +; CHECK: OpCapability UniformTexelBufferArrayNonUniformIndex +; CHECK: OpCapability VulkanMemoryModel +; CHECK: OpExtension "SPV_EXT_fragment_fully_covered" +; CHECK: OpExtension "SPV_EXT_shader_image_int64" +; CHECK: OpExtension "SPV_EXT_shader_stencil_export" +; CHECK: OpExtension "SPV_EXT_shader_viewport_index_layer" +; CHECK: OpExtension "SPV_KHR_fragment_shader_barycentric" +; CHECK: OpExtension "SPV_KHR_fragment_shading_rate" +; CHECK: OpExtension "SPV_KHR_post_depth_coverage" +; CHECK: OpExtension "SPV_KHR_ray_query" +; CHECK: OpExtension "SPV_KHR_ray_tracing" +; CHECK: OpExtension "SPV_KHR_shader_clock" +; CHECK: OpExtension "SPV_NV_mesh_shader" +; CHECK: OpExtension "SPV_NV_ray_tracing" +; CHECK: OpExtension "SPV_NV_viewport_array2" + OpMemoryModel Logical Vulkan + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + SetTargetEnv(SPV_ENV_VULKAN_1_3); + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, KeepShader) { + 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 + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, KeepShaderClockWhenInUse) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Int64 + OpCapability ShaderClockKHR + OpExtension "SPV_KHR_shader_clock" +; CHECK: OpCapability ShaderClockKHR +; CHECK: OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %ulong = OpTypeInt 64 0 + %scope = OpConstant %uint 1 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpReadClockKHR %ulong %scope + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, TrimShaderClockWhenUnused) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Int64 + OpCapability ShaderClockKHR + OpExtension "SPV_KHR_shader_clock" +; CHECK-NOT: OpCapability ShaderClockKHR +; CHECK-NOT: OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, AMDShaderBallotExtensionRemains) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Groups + OpExtension "SPV_AMD_shader_ballot" +; CHECK: OpCapability Groups +; CHECK: OpExtension "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %1 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %2 = OpFunction %void None %1 + %3 = OpLabel + %4 = OpGroupIAddNonUniformAMD %uint %uint_0 ExclusiveScan %uint_0 + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, AMDShaderBallotExtensionRemoved) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Groups + OpExtension "SPV_AMD_shader_ballot" +; CHECK-NOT: OpCapability Groups +; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %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, MinLod_RemovedIfNotUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability MinLod +; CHECK-NOT: OpCapability MinLod + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %type_image = OpTypeImage %float Cube 2 0 0 1 Rgba32f + %ptr_type_image = OpTypePointer UniformConstant %type_image + %type_sampler = OpTypeSampler + %ptr_type_sampler = OpTypePointer UniformConstant %type_sampler + %float_0 = OpConstant %float 0 + %float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %image = OpVariable %ptr_type_image UniformConstant + %sampler = OpVariable %ptr_type_sampler UniformConstant + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + %21 = OpLoad %type_image %image + %22 = OpLoad %type_sampler %sampler + %24 = OpSampledImage %type_sampled_image %21 %22 + %25 = OpImageSampleImplicitLod %v4float %24 %float_000 + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, MinLod_RemainsWithOpImageSampleImplicitLod) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Sampled1D + OpCapability MinLod +; CHECK: OpCapability MinLod + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %type_image = OpTypeImage %float Cube 2 0 0 1 Rgba32f + %ptr_type_image = OpTypePointer UniformConstant %type_image + %type_sampler = OpTypeSampler + %ptr_type_sampler = OpTypePointer UniformConstant %type_sampler + %float_0 = OpConstant %float 0 + %float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %image = OpVariable %ptr_type_image UniformConstant + %sampler = OpVariable %ptr_type_sampler UniformConstant + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + %21 = OpLoad %type_image %image + %22 = OpLoad %type_sampler %sampler + %24 = OpSampledImage %type_sampled_image %21 %22 + %25 = OpImageSampleImplicitLod %v4float %24 %float_000 MinLod %float_0 + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + MinLod_RemainsWithOpImageSparseSampleImplicitLod) { + const std::string kTest = R"( + OpCapability Shader + OpCapability SparseResidency + OpCapability ImageGatherExtended + OpCapability MinLod +; CHECK: OpCapability MinLod + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %type_image = OpTypeImage %float 2D 2 0 0 1 Unknown + %ptr_type_image = OpTypePointer UniformConstant %type_image + %type_sampler = OpTypeSampler + %ptr_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_sampled_image = OpTypeSampledImage %type_image + %sparse_struct = OpTypeStruct %uint %v4float + %float_0 = OpConstant %float 0 + %float_00 = OpConstantComposite %v2float %float_0 %float_0 + %float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %image = OpVariable %ptr_type_image UniformConstant + %sampler = OpVariable %ptr_type_sampler UniformConstant + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + %21 = OpLoad %type_image %image + %22 = OpLoad %type_sampler %sampler + %24 = OpSampledImage %type_sampled_image %21 %22 + %25 = OpImageSparseSampleImplicitLod %sparse_struct %24 %float_00 MinLod %float_0 + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, MinLod_DetectsMinLodWithBitmaskImageOperand) { + const std::string kTest = R"( + OpCapability MinLod +; CHECK: OpCapability MinLod + OpCapability Shader + OpCapability SparseResidency + OpCapability ImageGatherExtended + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %type_sampler = OpTypeSampler + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %v2int = OpTypeVector %int 2 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %ptr_sampler = OpTypePointer UniformConstant %type_sampler + %type_image = OpTypeImage %float 2D 2 0 0 1 Unknown + %ptr_image = OpTypePointer UniformConstant %type_image + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %type_sampled_image = OpTypeSampledImage %type_image + %type_struct = OpTypeStruct %uint %v4float + + %int_1 = OpConstant %int 1 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %8 = OpConstantComposite %v2float %float_0 %float_0 + %12 = OpConstantComposite %v2int %int_1 %int_1 + + %2 = OpVariable %ptr_sampler UniformConstant + %3 = OpVariable %ptr_image UniformConstant + %27 = OpTypeFunction %void + %1 = OpFunction %void None %27 + %28 = OpLabel + %29 = OpLoad %type_image %3 + %30 = OpLoad %type_sampler %2 + %31 = OpSampledImage %type_sampled_image %29 %30 + %32 = OpImageSparseSampleImplicitLod %type_struct %31 %8 ConstOffset|MinLod %12 %float_0 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageInputOutput16_RemainsWithInputPointer_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer Input %half + %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, + StorageInputOutput16_RemainsWithInputPointer_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer Input %half + %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, + StorageInputOutput16_RemainsWithInputPointerArray_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %array = OpTypeArray %half %uint_1 + %ptr = OpTypePointer Input %array + %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, + StorageInputOutput16_RemainsWithInputPointerArray_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %array = OpTypeArray %half %uint_1 + %ptr = OpTypePointer Input %array + %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, + StorageInputOutput16_RemainsWithInputPointerStruct_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Input %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, + StorageInputOutput16_RemainsWithInputPointerStruct_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Input %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageInputOutput16_RemainsWithInputPointerStructOfStruct_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float %half + %parent = OpTypeStruct %float %struct + %ptr = OpTypePointer Input %parent + %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, + StorageInputOutput16_RemainsWithInputPointerStructOfStruct_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float %half + %parent = OpTypeStruct %float %struct + %ptr = OpTypePointer Input %parent + %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, + StorageInputOutput16_RemainsWithInputPointerArrayOfStruct_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %array = OpTypeArray %struct %uint_1 + %ptr = OpTypePointer Input %array + %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, + StorageInputOutput16_RemainsWithInputPointerArrayOfStruct_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %array = OpTypeArray %struct %uint_1 + %ptr = OpTypePointer Input %array + %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, + StorageInputOutput16_RemainsWithInputPointerVector_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %vector = OpTypeVector %half 4 + %ptr = OpTypePointer Input %vector + %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, + StorageInputOutput16_RemainsWithInputPointerVector_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %vector = OpTypeVector %half 4 + %ptr = OpTypePointer Input %vector + %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, + StorageInputOutput16_RemainsWithInputPointerMatrix_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %vector = OpTypeVector %half 4 + %matrix = OpTypeMatrix %vector 4 + %ptr = OpTypePointer Input %matrix + %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, + StorageInputOutput16_RemainsWithInputPointerMatrix_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %vector = OpTypeVector %half 4 + %matrix = OpTypeMatrix %vector 4 + %ptr = OpTypePointer Input %matrix + %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, + StorageInputOutput16_IsRemovedWithoutInputPointer) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK-NOT: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageInputOutput16_RemainsWithOutputPointer_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer Output %half + %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, + StorageInputOutput16_RemainsWithOutputPointer_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer Output %half + %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, + StorageInputOutput16_RemovedWithoutOutputPointer) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK-NOT: OpCapability StorageInputOutput16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %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, + StoragePushConstant16_RemainsSimplePointer_Vulkan1_0) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StoragePushConstant16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StoragePushConstant16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer PushConstant %half + %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, + StoragePushConstant16_RemainsSimplePointer_Vulkan1_1) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StoragePushConstant16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK: OpCapability StoragePushConstant16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer PushConstant %half + %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, StoragePushConstant16_RemovedSimplePointer) { + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StoragePushConstant16 + OpExtension "SPV_KHR_16bit_storage" +; CHECK-NOT: OpCapability StoragePushConstant16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %ptr = OpTypePointer Function %half + %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, + StorageUniformBufferBlock16_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 + OpExtension "SPV_KHR_16bit_storage" + +; CHECK: OpCapability StorageBuffer16BitAccess +; `-> StorageUniformBufferBlock16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct BufferBlock + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Uniform %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, + StorageUniformBufferBlock16_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 + OpExtension "SPV_KHR_16bit_storage" + +; CHECK: OpCapability StorageBuffer16BitAccess +; `-> StorageUniformBufferBlock16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct BufferBlock + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Uniform %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageUniformBufferBlock16_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 +; `-> StorageUniformBufferBlock16 +; 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, + StorageUniform16_RemovedWithBufferBlockPointer_Vulkan1_0) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + static_assert(spv::Capability::StorageUniform16 == + spv::Capability::UniformAndStorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpCapability UniformAndStorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + +; CHECK: OpCapability StorageBuffer16BitAccess +; `-> StorageUniformBufferBlock16 +; CHECK-NOT: OpCapability UniformAndStorageBuffer16BitAccess +; `-> StorageUniform16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct BufferBlock + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Uniform %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageUniform16_RemovedWithBufferBlockPointer_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + static_assert(spv::Capability::StorageUniform16 == + spv::Capability::UniformAndStorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpCapability UniformAndStorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + +; CHECK: OpCapability StorageBuffer16BitAccess +; `-> StorageUniformBufferBlock16 +; CHECK-NOT: OpCapability UniformAndStorageBuffer16BitAccess +; `-> StorageUniform16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %struct BufferBlock + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Uniform %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageUniform16_RemovedWithNonBlockUniformPointer_Vulkan1_0) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + static_assert(spv::Capability::StorageUniform16 == + spv::Capability::UniformAndStorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpCapability UniformAndStorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + +; CHECK-NOT: OpCapability StorageBuffer16BitAccess +; `-> StorageUniformBufferBlock16 +; CHECK: OpCapability UniformAndStorageBuffer16BitAccess +; `-> StorageUniform16 +; CHECK: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Uniform %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageUniform16_RemovedWithNonBlockUniformPointer_Vulkan1_1) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354 + static_assert(spv::Capability::StorageUniformBufferBlock16 == + spv::Capability::StorageBuffer16BitAccess); + static_assert(spv::Capability::StorageUniform16 == + spv::Capability::UniformAndStorageBuffer16BitAccess); + + const std::string kTest = R"( + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpCapability UniformAndStorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + +; CHECK-NOT: OpCapability StorageBuffer16BitAccess +; `-> StorageUniformBufferBlock16 +; CHECK: OpCapability UniformAndStorageBuffer16BitAccess +; `-> StorageUniform16 +; CHECK-NOT: OpExtension "SPV_KHR_16bit_storage" + + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + %void = OpTypeVoid + %half = OpTypeFloat 16 + %struct = OpTypeStruct %half + %ptr = OpTypePointer Uniform %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::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, FragmentShaderInterlock_RemovedIfNotUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK-NOT: OpCapability FragmentShaderPixelInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderSampleInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK-NOT: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %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, + FragmentShaderPixelInterlock_RemainsWhenOrderedIsUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK: OpCapability FragmentShaderPixelInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderSampleInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %main PixelInterlockOrderedEXT + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + FragmentShaderPixelInterlock_RemainsWhenUnorderedIsUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK: OpCapability FragmentShaderPixelInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderSampleInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %main PixelInterlockUnorderedEXT + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + FragmentShaderSampleInterlock_RemainsWhenOrderedIsUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK-NOT: OpCapability FragmentShaderPixelInterlockEXT +; CHECK: OpCapability FragmentShaderSampleInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %main SampleInterlockOrderedEXT + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + FragmentShaderSampleInterlock_RemainsWhenUnorderedIsUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK-NOT: OpCapability FragmentShaderPixelInterlockEXT +; CHECK: OpCapability FragmentShaderSampleInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %main SampleInterlockUnorderedEXT + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + FragmentShaderShadingRateInterlock_RemainsWhenOrderedIsUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK-NOT: OpCapability FragmentShaderPixelInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderSampleInterlockEXT +; CHECK: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %main ShadingRateInterlockOrderedEXT + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + FragmentShaderShadingRateInterlock_RemainsWhenUnorderedIsUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpCapability FragmentShaderSampleInterlockEXT + OpCapability FragmentShaderShadingRateInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" +; CHECK-NOT: OpCapability FragmentShaderPixelInterlockEXT +; CHECK-NOT: OpCapability FragmentShaderSampleInterlockEXT +; CHECK: OpCapability FragmentShaderShadingRateInterlockEXT +; CHECK: OpExtension "SPV_EXT_fragment_shader_interlock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %main ShadingRateInterlockUnorderedEXT + %void = OpTypeVoid + %1 = OpTypeFunction %void + %2 = OpFunction %void None %1 + %3 = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, Int64_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability Int64 +; CHECK-NOT: OpCapability Int64 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, Int64_RemainsWhenUsed) { + const std::string kTest = R"( + OpCapability Int64 +; CHECK: OpCapability Int64 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %int = OpTypeInt 64 0 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, RayQueryKHR_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpExtension "SPV_KHR_ray_query" +; CHECK-NOT: OpCapability RayQueryKHR +; CHECK-NOT: OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %out_var_TEXCOORD1 + OpSource HLSL 660 + OpName %out_var_TEXCOORD1 "out.var.TEXCOORD1" + OpName %main "main" + OpDecorate %out_var_TEXCOORD1 Flat + OpDecorate %out_var_TEXCOORD1 Location 0 + %uint = OpTypeInt 32 0 + %uint_1234 = OpConstant %uint 1234 +%_ptr_Output_uint = OpTypePointer Output %uint + %void = OpTypeVoid + %7 = OpTypeFunction %void +%out_var_TEXCOORD1 = OpVariable %_ptr_Output_uint Output + %main = OpFunction %void None %7 + %8 = OpLabel + OpStore %out_var_TEXCOORD1 %uint_1234 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + RayQueryKHR_RemainsWhenAccelerationStructureIsPresent) { + const std::string kTest = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpExtension "SPV_KHR_ray_query" +; CHECK: OpCapability RayQueryKHR +; CHECK: OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + OpDecorate %var_bvh DescriptorSet 0 + OpDecorate %var_bvh Binding 0 + %bvh = OpTypeAccelerationStructureKHR + %ptr_bvh = OpTypePointer UniformConstant %bvh + %void = OpTypeVoid + %20 = OpTypeFunction %void + %var_bvh = OpVariable %ptr_bvh UniformConstant + %main = OpFunction %void None %20 + %30 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, RayQueryKHR_RemainsWhenRayQueryTypeIsPresent) { + const std::string kTest = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpExtension "SPV_KHR_ray_query" +; CHECK: OpCapability RayQueryKHR +; CHECK: OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + %query = OpTypeRayQueryKHR + %void = OpTypeVoid + %20 = OpTypeFunction %void + %ptr_query = OpTypePointer Function %query + %main = OpFunction %void None %20 + %30 = OpLabel + %var_query = OpVariable %ptr_query Function + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, RayQueryKHR_RemainsWhenUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability RayQueryKHR + OpExtension "SPV_KHR_ray_query" +; CHECK: OpCapability RayQueryKHR +; CHECK: OpExtension "SPV_KHR_ray_query" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + OpDecorate %bvh DescriptorSet 0 + OpDecorate %bvh Binding 0 + OpDecorate %output DescriptorSet 0 + OpDecorate %output Binding 1 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_float 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_float BufferBlock + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 1 + %v3float = OpTypeVector %float 3 + %12 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 +%accelerationStructureKHR = OpTypeAccelerationStructureKHR +%_ptr_UniformConstant_accelerationStructureKHR = OpTypePointer UniformConstant %accelerationStructureKHR +%_runtimearr_float = OpTypeRuntimeArray %float +%type_RWStructuredBuffer_float = OpTypeStruct %_runtimearr_float +%_ptr_Uniform_type_RWStructuredBuffer_float = OpTypePointer Uniform %type_RWStructuredBuffer_float + %void = OpTypeVoid + %20 = OpTypeFunction %void +%rayQueryKHR = OpTypeRayQueryKHR +%_ptr_Function_rayQueryKHR = OpTypePointer Function %rayQueryKHR + %bool = OpTypeBool +%_ptr_Uniform_float = OpTypePointer Uniform %float + %bvh = OpVariable %_ptr_UniformConstant_accelerationStructureKHR UniformConstant + %output = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_float Uniform + %main = OpFunction %void None %20 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_rayQueryKHR Function + %26 = OpLoad %accelerationStructureKHR %bvh + OpRayQueryInitializeKHR %25 %26 %uint_0 %uint_0 %12 %float_0 %12 %float_0 + %27 = OpRayQueryProceedKHR %bool %25 + %28 = OpRayQueryGetIntersectionTypeKHR %uint %25 %uint_1 + %29 = OpIEqual %bool %28 %uint_1 + OpSelectionMerge %30 None + OpBranchConditional %29 %31 %30 + %31 = OpLabel + %32 = OpAccessChain %_ptr_Uniform_float %output %int_0 %uint_0 + OpStore %32 %float_0 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + RayTracingKHR_RemainsWithIntersectionExecutionMode) { + const std::string kTest = R"( + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" +; CHECK: OpCapability RayTracingKHR +; CHECK: OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint IntersectionKHR %main "main" + OpSource HLSL 660 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %4 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + RayTracingKHR_RemainsWithClosestHitExecutionMode) { + const std::string kTest = R"( + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" +; CHECK: OpCapability RayTracingKHR +; CHECK: OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint ClosestHitKHR %main "main" %a + OpSource HLSL 630 + OpName %Payload "Payload" + OpMemberName %Payload 0 "color" + OpName %a "a" + OpName %main "main" + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Payload = OpTypeStruct %v4float +%ptr_payload = OpTypePointer IncomingRayPayloadKHR %Payload + %void = OpTypeVoid + %8 = OpTypeFunction %void + %a = OpVariable %ptr_payload IncomingRayPayloadKHR + %main = OpFunction %void None %8 + %9 = OpLabel + %10 = OpLoad %Payload %a + OpStore %a %10 + OpReturn + OpFunctionEnd + + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, RayTracingKHR_RemainsWithAnyHitExecutionMode) { + const std::string kTest = R"( + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" +; CHECK: OpCapability RayTracingKHR +; CHECK: OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint AnyHitKHR %main "main" %a + OpSource HLSL 630 + OpName %Payload "Payload" + OpMemberName %Payload 0 "color" + OpName %a "a" + OpName %main "main" + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Payload = OpTypeStruct %v4float +%ptr_payload = OpTypePointer IncomingRayPayloadKHR %Payload + %void = OpTypeVoid + %8 = OpTypeFunction %void + %a = OpVariable %ptr_payload IncomingRayPayloadKHR + %main = OpFunction %void None %8 + %9 = OpLabel + %10 = OpLoad %Payload %a + OpStore %a %10 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, RayTracingKHR_RemainsWithMissExecutionMode) { + const std::string kTest = R"( + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" +; CHECK: OpCapability RayTracingKHR +; CHECK: OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint MissKHR %main "main" %a + OpSource HLSL 630 + OpName %Payload "Payload" + OpMemberName %Payload 0 "color" + OpName %a "a" + OpName %main "main" + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Payload = OpTypeStruct %v4float +%ptr_payload = OpTypePointer IncomingRayPayloadKHR %Payload + %void = OpTypeVoid + %8 = OpTypeFunction %void + %a = OpVariable %ptr_payload IncomingRayPayloadKHR + %main = OpFunction %void None %8 + %9 = OpLabel + %10 = OpLoad %Payload %a + OpStore %a %10 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + RayTracingKHR_RemainsWithRayGenerationExecutionMode) { + const std::string kTest = R"( + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" +; CHECK: OpCapability RayTracingKHR +; CHECK: OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint RayGenerationKHR %main "main" + OpSource HLSL 630 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %4 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + RayTracingKHR_RemainsWithCallableExecutionMode) { + const std::string kTest = R"( +; CHECK: OpCapability RayTracingKHR +; CHECK: OpExtension "SPV_KHR_ray_tracing" + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint CallableKHR %main "main" %a + OpSource HLSL 660 + OpName %Payload "Payload" + OpMemberName %Payload 0 "data" + OpName %a "a" + OpName %main "main" + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Payload = OpTypeStruct %v4float +%ptr_payload = OpTypePointer IncomingCallableDataKHR %Payload + %void = OpTypeVoid + %8 = OpTypeFunction %void + %a = OpVariable %ptr_payload IncomingCallableDataKHR + %main = OpFunction %void None %8 + %9 = OpLabel + %10 = OpLoad %Payload %a + OpStore %a %10 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + ImageMSArray_RemainsIfSampledIs2AndArrayedIs1) { + const std::string kTest = R"( + OpCapability ImageMSArray + ; CHECK: OpCapability ImageMSArray + OpCapability Shader + OpCapability StorageImageMultisample + OpCapability StorageImageReadWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %var_image DescriptorSet 0 + OpDecorate %var_image Binding 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + %uint_2 = OpConstant %u32 2 + %uint_1 = OpConstant %u32 1 + %v2uint = OpTypeVector %u32 2 + %v4float = OpTypeVector %f32 4 + %image = OpTypeImage %f32 2D 2 1 1 2 Unknown +%ptr_image = OpTypePointer UniformConstant %image + %10 = OpConstantComposite %v2uint %uint_1 %uint_2 +%var_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %main_lab = OpLabel + %18 = OpLoad %image %var_image + %19 = OpImageRead %v4float %18 %10 Sample %uint_2 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, ImageMSArray_RemovedIfNotUsed) { + const std::string kTest = R"( + OpCapability Shader + OpCapability ImageMSArray +; CHECK-NOT: OpCapability ImageMSArray + 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, ImageMSArray_RemovedIfArrayedIsNot1) { + const std::string kTest = R"( + OpCapability ImageMSArray + ; CHECK-NOT: OpCapability ImageMSArray + OpCapability Shader + OpCapability StorageImageMultisample + OpCapability StorageImageReadWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %var_image DescriptorSet 0 + OpDecorate %var_image Binding 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + %uint_2 = OpConstant %u32 2 + %uint_1 = OpConstant %u32 1 + %v2uint = OpTypeVector %u32 2 + %v4float = OpTypeVector %f32 4 + %image = OpTypeImage %f32 2D 2 0 1 2 Unknown +%ptr_image = OpTypePointer UniformConstant %image + %10 = OpConstantComposite %v2uint %uint_1 %uint_2 +%var_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %main_lab = OpLabel + %18 = OpLoad %image %var_image + %19 = OpImageRead %v4float %18 %10 Sample %uint_2 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, ImageMSArray_RemovedIfSampledNot2) { + const std::string kTest = R"( + OpCapability ImageMSArray + ; CHECK-NOT: OpCapability ImageMSArray + OpCapability Shader + OpCapability StorageImageReadWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %var_image DescriptorSet 0 + OpDecorate %var_image Binding 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + %uint_3 = OpConstant %u32 3 + %uint_2 = OpConstant %u32 2 + %uint_1 = OpConstant %u32 1 + %v3uint = OpTypeVector %u32 3 + %v4float = OpTypeVector %f32 4 + %image = OpTypeImage %f32 2D 2 1 0 2 Unknown +%ptr_image = OpTypePointer UniformConstant %image + %10 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3 +%var_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %main_lab = OpLabel + %18 = OpLoad %image %var_image + %19 = OpImageRead %v4float %18 %10 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, Float64_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability Float64 +; CHECK-NOT: OpCapability Float64 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, Float64_RemainsWhenUsed) { + const std::string kTest = R"( + OpCapability Float64 +; CHECK: OpCapability Float64 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %float = OpTypeFloat 64 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + ComputeDerivativeGroupQuads_ReamainsWithExecMode) { + const std::string kTest = R"( + OpCapability ComputeDerivativeGroupQuadsNV + OpCapability ComputeDerivativeGroupLinearNV +; CHECK-NOT: OpCapability ComputeDerivativeGroupLinearNV +; CHECK: OpCapability ComputeDerivativeGroupQuadsNV +; CHECK-NOT: OpCapability ComputeDerivativeGroupLinearNV + OpCapability Shader +; CHECK: OpExtension "SPV_NV_compute_shader_derivatives" + OpExtension "SPV_NV_compute_shader_derivatives" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 DerivativeGroupQuadsNV + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + ComputeDerivativeGroupLinear_ReamainsWithExecMode) { + const std::string kTest = R"( + OpCapability ComputeDerivativeGroupLinearNV + OpCapability ComputeDerivativeGroupQuadsNV +; CHECK-NOT: OpCapability ComputeDerivativeGroupQuadsNV +; CHECK: OpCapability ComputeDerivativeGroupLinearNV +; CHECK-NOT: OpCapability ComputeDerivativeGroupQuadsNV + OpCapability Shader +; CHECK: OpExtension "SPV_NV_compute_shader_derivatives" + OpExtension "SPV_NV_compute_shader_derivatives" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 DerivativeGroupLinearNV + %void = OpTypeVoid + %float = OpTypeFloat 64 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageImageReadWithoutFormat_RemovedIfUnused) { + const std::string kTest = R"( + OpCapability StorageImageReadWithoutFormat +; CHECK-NOT: OpCapability StorageImageReadWithoutFormat + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var + OpExecutionMode %PSMain OriginUpperLeft + OpDecorate %out_var Location 0 + %float = OpTypeFloat 32 + %float4 = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 +%float4_0000 = OpConstantComposite %float4 %float_0 %float_0 %float_0 %float_0 + %ptr_float4 = OpTypePointer Output %float4 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %out_var = OpVariable %ptr_float4 Output + %PSMain = OpFunction %void None %9 + %10 = OpLabel + OpStore %out_var %float4_0000 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageImageReadWithoutFormat_RemovedIfUnusedOpImageFetch) { + const std::string kTest = R"( + OpCapability StorageImageReadWithoutFormat +; CHECK-NOT: OpCapability StorageImageReadWithoutFormat + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var + OpExecutionMode %PSMain OriginUpperLeft + OpDecorate %out_var Location 0 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 1 + %float = OpTypeFloat 32 + %float4 = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int2 = OpTypeVector %int 2 + %type_image = OpTypeImage %float 2D 2 0 0 1 Unknown + %ptr_image = OpTypePointer UniformConstant %type_image + %int_0 = OpConstant %int 0 + %int2_00 = OpConstantComposite %int2 %int_0 %int_0 + %ptr_float4 = OpTypePointer Output %float4 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %texture = OpVariable %ptr_image UniformConstant + %out_var = OpVariable %ptr_float4 Output + %PSMain = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %type_image %texture + %12 = OpImageFetch %float4 %11 %int2_00 Lod %int_0 + OpStore %out_var %12 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageImageReadWithoutFormat_RemainsWhenRequiredWithRead) { + const std::string kTest = R"( + OpCapability StorageImageReadWithoutFormat +; CHECK: OpCapability StorageImageReadWithoutFormat + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var + OpExecutionMode %PSMain OriginUpperLeft + OpDecorate %out_var Location 0 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 1 + %float = OpTypeFloat 32 + %float4 = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int2 = OpTypeVector %int 2 + %type_image = OpTypeImage %float 2D 2 0 0 1 Unknown + %ptr_image = OpTypePointer UniformConstant %type_image + %int_0 = OpConstant %int 0 + %int2_00 = OpConstantComposite %int2 %int_0 %int_0 + %ptr_float4 = OpTypePointer Output %float4 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %texture = OpVariable %ptr_image UniformConstant + %out_var = OpVariable %ptr_float4 Output + %PSMain = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %type_image %texture + %12 = OpImageRead %float4 %11 %int2_00 Lod %int_0 + OpStore %out_var %12 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageImageReadWithoutFormat_RemainsWhenRequiredWithSparseRead) { + const std::string kTest = R"( + OpCapability StorageImageReadWithoutFormat +; CHECK: OpCapability StorageImageReadWithoutFormat + OpCapability SparseResidency + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" + OpExecutionMode %PSMain OriginUpperLeft + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 1 + %float = OpTypeFloat 32 + %float4 = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int2 = OpTypeVector %int 2 + %type_image = OpTypeImage %float 2D 2 0 0 2 Unknown + %struct = OpTypeStruct %int %float4 + %ptr_image = OpTypePointer UniformConstant %type_image + %int_0 = OpConstant %int 0 + %int2_00 = OpConstantComposite %int2 %int_0 %int_0 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %texture = OpVariable %ptr_image UniformConstant + %PSMain = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %type_image %texture + %12 = OpImageSparseRead %struct %11 %int2_00 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + StorageImageReadWithoutFormat_RemovedWithReadOnSubpassData) { + const std::string kTest = R"( + OpCapability StorageImageReadWithoutFormat +; CHECK-NOT: OpCapability StorageImageReadWithoutFormat + OpCapability InputAttachment + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %out_var + OpExecutionMode %PSMain OriginUpperLeft + OpDecorate %out_var Location 0 + OpDecorate %texture DescriptorSet 0 + OpDecorate %texture Binding 1 + %float = OpTypeFloat 32 + %float4 = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int2 = OpTypeVector %int 2 + %type_image = OpTypeImage %float SubpassData 2 0 0 2 Unknown + %ptr_image = OpTypePointer UniformConstant %type_image + %int_0 = OpConstant %int 0 + %int2_00 = OpConstantComposite %int2 %int_0 %int_0 + %ptr_float4 = OpTypePointer Output %float4 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %texture = OpVariable %ptr_image UniformConstant + %out_var = OpVariable %ptr_float4 Output + %PSMain = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %type_image %texture + %12 = OpImageRead %float4 %11 %int2_00 + OpStore %out_var %12 + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, PhysicalStorageBuffer_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability PhysicalStorageBufferAddresses +; CHECK-NOT: OpCapability PhysicalStorageBufferAddresses + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + PhysicalStorageBuffer_RemainsWithOpTypeForwardPointer) { + const std::string kTest = R"( + OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability PhysicalStorageBufferAddresses + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %struct = OpTypeStruct %int + OpTypeForwardPointer %ptr PhysicalStorageBuffer + %ptr = OpTypePointer PhysicalStorageBuffer %struct + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + PhysicalStorageBuffer_RemainsWithPhysicalStorageBufferStorage) { + const std::string kTest = R"( + OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability PhysicalStorageBufferAddresses + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %struct = OpTypeStruct %int + %ptr = OpTypePointer PhysicalStorageBuffer %struct + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + PhysicalStorageBuffer_RemainsWithRestrictDecoration) { + const std::string kTest = R"( + OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability PhysicalStorageBufferAddresses + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + OpDecorate %var RestrictPointer + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %struct = OpTypeStruct %int + %ptr = OpTypePointer Function %struct + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %6 = OpLabel + %var = OpVariable %ptr Function + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + PhysicalStorageBuffer_RemainsWithAliasedDecoration) { + const std::string kTest = R"( + OpCapability PhysicalStorageBufferAddresses +; CHECK: OpCapability PhysicalStorageBufferAddresses + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + OpDecorate %var AliasedPointer + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %struct = OpTypeStruct %int + %ptr = OpTypePointer Function %struct + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %6 = OpLabel + %var = OpVariable %ptr Function + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, Float16_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability Float16 +; CHECK-NOT: OpCapability Float16 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, Float16_RemainsWhenUsed) { + const std::string kTest = R"( + OpCapability Float16 +; CHECK: OpCapability Float16 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %float = OpTypeFloat 16 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, Int16_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability Int16 +; CHECK-NOT: OpCapability Int16 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, Int16_RemainsWhenUsed) { + const std::string kTest = R"( + OpCapability Int16 +; CHECK: OpCapability Int16 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %int = OpTypeInt 16 1 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, UInt16_RemainsWhenUsed) { + const std::string kTest = R"( + OpCapability Int16 +; CHECK: OpCapability Int16 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %uint = OpTypeInt 16 0 + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(TrimCapabilitiesPassTest, + VulkanMemoryModelDeviceScope_RemovedWhenUnused) { + const std::string kTest = R"( + OpCapability VulkanMemoryModelDeviceScope +; CHECK-NOT: OpCapability VulkanMemoryModelDeviceScope + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %6 = OpLabel + OpReturn + OpFunctionEnd; + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + VulkanMemoryModelDeviceScope_RemovedWhenUsedWithGLSL450) { + const std::string kTest = R"( + OpCapability VulkanMemoryModelDeviceScope +; CHECK-NOT: OpCapability VulkanMemoryModelDeviceScope + OpCapability Shader + OpCapability ShaderClockKHR + OpCapability Int64 + OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %ulong = OpTypeInt 64 0 + %uint_1 = OpConstant %uint 1 + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %6 = OpLabel + %22 = OpReadClockKHR %ulong %uint_1 ; Device Scope + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(TrimCapabilitiesPassTest, + VulkanMemoryModelDeviceScope_RemainsWhenUsedWithVulkan) { + const std::string kTest = R"( + OpCapability VulkanMemoryModelDeviceScope +; CHECK: OpCapability VulkanMemoryModelDeviceScope + OpCapability Shader + OpCapability ShaderClockKHR + OpCapability Int64 + OpExtension "SPV_KHR_shader_clock" + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 2 4 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %ulong = OpTypeInt 64 0 + %uint_1 = OpConstant %uint 1 + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %6 = OpLabel + %22 = OpReadClockKHR %ulong %uint_1 ; Device Scope + OpReturn + OpFunctionEnd + )"; + const auto result = + SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +} // namespace +} // namespace opt +} // namespace spvtools
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp index 563eb74..d4d0fef 100644 --- a/test/opt/type_manager_test.cpp +++ b/test/opt/type_manager_test.cpp
@@ -171,6 +171,7 @@ types.emplace_back(new NamedBarrier()); types.emplace_back(new AccelerationStructureNV()); types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24)); + types.emplace_back(new CooperativeMatrixKHR(f32, 8, 8, 8, 1002)); types.emplace_back(new RayQueryKHR()); types.emplace_back(new HitObjectNV()); @@ -237,6 +238,8 @@ %arr_long_constant = OpTypeArray %s32 %long_constant %arr_spec_const_op = OpTypeArray %s32 %spec_const_op %cm = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4 + %id2 = OpConstant %u32 2 + %cmkhr = OpTypeCooperativeMatrixKHR %f64 %id4 %id4 %id4 %id2 )"; std::vector<std::pair<uint32_t, std::string>> type_id_strs = { @@ -275,6 +278,7 @@ {37, "[sint32, id(33), words(0,705032704,1)]"}, {38, "[sint32, id(34), words(2,34)]"}, {39, "<float64, 6, 6, 6>"}, + {41, "<float64, 6, 6, 6, 40>"}, }; std::unique_ptr<IRContext> context = @@ -938,10 +942,11 @@ EXPECT_NE(context, nullptr); std::vector<std::unique_ptr<Type>> types = GenerateAllTypes(); - uint32_t id = 1u; + uint32_t id = 0u; for (auto& t : types) { - context->get_type_mgr()->RegisterType(id, *t); + context->get_type_mgr()->RegisterType(++id, *t); EXPECT_EQ(*t, *context->get_type_mgr()->GetType(id)); + EXPECT_EQ(id, context->get_type_mgr()->GetId(t.get())); } types.clear(); @@ -1030,6 +1035,8 @@ ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 ; CHECK: [[input_ptr:%\w+]] = OpTypePointer Input [[uint]] ; CHECK: [[uniform_ptr:%\w+]] = OpTypePointer Uniform [[uint]] +; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2 +; CHECK: [[uint8:%\w+]] = OpConstant [[uint]] 8 ; CHECK: [[uint24:%\w+]] = OpConstant [[uint]] 24 ; CHECK: [[uint42:%\w+]] = OpConstant [[uint]] 42 ; CHECK: [[uint100:%\w+]] = OpConstant [[uint]] 100 @@ -1085,6 +1092,7 @@ ; CHECK: OpTypeNamedBarrier ; CHECK: OpTypeAccelerationStructureKHR ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] +; CHECK: OpTypeCooperativeMatrixKHR [[f32]] [[uint8]] [[uint8]] [[uint8]] [[uint2]] ; CHECK: OpTypeRayQueryKHR ; CHECK: OpTypeHitObjectNV OpCapability Shader @@ -1094,6 +1102,8 @@ %uint = OpTypeInt 32 0 %1 = OpTypePointer Input %uint %2 = OpTypePointer Uniform %uint +%1002 = OpConstant %uint 2 +%8 = OpConstant %uint 8 %24 = OpConstant %uint 24 %42 = OpConstant %uint 42 %100 = OpConstant %uint 100 @@ -1190,6 +1200,39 @@ Match(text, context.get()); } +// Structures containing circular type references +// (from https://github.com/KhronosGroup/SPIRV-Tools/issues/5623). +TEST(TypeManager, CircularPointerToStruct) { + const std::string text = R"( + OpCapability VariablePointers + OpCapability PhysicalStorageBufferAddresses + OpCapability Int64 + OpCapability Shader + OpExtension "SPV_KHR_variable_pointers" + OpExtension "SPV_KHR_physical_storage_buffer" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + OpExecutionMode %1 DepthReplacing + OpDecorate %1200 ArrayStride 24 + OpMemberDecorate %600 0 Offset 0 + OpMemberDecorate %800 0 Offset 0 + OpMemberDecorate %120 0 Offset 16 + OpTypeForwardPointer %1200 PhysicalStorageBuffer + %600 = OpTypeStruct %1200 + %800 = OpTypeStruct %1200 + %120 = OpTypeStruct %800 + %1200 = OpTypePointer PhysicalStorageBuffer %120 + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + uint32_t id = manager.FindPointerToType(600, spv::StorageClass::Function); + EXPECT_EQ(id, 1201); +} + } // namespace } // namespace analysis } // namespace opt
diff --git a/test/scripts/test_compact_ids.py b/test/scripts/test_compact_ids.py index 6ca6e67..b1d5387 100644 --- a/test/scripts/test_compact_ids.py +++ b/test/scripts/test_compact_ids.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2017 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License"); @@ -47,7 +47,7 @@ template= \ """{script} tests correctness of opt pass tools/opt --compact-ids -USAGE: python {script} [<spirv_files>] +USAGE: python3 {script} [<spirv_files>] Requires tools/spirv-dis, tools/spirv-as and tools/spirv-opt to be in path (call the script from the SPIRV-Tools build output directory).
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 55f8466..8e78312 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp
@@ -1247,5 +1247,58 @@ MakeInstruction(spv::Op::OpStencilAttachmentReadEXT, {1, 2, 3})}, }))); +// SPV_KHR_maximal_reconvergence + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_maximal_reconvergence, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_maximal_reconvergence\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_KHR_maximal_reconvergence"))}, + {"OpExecutionMode %1 MaximallyReconvergesKHR\n", + MakeInstruction( + spv::Op::OpExecutionMode, + {1, (uint32_t)spv::ExecutionMode::MaximallyReconvergesKHR})}, + }))); + +// SPV_KHR_float_controls2 + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_float_controls2, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_float_controls2\"\n", + MakeInstruction(spv::Op::OpExtension, + MakeVector("SPV_KHR_float_controls2"))}, + {"OpCapability FloatControls2\n", + MakeInstruction(spv::Op::OpCapability, + {(uint32_t)spv::Capability::FloatControls2})}, + {"OpExecutionMode %1 FPFastMathDefault %2 %3\n", + // The operands are: target type, flags constant + MakeInstruction( + spv::Op::OpExecutionMode, + {1, (uint32_t)spv::ExecutionMode::FPFastMathDefault, 2, 3})}, + {"OpDecorate %1 FPFastMathMode AllowContract\n", + MakeInstruction( + spv::Op::OpDecorate, + {1, (uint32_t)spv::Decoration::FPFastMathMode, + (uint32_t)spv::FPFastMathModeMask::AllowContract})}, + {"OpDecorate %1 FPFastMathMode AllowReassoc\n", + MakeInstruction( + spv::Op::OpDecorate, + {1, (uint32_t)spv::Decoration::FPFastMathMode, + (uint32_t)spv::FPFastMathModeMask::AllowReassoc})}, + {"OpDecorate %1 FPFastMathMode AllowTransform\n", + MakeInstruction( + spv::Op::OpDecorate, + {1, (uint32_t)spv::Decoration::FPFastMathMode, + (uint32_t)spv::FPFastMathModeMask::AllowTransform})}, + }))); + } // namespace } // namespace spvtools
diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp index 2a41d42..ef899a2 100644 --- a/test/text_to_binary.pipe_storage_test.cpp +++ b/test/text_to_binary.pipe_storage_test.cpp
@@ -41,7 +41,7 @@ Eq(MakeInstruction(spv::Op::OpTypePipeStorage, {1}))); EXPECT_THAT(CompileFailure("%res = OpTypePipeStorage %1 %2 %3 %4 %5", SPV_ENV_UNIVERSAL_1_1), - Eq("'=' expected after result id.")); + Eq("'=' expected after result id but found '%2'.")); } using OpConstantPipeStorageTest = spvtest::TextToBinaryTest; @@ -72,7 +72,7 @@ Eq(MakeInstruction(spv::Op::OpConstantPipeStorage, {1, 2, 3, 4, 5}))); EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4 5 %6 %7", SPV_ENV_UNIVERSAL_1_1), - Eq("'=' expected after result id.")); + Eq("'=' expected after result id but found '%7'.")); } TEST_F(OpConstantPipeStorageTest, ArgumentTypes) { @@ -118,7 +118,7 @@ Eq(MakeInstruction(spv::Op::OpCreatePipeFromPipeStorage, {1, 2, 3}))); EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 %3 %4 %5", SPV_ENV_UNIVERSAL_1_1), - Eq("'=' expected after result id.")); + Eq("'=' expected after result id but found '%5'.")); } TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentTypes) {
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 4c8989f..37fe2b9 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt
@@ -13,10 +13,10 @@ # limitations under the License. add_test(NAME spirv-tools_expect_unittests - COMMAND ${PYTHON_EXECUTABLE} -m unittest expect_unittest.py + COMMAND Python3::Interpreter -m unittest expect_unittest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_test(NAME spirv-tools_spirv_test_framework_unittests - COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py + COMMAND Python3::Interpreter -m unittest spirv_test_framework_unittest.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_spvtools_unittest(
diff --git a/test/tools/opt/CMakeLists.txt b/test/tools/opt/CMakeLists.txt index 21aa247..966ffbb 100644 --- a/test/tools/opt/CMakeLists.txt +++ b/test/tools/opt/CMakeLists.txt
@@ -13,9 +13,9 @@ # limitations under the License. if(NOT ${SPIRV_SKIP_TESTS}) - if(${PYTHONINTERP_FOUND}) + if(${Python3_Interpreter_FOUND}) add_test(NAME spirv_opt_cli_tools_tests - COMMAND ${PYTHON_EXECUTABLE} + COMMAND Python3::Interpreter ${CMAKE_CURRENT_SOURCE_DIR}/../spirv_test_framework.py $<TARGET_FILE:spirv-opt> $<TARGET_FILE:spirv-as> $<TARGET_FILE:spirv-dis> --test-dir ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/test/unit_spirv.h b/test/unit_spirv.h index bc9857e..9e7074c 100644 --- a/test/unit_spirv.h +++ b/test/unit_spirv.h
@@ -202,9 +202,8 @@ // Returns the capabilities in a CapabilitySet as an ordered vector. inline std::vector<spv::Capability> ElementsIn( const spvtools::CapabilitySet& capabilities) { - std::vector<spv::Capability> result; - capabilities.ForEach([&result](spv::Capability c) { result.push_back(c); }); - return result; + return std::vector<spv::Capability>(capabilities.cbegin(), + capabilities.cend()); } } // namespace spvtest
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 62d93bd..9d6f6ea 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt
@@ -46,6 +46,7 @@ val_extension_spv_khr_bit_instructions_test.cpp 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_ext_inst_test.cpp val_ext_inst_debug_test.cpp ${VAL_TEST_COMMON_SRCS}
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp index 9f85a30..97dde2d 100644 --- a/test/val/val_annotation_test.cpp +++ b/test/val/val_annotation_test.cpp
@@ -65,6 +65,171 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(DecorationTest, FPFastMathModeInvalidMask) { + const std::string text = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode !524288 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%undef = OpUndef %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%add = OpFAdd %float %undef %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid floating-point fast math mode operand")); +} + +TEST_F(DecorationTest, FPFastMathModeAllowTransformMissingAllowContract) { + const std::string text = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode AllowTransform|AllowReassoc +%void = OpTypeVoid +%float = OpTypeFloat 32 +%undef = OpUndef %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%add = OpFAdd %float %undef %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AllowReassoc and AllowContract must be specified when " + "AllowTransform is specified")); +} + +TEST_F(DecorationTest, FPFastMathModeAllowTransformMissingAllowReassoc) { + const std::string text = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode AllowTransform|AllowContract +%void = OpTypeVoid +%float = OpTypeFloat 32 +%undef = OpUndef %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%add = OpFAdd %float %undef %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AllowReassoc and AllowContract must be specified when " + "AllowTransform is specified")); +} + +TEST_F(DecorationTest, FPFastMathModeAllowTransformMissingContractAndReassoc) { + const std::string text = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode AllowTransform +%void = OpTypeVoid +%float = OpTypeFloat 32 +%undef = OpUndef %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%add = OpFAdd %float %undef %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AllowReassoc and AllowContract must be specified when " + "AllowTransform is specified")); +} + +TEST_F(DecorationTest, FPFastMathModeAndNoContraction) { + const std::string text = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode None +OpDecorate %add NoContraction +%void = OpTypeVoid +%float = OpTypeFloat 32 +%undef = OpUndef %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%add = OpFAdd %float %undef %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "FPFastMathMode and NoContraction cannot decorate the same target")); +} + +TEST_F(DecorationTest, FPFastMathModeAndNoContraction2) { + const std::string text = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add NoContraction +OpDecorate %add FPFastMathMode None +%void = OpTypeVoid +%float = OpTypeFloat 32 +%undef = OpUndef %float +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%add = OpFAdd %float %undef %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "FPFastMathMode and NoContraction cannot decorate the same target")); +} + using MemberOnlyDecorations = spvtest::ValidateBase<std::string>; TEST_P(MemberOnlyDecorations, MemberDecoration) {
diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp index 631375e..58ac442 100644 --- a/test/val/val_arithmetics_test.cpp +++ b/test/val/val_arithmetics_test.cpp
@@ -1318,7 +1318,7 @@ CompileSuccessfully(GenerateCoopMatCode(types, "").c_str()); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpTypeCooperativeMatrixNV Component Type <id> " + HasSubstr("OpTypeCooperativeMatrix Component Type <id> " "'4[%bool]' is not a scalar numerical type.")); } @@ -1331,7 +1331,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpTypeCooperativeMatrixNV Scope <id> '17[%float_1]' is not a " + HasSubstr("OpTypeCooperativeMatrix Scope <id> '17[%float_1]' is not a " "constant instruction with scalar integer type.")); } @@ -1344,7 +1344,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpTypeCooperativeMatrixNV Rows <id> '17[%float_1]' is not a " + HasSubstr("OpTypeCooperativeMatrix Rows <id> '17[%float_1]' is not a " "constant instruction with scalar integer type.")); } @@ -1357,7 +1357,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("OpTypeCooperativeMatrixNV Cols <id> '17[%float_1]' is not a " + HasSubstr("OpTypeCooperativeMatrix Cols <id> '17[%float_1]' is not a " "constant instruction with scalar integer type.")); } @@ -1469,6 +1469,149 @@ "SMulExtended")); } +std::string GenerateCoopMatKHRCode(const std::string& extra_types, + const std::string& main_body) { + const std::string prefix = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +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_16 = OpConstant %u32 16 +%u32_4 = OpConstant %u32 4 +%subgroup = OpConstant %u32 3 +%useA = OpConstant %u32 0 +%useB = OpConstant %u32 1 +%useC = OpConstant %u32 2 + +%f16matA = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useA +%u32matA = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_16 %u32_16 %useA +%s32matA = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_16 %u32_16 %useA + +%f16matB = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useB +%u32matB = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_16 %u32_16 %useB +%s32matB = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_16 %u32_16 %useB + +%f16matC = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useC +%f32matC = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_16 %u32_16 %useC +%u32matC = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_16 %u32_16 %useC +%s32matC = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_16 %u32_16 %useC + +%f16_1 = OpConstant %f16 1 +%f32_1 = OpConstant %f32 1 +%u32_1 = OpConstant %u32 1 +%s32_1 = OpConstant %s32 1 + +%f16mat_A_1 = OpConstantComposite %f16matA %f16_1 +%u32mat_A_1 = OpConstantComposite %u32matA %u32_1 +%s32mat_A_1 = OpConstantComposite %s32matA %s32_1 + +%f16mat_B_1 = OpConstantComposite %f16matB %f16_1 +%u32mat_B_1 = OpConstantComposite %u32matB %u32_1 +%s32mat_B_1 = OpConstantComposite %s32matB %s32_1 + +%f16mat_C_1 = OpConstantComposite %f16matC %f16_1 +%u32mat_C_1 = OpConstantComposite %u32matC %u32_1 +%s32mat_C_1 = OpConstantComposite %s32matC %s32_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, CoopMatKHRSuccess) { + const std::string body = R"( +%val1 = OpFAdd %f16matA %f16mat_A_1 %f16mat_A_1 +%val2 = OpFSub %f16matA %f16mat_A_1 %f16mat_A_1 +%val3 = OpFMul %f16matA %f16mat_A_1 %f16mat_A_1 +%val4 = OpFDiv %f16matA %f16mat_A_1 %f16mat_A_1 +%val5 = OpFNegate %f16matA %f16mat_A_1 +%val6 = OpIAdd %u32matA %u32mat_A_1 %u32mat_A_1 +%val7 = OpISub %u32matA %u32mat_A_1 %u32mat_A_1 +%val8 = OpUDiv %u32matA %u32mat_A_1 %u32mat_A_1 +%val9 = OpIAdd %s32matA %s32mat_A_1 %s32mat_A_1 +%val10 = OpISub %s32matA %s32mat_A_1 %s32mat_A_1 +%val11 = OpSDiv %s32matA %s32mat_A_1 %s32mat_A_1 +%val12 = OpSNegate %s32matA %s32mat_A_1 +%val13 = OpMatrixTimesScalar %f16matA %f16mat_A_1 %f16_1 +%val14 = OpMatrixTimesScalar %u32matA %u32mat_A_1 %u32_1 +%val15 = OpMatrixTimesScalar %s32matA %s32mat_A_1 %s32_1 +%val16 = OpCooperativeMatrixMulAddKHR %f32matC %f16mat_A_1 %f16mat_B_1 %f16mat_C_1 +%val17 = OpCooperativeMatrixMulAddKHR %s32matC %s32mat_A_1 %s32mat_B_1 %s32mat_C_1 + MatrixASignedComponentsKHR|MatrixBSignedComponentsKHR|MatrixCSignedComponentsKHR|MatrixResultSignedComponentsKHR +%val18 = OpCooperativeMatrixMulAddKHR %u32matC %u32mat_A_1 %u32mat_B_1 %u32mat_C_1 +)"; + + CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, CoopMatMatrixKHRTimesScalarMismatchFail) { + const std::string body = R"( +%val1 = OpMatrixTimesScalar %f16matA %f16mat_A_1 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopMatKHRCode("", 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 matrix operand: MatrixTimesScalar")); +} + +TEST_F(ValidateArithmetics, CoopMatKHRScopeFail) { + const std::string types = R"( +%workgroup = OpConstant %u32 2 +%mat16x16_wg = OpTypeCooperativeMatrixKHR %f16 %workgroup %u32_16 %u32_16 %useC +%f16matwg_16x16_1 = OpConstantComposite %mat16x16_wg %f16_1 +)"; + + const std::string body = R"( +%val1 = OpFAdd %f16matA %f16matwg_16x16_1 %f16mat_A_1 +)"; + + CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scopes of Matrix and Result Type to be identical")); +} + +TEST_F(ValidateArithmetics, CoopMatKHRDimFail) { + const std::string types = R"( +%mat16x4 = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_4 %useC +%mat16x4_C_1 = OpConstantComposite %mat16x4 %f16_1 +)"; + + const std::string body = R"( +%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()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cooperative matrix 'N' mismatch: CooperativeMatrixMulAddKHR")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index b266ad6..0f65634 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp
@@ -318,7 +318,8 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: " - "AtomicFloat32AddEXT AtomicFloat64AddEXT AtomicFloat16AddEXT")); + "AtomicFloat16VectorNV AtomicFloat32AddEXT AtomicFloat64AddEXT " + "AtomicFloat16AddEXT")); } TEST_F(ValidateAtomics, AtomicMinFloatVulkan) { @@ -331,7 +332,8 @@ EXPECT_THAT( getDiagnosticString(), HasSubstr("Opcode AtomicFMinEXT requires one of these capabilities: " - "AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT")); + "AtomicFloat16VectorNV AtomicFloat32MinMaxEXT " + "AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT")); } TEST_F(ValidateAtomics, AtomicMaxFloatVulkan) { @@ -343,8 +345,10 @@ ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Opcode AtomicFMaxEXT requires one of these capabilities: " - "AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT")); + HasSubstr( + "Opcode AtomicFMaxEXT requires one of these capabilities: " + "AtomicFloat16VectorNV AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT " + "AtomicFloat16MinMaxEXT")); } TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType1) { @@ -2713,6 +2717,136 @@ "value of type Result Type")); } +TEST_F(ValidateAtomics, AtomicFloat16VectorSuccess) { + const std::string definitions = R"( +%f16 = OpTypeFloat 16 +%f16vec2 = OpTypeVector %f16 2 +%f16vec4 = OpTypeVector %f16 4 + +%f16_1 = OpConstant %f16 1 +%f16vec2_1 = OpConstantComposite %f16vec2 %f16_1 %f16_1 +%f16vec4_1 = OpConstantComposite %f16vec4 %f16_1 %f16_1 %f16_1 %f16_1 + +%f16vec2_ptr = OpTypePointer Workgroup %f16vec2 +%f16vec4_ptr = OpTypePointer Workgroup %f16vec4 +%f16vec2_var = OpVariable %f16vec2_ptr Workgroup +%f16vec4_var = OpVariable %f16vec4_ptr Workgroup +)"; + + const std::string body = R"( +%val3 = OpAtomicFMinEXT %f16vec2 %f16vec2_var %device %relaxed %f16vec2_1 +%val4 = OpAtomicFMaxEXT %f16vec2 %f16vec2_var %device %relaxed %f16vec2_1 +%val8 = OpAtomicFAddEXT %f16vec2 %f16vec2_var %device %relaxed %f16vec2_1 +%val9 = OpAtomicExchange %f16vec2 %f16vec2_var %device %relaxed %f16vec2_1 + +%val11 = OpAtomicFMinEXT %f16vec4 %f16vec4_var %device %relaxed %f16vec4_1 +%val12 = OpAtomicFMaxEXT %f16vec4 %f16vec4_var %device %relaxed %f16vec4_1 +%val18 = OpAtomicFAddEXT %f16vec4 %f16vec4_var %device %relaxed %f16vec4_1 +%val19 = OpAtomicExchange %f16vec4 %f16vec4_var %device %relaxed %f16vec4_1 + +)"; + + CompileSuccessfully(GenerateShaderComputeCode( + body, + "OpCapability Float16\n" + "OpCapability AtomicFloat16VectorNV\n" + "OpExtension \"SPV_NV_shader_atomic_fp16_vector\"\n", + definitions), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +static constexpr char Float16Vector3Defs[] = R"( +%f16 = OpTypeFloat 16 +%f16vec3 = OpTypeVector %f16 3 + +%f16_1 = OpConstant %f16 1 +%f16vec3_1 = OpConstantComposite %f16vec3 %f16_1 %f16_1 %f16_1 + +%f16vec3_ptr = OpTypePointer Workgroup %f16vec3 +%f16vec3_var = OpVariable %f16vec3_ptr Workgroup +)"; + +TEST_F(ValidateAtomics, AtomicFloat16Vector3MinFail) { + const std::string definitions = Float16Vector3Defs; + + const std::string body = R"( +%val11 = OpAtomicFMinEXT %f16vec3 %f16vec3_var %device %relaxed %f16vec3_1 +)"; + + CompileSuccessfully(GenerateShaderComputeCode( + body, + "OpCapability Float16\n" + "OpCapability AtomicFloat16VectorNV\n" + "OpExtension \"SPV_NV_shader_atomic_fp16_vector\"\n", + definitions), + SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFMinEXT: expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicFloat16Vector3MaxFail) { + const std::string definitions = Float16Vector3Defs; + + const std::string body = R"( +%val12 = OpAtomicFMaxEXT %f16vec3 %f16vec3_var %device %relaxed %f16vec3_1 +)"; + + CompileSuccessfully(GenerateShaderComputeCode( + body, + "OpCapability Float16\n" + "OpCapability AtomicFloat16VectorNV\n" + "OpExtension \"SPV_NV_shader_atomic_fp16_vector\"\n", + definitions), + SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFMaxEXT: expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicFloat16Vector3AddFail) { + const std::string definitions = Float16Vector3Defs; + + const std::string body = R"( +%val18 = OpAtomicFAddEXT %f16vec3 %f16vec3_var %device %relaxed %f16vec3_1 +)"; + + CompileSuccessfully(GenerateShaderComputeCode( + body, + "OpCapability Float16\n" + "OpCapability AtomicFloat16VectorNV\n" + "OpExtension \"SPV_NV_shader_atomic_fp16_vector\"\n", + definitions), + SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFAddEXT: expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicFloat16Vector3ExchangeFail) { + const std::string definitions = Float16Vector3Defs; + + const std::string body = R"( +%val19 = OpAtomicExchange %f16vec3 %f16vec3_var %device %relaxed %f16vec3_1 +)"; + + CompileSuccessfully(GenerateShaderComputeCode( + body, + "OpCapability Float16\n" + "OpCapability AtomicFloat16VectorNV\n" + "OpExtension \"SPV_NV_shader_atomic_fp16_vector\"\n", + definitions), + SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicExchange: expected Result Type to be integer or " + "float scalar type")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp index f160904..ba8ac7d 100644 --- a/test/val/val_barriers_test.cpp +++ b/test/val/val_barriers_test.cpp
@@ -410,7 +410,7 @@ TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeMemoryFailure) { const std::string body = R"( -OpControlBarrier %subgroup %workgroup %acquire +OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); @@ -427,7 +427,7 @@ TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeExecutionFailure) { const std::string body = R"( -OpControlBarrier %workgroup %subgroup %acquire +OpControlBarrier %workgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); @@ -442,7 +442,7 @@ TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) { const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire +OpControlBarrier %workgroup %workgroup %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); @@ -451,7 +451,7 @@ TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) { const std::string body = R"( -OpControlBarrier %subgroup %subgroup %acquire +OpControlBarrier %subgroup %subgroup %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); @@ -495,15 +495,15 @@ "AcquireRelease or SequentiallyConsistent")); } -// TODO(atgoo@github.com): the corresponding check fails Vulkan CTS, -// reenable once fixed. -TEST_F(ValidateBarriers, DISABLED_OpControlBarrierVulkanSubgroupStorageClass) { +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( @@ -513,7 +513,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p1) { const std::string body = R"( -OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), @@ -523,7 +523,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionFragment1p1) { const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), @@ -541,7 +541,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p0) { const std::string body = R"( -OpControlBarrier %subgroup %workgroup %acquire_release +OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), @@ -556,7 +556,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p1) { const std::string body = R"( -OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), @@ -566,7 +566,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionVertex1p1) { const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), @@ -584,7 +584,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p0) { const std::string body = R"( -OpControlBarrier %subgroup %workgroup %acquire_release +OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), @@ -599,7 +599,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p1) { const std::string body = R"( -OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully( @@ -610,7 +610,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionGeometry1p1) { const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully( @@ -629,7 +629,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p0) { const std::string body = R"( -OpControlBarrier %subgroup %workgroup %acquire_release +OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully( @@ -646,7 +646,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionTessellationEvaluation1p1) { const std::string body = R"( -OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", @@ -658,7 +658,7 @@ TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionTessellationEvaluation1p1) { const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", @@ -678,7 +678,7 @@ TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionTessellationEvaluation1p0) { const std::string body = R"( -OpControlBarrier %subgroup %workgroup %acquire_release +OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n",
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 4f9fc97..0104969 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp
@@ -4261,6 +4261,260 @@ Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 3-component 32-bit float vector")))); +std::string GenerateMeshShadingCode(const std::string& built_in, + const std::string& execution_mode, + const std::string& body, + const std::string& declarations = "") { + std::ostringstream ss; + ss << R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 1 +OpExecutionMode %main OutputPrimitivesEXT 16 +)"; + ss << "OpExecutionMode %main " << execution_mode << "\n"; + ss << "OpDecorate %var BuiltIn " << built_in << "\n"; + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%v2uint = OpTypeVector %uint 2 +%v3uint = OpTypeVector %uint 3 + +%int_0 = OpConstant %int 0 +%uint_16 = OpConstant %uint 16 +)"; + + ss << declarations; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + return ss.str(); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTSuccess) { + 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", + "OutputTrianglesEXT", body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTStorageClass) { + const std::string declarations = R"( +%array = OpTypeArray %v3uint %uint_16 +%array_ptr = OpTypePointer Input %array +%var = OpVariable %array_ptr Input +%ptr = OpTypePointer Input %v3uint +)"; + const std::string body = R"( +%access = OpAccessChain %ptr %var %int_0 +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveTriangleIndicesEXT", + "OutputTrianglesEXT", body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07055")); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTVectorSize) { + 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("PrimitiveTriangleIndicesEXT", + "OutputTrianglesEXT", body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07056")); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTNonArray) { + const std::string declarations = R"( +%ptr = OpTypePointer Output %v3uint +%var = OpVariable %ptr Output +)"; + const std::string body = R"( +%load = OpLoad %v3uint %var +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveTriangleIndicesEXT", + "OutputTrianglesEXT", body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07056")); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTSuccess) { + 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", "OutputLinesEXT", body, + declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTStorageClass) { + const std::string declarations = R"( +%array = OpTypeArray %v2uint %uint_16 +%array_ptr = OpTypePointer Input %array +%var = OpVariable %array_ptr Input +%ptr = OpTypePointer Input %v2uint +)"; + const std::string body = R"( +%access = OpAccessChain %ptr %var %int_0 +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveLineIndicesEXT", "OutputLinesEXT", 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-07049")); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTType) { + const std::string declarations = R"( +%array = OpTypeArray %v3uint %uint_16 +%array_ptr = OpTypePointer Input %array +%var = OpVariable %array_ptr Input +%ptr = OpTypePointer Input %v3uint +)"; + const std::string body = R"( +%access = OpAccessChain %ptr %var %int_0 +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveLineIndicesEXT", "OutputLinesEXT", 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-07050")); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTSuccess) { + 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", "OutputPoints", body, + declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTStorageClass) { + const std::string declarations = R"( +%array = OpTypeArray %uint %uint_16 +%array_ptr = OpTypePointer Input %array +%var = OpVariable %array_ptr Input +%ptr = OpTypePointer Input %uint +)"; + const std::string body = R"( +%access = OpAccessChain %ptr %var %int_0 +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitivePointIndicesEXT", "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-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043")); +} + +TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTType) { + 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("PrimitivePointIndicesEXT", "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-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 3953057..233aee6 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp
@@ -4803,6 +4803,321 @@ "via a structured exit")); } +TEST_F(ValidateCFG, + MaximalReconvergenceBranchConditionalSameTargetNotInCallTree) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +OpBranchConditional %cond %func_exit %func_exit +%func_exit = OpLabel +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateCFG, MaximalReconvergenceBranchConditionalSameTargetInCallTree) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +OpBranchConditional %cond %func_exit %func_exit +%func_exit = OpLabel +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +%call = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In entry points using the MaximallyReconvergesKHR " + "execution mode, True " + "Label and False Label must be different labels")); +} + +TEST_F(ValidateCFG, MaximalReconvergenceEarlyReconvergenceNotInCallTree) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +OpSelectionMerge %func_exit None +OpBranchConditional %cond %then %else +%then = OpLabel +OpBranch %merge +%else = OpLabel +OpBranch %merge +%merge = OpLabel +OpBranch %func_exit +%func_exit = OpLabel +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MaximalReconvergenceEarlyReconvergenceInCallTree) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +OpSelectionMerge %func_exit None +OpBranchConditional %cond %then %else +%then = OpLabel +OpBranch %merge +%else = OpLabel +OpBranch %merge +%merge = OpLabel +OpBranch %func_exit +%func_exit = OpLabel +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +%call = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In entry points using the MaximallyReconvergesKHR execution mode, " + "this basic block must not have multiple unique predecessors")); +} + +TEST_F(ValidateCFG, MaximalReconvergenceLoopMultiplePredsOk) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %merge %loop None +OpBranchConditional %cond %loop %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MaximalReconvergenceLoopMultiplePredsOk2) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %merge %cont None +OpBranch %body +%body = OpLabel +OpBranch %cont +%cont = OpLabel +OpBranchConditional %cond %loop %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MaximalReconvergenceSelectionMergeMultiplePredsOk) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %cond %then %else +%then = OpLabel +OpBranch %merge +%else = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MaximalReconvergenceSelectionMergeMultiplePredsOk2) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +OpName %merge "merge" +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %cond %then %else +%then = OpLabel +OpBranch %merge +%else = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MaximalReconvergenceLoopMergeMultiplePredsOk) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %cond %merge %continue +%continue = OpLabel +OpBranchConditional %cond %loop %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MaximalReconvergenceCaseFallthroughMultiplePredsOk) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_maximal_reconvergence" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%bool = OpTypeBool +%cond = OpUndef %bool +%int = OpTypeInt 32 0 +%val = OpUndef %int +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %val %merge 0 %case1 1 %case2 +%case1 = OpLabel +OpBranch %case2 +%case2 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp index 0fd1ed6..6e0d7c0 100644 --- a/test/val/val_composites_test.cpp +++ b/test/val/val_composites_test.cpp
@@ -1486,8 +1486,7 @@ } TEST_F(ValidateComposites, CoopMatConstantCompositeMismatchFail) { - const std::string body = - R"( + const std::string body = R"( OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixNV @@ -1525,8 +1524,7 @@ } TEST_F(ValidateComposites, CoopMatCompositeConstructMismatchFail) { - const std::string body = - R"( + const std::string body = R"( OpCapability Shader OpCapability Float16 OpCapability CooperativeMatrixNV @@ -1562,6 +1560,86 @@ HasSubstr("Expected Constituent type to be equal to the component type")); } +TEST_F(ValidateComposites, CoopMatKHRConstantCompositeMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +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 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useA + +%f32_1 = OpConstant %f32 1 + +%f16mat_1 = OpConstantComposite %f16mat %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> '12[%float_1]' type " + "does not match the Result Type <id> '11[%11]'s component type.")); +} + +TEST_F(ValidateComposites, CoopMatKHRCompositeConstructMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +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 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_16 %u32_16 %useA + +%f32_1 = OpConstant %f32 1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%f16mat_1 = OpCompositeConstruct %f16mat %f32_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Constituent type to be equal to the component type")); +} + TEST_F(ValidateComposites, ExtractDynamicLabelIndex) { const std::string spirv = R"( OpCapability Shader
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp index 1f8c426..0128aa1 100644 --- a/test/val/val_conversion_test.cpp +++ b/test/val/val_conversion_test.cpp
@@ -1149,8 +1149,7 @@ } TEST_F(ValidateConversion, CoopMatConversionShapesMismatchPass) { - const std::string body = - R"( + const std::string body = R"( OpCapability Shader OpCapability Float16 OpCapability Int16 @@ -1191,6 +1190,179 @@ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateConversion, CoopMatKHRConversionSuccess) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +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 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A +%f32mat = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_8 %u32_8 %use_A +%u16mat = OpTypeCooperativeMatrixKHR %u16 %subgroup %u32_8 %u32_8 %use_A +%u32mat = OpTypeCooperativeMatrixKHR %u32 %subgroup %u32_8 %u32_8 %use_A +%s16mat = OpTypeCooperativeMatrixKHR %s16 %subgroup %u32_8 %u32_8 %use_A +%s32mat = OpTypeCooperativeMatrixKHR %s32 %subgroup %u32_8 %u32_8 %use_A + +%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 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 +%f32mat_1 = OpConstantComposite %f32mat %f32_1 +%u16mat_1 = OpConstantComposite %u16mat %u16_1 +%u32mat_1 = OpConstantComposite %u32mat %u32_1 +%s16mat_1 = OpConstantComposite %s16mat %s16_1 +%s32mat_1 = OpConstantComposite %s32mat %s32_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val11 = OpConvertFToU %u16mat %f16mat_1 +%val12 = OpConvertFToU %u32mat %f16mat_1 +%val13 = OpConvertFToS %s16mat %f16mat_1 +%val14 = OpConvertFToS %s32mat %f16mat_1 +%val15 = OpFConvert %f32mat %f16mat_1 + +%val21 = OpConvertFToU %u16mat %f32mat_1 +%val22 = OpConvertFToU %u32mat %f32mat_1 +%val23 = OpConvertFToS %s16mat %f32mat_1 +%val24 = OpConvertFToS %s32mat %f32mat_1 +%val25 = OpFConvert %f16mat %f32mat_1 + +%val31 = OpConvertUToF %f16mat %u16mat_1 +%val32 = OpConvertUToF %f32mat %u16mat_1 +%val33 = OpUConvert %u32mat %u16mat_1 +%val34 = OpSConvert %s32mat %u16mat_1 + +%val41 = OpConvertSToF %f16mat %s16mat_1 +%val42 = OpConvertSToF %f32mat %s16mat_1 +%val43 = OpUConvert %u32mat %s16mat_1 +%val44 = OpSConvert %s32mat %s16mat_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, CoopMatKHRConversionUseMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +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 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A +%f32mat = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_8 %u32_8 %use_B + +%f16_1 = OpConstant %f16 1 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val1 = OpFConvert %f32mat %f16mat_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Use of Matrix type and Result Type to be identical")); +} + +TEST_F(ValidateConversion, CoopMatKHRConversionScopeMismatchFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +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 +%workgroup = OpConstant %u32 2 +%use_A = OpConstant %u32 0 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A +%f32mat = OpTypeCooperativeMatrixKHR %f32 %workgroup %u32_8 %u32_8 %use_A + +%f16_1 = OpConstant %f16 1 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val1 = OpFConvert %f32mat %f16mat_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scopes of Matrix and Result Type to be identical")); +} + TEST_F(ValidateConversion, BitcastSuccess) { const std::string body = R"( %ptr = OpVariable %f32ptr_func Function
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index be16aba..ba0e959 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp
@@ -3209,6 +3209,48 @@ "statically used per shader entry point.")); } +TEST_F(ValidateDecorations, + VulkanMultiplePushConstantsSingleEntryPointInterfaceBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %func1 "func1" %pc1 %pc2 + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct +%ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + %func1 = OpFunction %void None %voidfn + %label1 = OpLabel + %access1 = OpAccessChain %ptr_float %pc1 %int_0 + %load1 = OpLoad %float %access1 + OpReturn + OpFunctionEnd + %func2 = OpFunction %void None %voidfn + %label2 = OpLabel + %access2 = OpAccessChain %ptr_float %pc2 %int_0 + %load2 = OpLoad %float %access2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpVariable-06673")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has more than one variable with the " + "PushConstant storage class in the interface")); +} + TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) { std::string spirv = R"( OpCapability Shader @@ -8003,6 +8045,7 @@ OpCapability Shader OpCapability Float16 OpCapability Int16 + OpCapability WorkgroupMemoryExplicitLayoutKHR OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR OpExtension "SPV_KHR_workgroup_memory_explicit_layout" OpMemoryModel Logical GLSL450 @@ -8265,6 +8308,37 @@ "member 0 at offset 1 is not aligned to 4")); } +TEST_F(ValidateDecorations, WorkgroupBlockNoCapability) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 1 Offset 4 + OpDecorate %struct Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %struct = OpTypeStruct %int %int +%ptr_workgroup = OpTypePointer Workgroup %struct + %_ = OpVariable %ptr_workgroup Workgroup + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Workgroup Storage Class variables can't be decorated with Block " + "unless declaring the WorkgroupMemoryExplicitLayoutKHR capability")); +} + TEST_F(ValidateDecorations, BadMatrixStrideUniform) { const std::string spirv = R"( OpCapability Shader @@ -9252,6 +9326,124 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateDecorations, PhysicalStorageBufferWithOffset) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" %pc +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %pc_block Block +OpMemberDecorate %pc_block 0 Offset 0 +OpMemberDecorate %pssbo_struct 0 Offset 0 +%void = OpTypeVoid +%long = OpTypeInt 64 0 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%pc_block = OpTypeStruct %long +%pc_block_ptr = OpTypePointer PushConstant %pc_block +%pc_long_ptr = OpTypePointer PushConstant %long +%pc = OpVariable %pc_block_ptr PushConstant +%pssbo_struct = OpTypeStruct %float +%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0 +%addr = OpLoad %long %pc_gep +%ptr = OpConvertUToPtr %pssbo_ptr %addr +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + +TEST_F(ValidateDecorations, PhysicalStorageBufferMissingOffset) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" %pc +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %pc_block Block +OpMemberDecorate %pc_block 0 Offset 0 +%void = OpTypeVoid +%long = OpTypeInt 64 0 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%pc_block = OpTypeStruct %long +%pc_block_ptr = OpTypePointer PushConstant %pc_block +%pc_long_ptr = OpTypePointer PushConstant %long +%pc = OpVariable %pc_block_ptr PushConstant +%pssbo_struct = OpTypeStruct %float +%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0 +%addr = OpLoad %long %pc_gep +%ptr = OpConvertUToPtr %pssbo_ptr %addr +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("decorated as Block for variable in PhysicalStorageBuffer " + "storage class must follow relaxed storage buffer layout " + "rules: member 0 is missing an Offset decoration")); +} + +TEST_F(ValidateDecorations, PhysicalStorageBufferMissingArrayStride) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" %pc +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %pc_block Block +OpMemberDecorate %pc_block 0 Offset 0 +%void = OpTypeVoid +%long = OpTypeInt 64 0 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%pc_block = OpTypeStruct %long +%pc_block_ptr = OpTypePointer PushConstant %pc_block +%pc_long_ptr = OpTypePointer PushConstant %long +%pc = OpVariable %pc_block_ptr PushConstant +%pssbo_array = OpTypeArray %float %int_4 +%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_array +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0 +%addr = OpLoad %long %pc_gep +%ptr = OpConvertUToPtr %pssbo_ptr %addr +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( + "decorated as Block for variable in PhysicalStorageBuffer storage " + "class must follow relaxed storage buffer layout rules: member 0 " + "contains an array with stride 0, but with an element size of 4")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp index 554e78b..8f0da42 100644 --- a/test/val/val_ext_inst_debug_test.cpp +++ b/test/val/val_ext_inst_debug_test.cpp
@@ -1012,9 +1012,9 @@ CompileSuccessfully(GenerateShaderCodeForDebugInfo( src, size_const, dbg_inst_header, "", extension, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug type")); } TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { @@ -1077,9 +1077,9 @@ CompileSuccessfully(GenerateShaderCodeForDebugInfo( src, size_const, dbg_inst_header, "", extension, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug type")); } TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifier) { const std::string src = R"( @@ -1147,9 +1147,9 @@ CompileSuccessfully(GenerateShaderCodeForDebugInfo( src, constants, dbg_inst_header, "", extension, "Vertex")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected operand Base Type must be a result id of " - "DebugTypeBasic")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug type")); } TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) {
diff --git a/test/val/val_extension_spv_nv_raw_access_chains.cpp b/test/val/val_extension_spv_nv_raw_access_chains.cpp new file mode 100644 index 0000000..f06d7cd --- /dev/null +++ b/test/val/val_extension_spv_nv_raw_access_chains.cpp
@@ -0,0 +1,510 @@ +// Copyright (c) 2024 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 "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 ValidateSpvNVRawAccessChains = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvNVRawAccessChains, Valid) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %int_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvNVRawAccessChains, NoCapability) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %int_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("requires one of these capabilities: RawAccessChainsNV")); +} + +TEST_F(ValidateSpvNVRawAccessChains, NoExtension) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %int_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("requires one of these extensions: SPV_NV_raw_access_chains")); +} + +TEST_F(ValidateSpvNVRawAccessChains, ReturnTypeNotPointer) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %int %ssbo %int_16 %int_0 %int_0 RobustnessPerComponentNV + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be OpTypePointer. Found OpTypeInt")); +} + +TEST_F(ValidateSpvNVRawAccessChains, Workgroup) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer Workgroup %intStruct + %ssbo = OpVariable %intStructPtr Workgroup + %intPtr = OpTypePointer Workgroup %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %int_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must point to a storage class of")); +} + +TEST_F(ValidateSpvNVRawAccessChains, ReturnTypeArray) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %int_1 = OpConstant %int 1 + %intArray = OpTypeArray %int %int_1 + %intArrayPtr = OpTypePointer StorageBuffer %intArray + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intArrayPtr %ssbo %int_16 %int_0 %int_0 RobustnessPerComponentNV + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must not point to")); +} + +TEST_F(ValidateSpvNVRawAccessChains, VariableStride) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %stride = OpIAdd %int %int_0 %int_0 + %rawChain = OpRawAccessChainNV %intPtr %ssbo %stride %int_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be OpConstant")); +} + +TEST_F(ValidateSpvNVRawAccessChains, RobustnessPerElementZeroStride) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_0 %int_0 %int_0 RobustnessPerElementNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Stride must not be zero when per-element robustness is used")); +} + +TEST_F(ValidateSpvNVRawAccessChains, BothRobustness) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %int_0 %int_0 RobustnessPerElementNV|RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Per-component robustness and per-element robustness " + "are mutually exclusive")); +} + +TEST_F(ValidateSpvNVRawAccessChains, StrideFloat) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %float_16 = OpConstant %float 16 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %float_16 %int_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be OpTypeInt")); +} + +TEST_F(ValidateSpvNVRawAccessChains, IndexType) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpCapability Int64 + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %long = OpTypeInt 64 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %long_0 = OpConstant %long 0 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %long_0 %int_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("The integer width of Index")); +} + +TEST_F(ValidateSpvNVRawAccessChains, OffsetType) { + const std::string str = R"( + OpCapability Shader + OpCapability RawAccessChainsNV + OpCapability Int64 + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_NV_raw_access_chains" + OpMemoryModel Logical GLSL450 + + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + OpDecorate %intStruct Block + OpMemberDecorate %intStruct 0 Offset 0 + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + + %int = OpTypeInt 32 1 + %long = OpTypeInt 64 1 + %void = OpTypeVoid + %mainFunctionType = OpTypeFunction %void + %intStruct = OpTypeStruct %int + %intStructPtr = OpTypePointer StorageBuffer %intStruct + %ssbo = OpVariable %intStructPtr StorageBuffer + %intPtr = OpTypePointer StorageBuffer %int + + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %long_0 = OpConstant %long 0 + + %main = OpFunction %void None %mainFunctionType + %label = OpLabel + %rawChain = OpRawAccessChainNV %intPtr %ssbo %int_16 %int_0 %long_0 RobustnessPerComponentNV + %unused = OpLoad %int %rawChain + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("The integer width of Offset")); +} + +} // namespace +} // namespace val +} // namespace spvtools
diff --git a/test/val/val_extensions_test.cpp b/test/val/val_extensions_test.cpp index 0ab8c6e..932bbee 100644 --- a/test/val/val_extensions_test.cpp +++ b/test/val/val_extensions_test.cpp
@@ -131,6 +131,214 @@ EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_KHR_device_group")); } +TEST_F(ValidateExtensionCapabilities, + DeclCapabilityFailureBlockMatchWIndowSAD) { + const std::string str = R"( + OpCapability Shader + OpCapability TextureBlockMatch2QCOM + 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" + OpName %main "main" + OpName %tgt_coords "tgt_coords" + OpName %v_texcoord "v_texcoord" + OpName %ref_coords "ref_coords" + OpName %blockSize "blockSize" + OpName %fragColor "fragColor" + OpName %target_samp "target_samp" + OpName %ref_samp "ref_samp" + 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()); + ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("2nd operand of Decorate")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_QCOM_image_processing")); +} + +TEST_F(ValidateExtensionCapabilities, + DeclCapabilityFailureBlockMatchWIndowSSD) { + const std::string str = R"( + OpCapability Shader + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %v_texcoord %fragColor %tex2D_src1 %samp %tex2D_src2 + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_QCOM_image_processing" + OpSourceExtension "GL_QCOM_image_processing2" + OpName %main "main" + OpName %tgt_coords "tgt_coords" + OpName %v_texcoord "v_texcoord" + OpName %ref_coords "ref_coords" + OpName %blockSize "blockSize" + OpName %fragColor "fragColor" + OpName %tex2D_src1 "tex2D_src1" + OpName %samp "samp" + OpName %tex2D_src2 "tex2D_src2" + OpDecorate %v_texcoord Location 0 + OpDecorate %fragColor Location 0 + OpDecorate %tex2D_src1 DescriptorSet 0 + OpDecorate %tex2D_src1 Binding 1 + OpDecorate %samp DescriptorSet 0 + OpDecorate %samp Binding 3 + OpDecorate %tex2D_src2 DescriptorSet 0 + OpDecorate %tex2D_src2 Binding 2 + OpDecorate %tex2D_src1 BlockMatchTextureQCOM + OpDecorate %samp BlockMatchSamplerQCOM + OpDecorate %tex2D_src2 BlockMatchTextureQCOM + OpDecorate %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 +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %tex2D_src1 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %samp = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %tex2D_src2 = OpVariable %_ptr_UniformConstant_42 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 + %45 = OpLoad %42 %tex2D_src1 + %49 = OpLoad %46 %samp + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %tgt_coords + %54 = OpLoad %42 %tex2D_src2 + %55 = OpLoad %46 %samp + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %ref_coords + %58 = OpLoad %v2uint %blockSize + %59 = OpImageBlockMatchWindowSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %fragColor %59 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("2nd operand of Decorate")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_QCOM_image_processing")); +} + using ValidateAMDShaderBallotCapabilities = spvtest::ValidateBase<std::string>; // Returns a vector of strings for the prefix of a SPIR-V assembly shader
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index 3666f38..cc29736 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp
@@ -1056,7 +1056,7 @@ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(make_message("OpTypeArray Length <id> '2[%2]' default " - "value must be at least 1."))); + "value must be at least 1: found 0"))); } TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) { @@ -2317,7 +2317,7 @@ "be used with non-externally visible shader Storage Classes: " "Workgroup, CrossWorkgroup, Private, Function, Input, Output, " "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, " - "CallableDataKHR, or IncomingCallableDataKHR"))); + "CallableDataKHR, IncomingCallableDataKHR, or UniformConstant"))); } TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) { @@ -2339,6 +2339,25 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_P(ValidateIdWithMessage, OpVariableContainsBoolUniformConstantGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%block = OpTypeStruct %bool %int +%_ptr_UniformConstant_block = OpTypePointer UniformConstant %block +%var = OpVariable %_ptr_UniformConstant_block UniformConstant +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) { std::string spirv = kGLSL450MemoryModel + R"( %bool = OpTypeBool @@ -4206,7 +4225,7 @@ OpFunctionEnd )"; const std::string expected_err = "Index is out of bounds: " + instr + - " can not find index 3 into the structure " + " cannot find index 3 into the structure " "<id> '25[%_struct_25]'. This structure " "has 3 members. Largest valid index is 2."; CompileSuccessfully(spirv);
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index aa335c8..77b042f 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp
@@ -786,6 +786,21 @@ HasSubstr("Dim SubpassData requires Arrayed to be 0")); } +TEST_F(ValidateImage, TypeImageDimRectVulkan) { + const std::string code = GetShaderHeader("OpCapability InputAttachment\n") + + R"( +%img_type = OpTypeImage %f32 Rect 0 1 0 2 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + // Can't actually hit VUID-StandaloneSpirv-OpTypeImage-09638 + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("TypeImage requires one of these capabilities: SampledRect")); +} + TEST_F(ValidateImage, TypeImageWrongSampledTypeForTileImageDataEXT) { const std::string code = GetShaderHeader( "OpCapability TileImageColorReadAccessEXT\n" @@ -2177,8 +2192,6 @@ 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-04662")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets " @@ -5854,10 +5867,12 @@ %res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend )"; - EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", - SPV_ENV_UNIVERSAL_1_3), - SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION), - HasSubstr("Invalid image operand 'SignExtend'")); + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_UNIVERSAL_1_3)); + ASSERT_EQ(SPV_ERROR_WRONG_VERSION, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("SignExtend(4096) requires SPIR-V version 1.4 or later")); } TEST_F(ValidateImage, ZeroExtendV13Bad) { @@ -5866,10 +5881,12 @@ %res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend )"; - EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", - SPV_ENV_UNIVERSAL_1_3), - SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION), - HasSubstr("Invalid image operand 'ZeroExtend'")); + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_UNIVERSAL_1_3)); + ASSERT_EQ(SPV_ERROR_WRONG_VERSION, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ZeroExtend(8192) requires SPIR-V version 1.4 or later")); } TEST_F(ValidateImage, SignExtendScalarUIntTexelV14Good) { @@ -6675,6 +6692,3961 @@ HasSubstr("OpSamplerImageAddressingModeNV bitwidth should be 64 or 32")); } +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationA) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationB) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationC) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADNoDecorationD) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationA) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationB) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationC) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDNoDecorationD) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedNoDecorationA) { + std::string text = R"( + OpCapability Shader + OpCapability TextureSampleWeightedQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 %7 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 Location 0 + OpDecorate %7 DescriptorSet 0 + OpDecorate %7 Binding 0 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %13 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %4 = OpVariable %_ptr_UniformConstant_13 UniformConstant + %15 = OpTypeSampler +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 + %5 = OpVariable %_ptr_UniformConstant_15 UniformConstant + %17 = OpTypeSampledImage %13 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %6 = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %20 = OpTypeImage %float 2D 0 1 0 1 Unknown +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %7 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %22 = OpTypeSampledImage %20 +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 + %2 = OpFunction %void None %9 + %24 = OpLabel + %25 = OpLoad %13 %4 + %26 = OpLoad %15 %5 + %27 = OpSampledImage %17 %25 %26 + %28 = OpLoad %v4float %6 + %29 = OpVectorShuffle %v2float %28 %28 0 1 + %30 = OpLoad %20 %7 + %31 = OpLoad %15 %5 + %32 = OpSampledImage %22 %30 %31 + %33 = OpImageSampleWeightedQCOM %v4float %27 %29 %32 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration WeightTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedNoDecorationB) { + std::string text = R"( + OpCapability Shader + OpCapability TextureSampleWeightedQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %12 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %16 = OpTypeSampledImage %12 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %4 = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %19 = OpTypeImage %float 2D 0 1 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %21 = OpTypeSampledImage %19 +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 + %5 = OpVariable %_ptr_UniformConstant_16 UniformConstant +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %6 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpLoad %16 %5 + %26 = OpLoad %v4float %4 + %27 = OpVectorShuffle %v2float %26 %26 0 1 + %28 = OpLoad %21 %6 + %29 = OpImageSampleWeightedQCOM %v4float %25 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration WeightTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchWindowSADInvalidUseA) { + std::string text = R"( +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 11 +; Bound: 79 +; Schema: 0 + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchSADQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %102 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseB) { + std::string text = R"( +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 11 +; Bound: 79 +; Schema: 0 + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchSADQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %104 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseC) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchSADQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %5 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSADInvalidUseD) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchSADQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %6 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseA) { + std::string text = R"( +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 11 +; Bound: 79 +; Schema: 0 + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %102 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseB) { + std::string text = R"( +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 11 +; Bound: 79 +; Schema: 0 + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %104 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseC) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchSSDQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %5 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingBlockMatchSSDInvalidUseD) { + std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchSSDQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %6 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedInvalidUseA) { + std::string text = R"( + OpCapability Shader + OpCapability TextureSampleWeightedQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %6 WeightTextureQCOM + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %12 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %12 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %4 = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %17 = OpTypeImage %float 2D 0 1 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 + %19 = OpTypeSampledImage %17 +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %5 = OpVariable %_ptr_UniformConstant_14 UniformConstant +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %v3float = OpTypeVector %float 3 + %2 = OpFunction %void None %8 + %23 = OpLabel + %24 = OpLoad %v4float %4 + %25 = OpVectorShuffle %v2float %24 %24 0 1 + %26 = OpLoad %14 %5 + %27 = OpLoad %v4float %4 + %28 = OpVectorShuffle %v2float %27 %27 0 1 + %29 = OpLoad %19 %6 + %30 = OpImageSampleWeightedQCOM %v4float %26 %28 %29 + OpStore %3 %30 + %31 = OpLoad %19 %6 + %32 = OpLoad %v4float %4 + %33 = OpVectorShuffle %v3float %32 %32 0 1 0 + %34 = OpCompositeExtract %float %33 0 + %35 = OpCompositeExtract %float %33 1 + %36 = OpCompositeExtract %float %33 2 + %37 = OpCompositeConstruct %v3float %34 %35 %36 + %38 = OpImageSampleImplicitLod %v4float %31 %37 + OpStore %3 %38 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessingSampleWeightedInvalidUseB) { + std::string text = R"( + OpCapability Shader + OpCapability TextureSampleWeightedQCOM + OpExtension "SPV_QCOM_image_processing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 %7 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 1 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 3 + OpDecorate %4 Location 0 + OpDecorate %7 DescriptorSet 0 + OpDecorate %7 Binding 0 + OpDecorate %7 WeightTextureQCOM + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %13 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %5 = OpVariable %_ptr_UniformConstant_13 UniformConstant + %15 = OpTypeSampler +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 + %6 = OpVariable %_ptr_UniformConstant_15 UniformConstant + %17 = OpTypeSampledImage %13 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %4 = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %20 = OpTypeImage %float 2D 0 1 0 1 Unknown +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %7 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %22 = OpTypeSampledImage %20 + %v3float = OpTypeVector %float 3 + %2 = OpFunction %void None %9 + %24 = OpLabel + %25 = OpLoad %13 %5 + %26 = OpLoad %15 %6 + %27 = OpSampledImage %17 %25 %26 + %28 = OpLoad %v4float %4 + %29 = OpVectorShuffle %v2float %28 %28 0 1 + %30 = OpLoad %20 %7 + %31 = OpLoad %15 %6 + %32 = OpSampledImage %22 %30 %31 + %33 = OpImageSampleWeightedQCOM %v4float %27 %29 %32 + OpStore %3 %33 + %34 = OpLoad %20 %7 + %35 = OpLoad %15 %6 + %36 = OpSampledImage %22 %34 %35 + %37 = OpLoad %v4float %4 + %38 = OpVectorShuffle %v3float %37 %37 0 1 0 + %39 = OpCompositeExtract %float %38 0 + %40 = OpCompositeExtract %float %38 1 + %41 = OpCompositeExtract %float %38 2 + %42 = OpCompositeConstruct %v3float %39 %40 %41 + %43 = OpImageSampleImplicitLod %v4float %36 %42 + OpStore %3 %43 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorTargetIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorTargetIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorRefIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchSamplerQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorRefIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorTargetNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorTargetNIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorRefNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %5 BlockMatchSamplerQCOM + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADNoDecorRefNIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorTargetIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorTargetIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorRefIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchSamplerQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorRefIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchWindowSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorTargetNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorTargetNIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorRefNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %5 BlockMatchSamplerQCOM + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDNoDecorRefNIS) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %4 BlockMatchSamplerQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchWindowSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchSamplerQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSADNoDecorTargetIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchGatherSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSADNoDecorRefIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchGatherSADQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSADNoDecorTargetNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchGatherSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSADNoDecorRefNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchGatherSADQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSSDNoDecorTargetIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchGatherSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSSDNoDecorRefIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 4 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 5 + %void = OpTypeVoid + %7 = 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 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown + %19 = OpTypeSampledImage %18 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %5 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %7 + %22 = OpLabel + %23 = OpVariable %_ptr_Function_v2uint Function + %24 = OpLoad %19 %4 + %25 = OpLoad %v2uint %23 + %26 = OpLoad %19 %5 + %27 = OpLoad %v2uint %23 + %28 = OpLoad %v2uint %23 + %29 = OpImageBlockMatchGatherSSDQCOM %v4float %24 %25 %26 %27 %28 + OpStore %3 %29 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSSDNoDecorTargetNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchGatherSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSSDNoDecorRefNIT) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 1 + OpDecorate %4 BlockMatchTextureQCOM + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 3 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 2 + %void = OpTypeVoid + %8 = 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_float = OpTypePointer Input %float +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_4 = OpConstant %uint 4 + %17 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 + %4 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 + %5 = OpVariable %_ptr_UniformConstant_21 UniformConstant + %23 = OpTypeSampledImage %19 + %6 = OpVariable %_ptr_UniformConstant_19 UniformConstant + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + %26 = OpLoad %19 %4 + %27 = OpLoad %21 %5 + %28 = OpSampledImage %23 %26 %27 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %19 %6 + %31 = OpLoad %21 %5 + %32 = OpSampledImage %23 %30 %31 + %33 = OpImageBlockMatchGatherSSDQCOM %v4float %28 %29 %32 %29 %29 + OpStore %3 %33 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing decoration BlockMatchTextureQCOM")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchWindowSADInvalidUseTargetI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchWindowSADQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %5 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADInvalidUseRefI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchWindowSADQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %6 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchWindowSADInvalidUseTargetNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %103 BlockMatchSamplerQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchWindowSADQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %102 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSADInvalidUseRefNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %103 BlockMatchSamplerQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchWindowSADQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %104 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchWindowSSDInvalidUseTargetI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchWindowSSDQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %5 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDInvalidUseRefI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %5 BlockMatchSamplerQCOM + OpDecorate %6 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchSamplerQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchWindowSSDQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %6 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchWindowSSDInvalidUseTargetNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %103 BlockMatchSamplerQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchWindowSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %102 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchWindowSSDInvalidUseRefNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %103 BlockMatchSamplerQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchWindowSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %104 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchGatherSADInvalidUseTargetI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchGatherSADQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %5 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSADInvalidUseRefI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchGatherSADQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %6 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchGatherSADInvalidUseTargetNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchGatherSADQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %102 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSADInvalidUseRefNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchGatherSADQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %104 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchGatherSSDInvalidUseTargetI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchGatherSSDQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %5 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSSDInvalidUseRefI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 %5 %6 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 4 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 5 + OpDecorate %5 BlockMatchTextureQCOM + OpDecorate %6 BlockMatchTextureQCOM + %void = OpTypeVoid + %8 = 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 + %3 = OpVariable %_ptr_Input_v4float Input + %uint_4 = OpConstant %uint 4 + %16 = OpConstantComposite %v2uint %uint_4 %uint_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4 = OpVariable %_ptr_Output_v4float Output + %18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %20 = OpTypeSampledImage %18 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 + %5 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %6 = OpVariable %_ptr_UniformConstant_20 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpTypeImage %float 2D 0 1 0 1 Unknown + %2 = OpFunction %void None %8 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_v2uint Function + OpStore %25 %16 + %26 = OpLoad %20 %5 + %27 = OpLoad %v2uint %25 + %28 = OpLoad %20 %6 + %29 = OpLoad %v2uint %25 + %30 = OpLoad %v2uint %25 + %31 = OpImageBlockMatchGatherSSDQCOM %v4float %26 %27 %28 %29 %30 + OpStore %4 %31 + %32 = OpLoad %20 %6 + %33 = OpLoad %v4float %3 + %34 = OpVectorShuffle %v2float %33 %33 0 2 + %35 = OpImageSampleImplicitLod %v4float %32 %34 + OpStore %4 %35 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, + QCOMImageProcessing2BlockMatchGatherSSDInvalidUseTargetNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchGatherSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %102 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, QCOMImageProcessing2BlockMatchGatherSSDInvalidUseRefNI) { + const std::string text = R"( + OpCapability Shader + OpCapability TextureBlockMatchQCOM + OpCapability TextureBlockMatch2QCOM + OpExtension "SPV_QCOM_image_processing" + OpExtension "SPV_QCOM_image_processing2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %100 %101 %102 %103 %104 + OpExecutionMode %main OriginUpperLeft + OpDecorate %100 Location 0 + OpDecorate %101 Location 0 + OpDecorate %102 DescriptorSet 0 + OpDecorate %102 Binding 1 + OpDecorate %103 DescriptorSet 0 + OpDecorate %103 Binding 3 + OpDecorate %104 DescriptorSet 0 + OpDecorate %104 Binding 2 + OpDecorate %102 BlockMatchTextureQCOM + OpDecorate %104 BlockMatchTextureQCOM + %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 + %100 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %101 = OpVariable %_ptr_Output_v4float Output + %42 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 + %102 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %46 = OpTypeSampler +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %103 = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampledImage %42 + %104 = OpVariable %_ptr_UniformConstant_42 UniformConstant + %v2float = OpTypeVector %float 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpVariable %_ptr_Function_v2uint Function + %45 = OpLoad %42 %102 + %49 = OpLoad %46 %103 + %51 = OpSampledImage %50 %45 %49 + %52 = OpLoad %v2uint %15 + %54 = OpLoad %42 %104 + %55 = OpLoad %46 %103 + %56 = OpSampledImage %50 %54 %55 + %57 = OpLoad %v2uint %15 + %58 = OpLoad %v2uint %15 + %59 = OpImageBlockMatchGatherSSDQCOM %v4float %51 %52 %56 %57 %58 + OpStore %101 %59 + %69 = OpLoad %42 %104 + %70 = OpLoad %46 %103 + %71 = OpSampledImage %50 %69 %70 + %73 = OpLoad %v4float %100 + %74 = OpVectorShuffle %v2float %73 %73 0 0 + %75 = OpImageSampleImplicitLod %v4float %71 %74 + OpStore %101 %75 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal use of QCOM image processing decorated texture")); +} + +TEST_F(ValidateImage, ImageMSArray_ArrayedSampledTypeRequiresCapability) { + const std::string code = R"( + OpCapability Shader + OpCapability StorageImageMultisample + OpCapability StorageImageReadWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %var_image DescriptorSet 0 + OpDecorate %var_image Binding 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + %uint_2 = OpConstant %u32 2 + %uint_1 = OpConstant %u32 1 + %v2uint = OpTypeVector %u32 2 + %v4float = OpTypeVector %f32 4 + %image = OpTypeImage %f32 2D 2 1 1 2 Unknown +%ptr_image = OpTypePointer UniformConstant %image + %10 = OpConstantComposite %v2uint %uint_1 %uint_2 +%var_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %main_lab = OpLabel + %18 = OpLoad %image %var_image + %19 = OpImageRead %v4float %18 %10 Sample %uint_2 + OpReturn + OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability ImageMSArray is required to access storage image")); +} + +TEST_F(ValidateImage, ImageMSArray_SampledTypeDoesNotRequireCapability) { + const std::string code = R"( + OpCapability Shader + OpCapability StorageImageMultisample + OpCapability StorageImageReadWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %var_image DescriptorSet 0 + OpDecorate %var_image Binding 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + %uint_2 = OpConstant %u32 2 + %uint_1 = OpConstant %u32 1 + %v2uint = OpTypeVector %u32 2 + %v4float = OpTypeVector %f32 4 + %image = OpTypeImage %f32 2D 2 0 1 2 Unknown +%ptr_image = OpTypePointer UniformConstant %image + %10 = OpConstantComposite %v2uint %uint_1 %uint_2 +%var_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %main_lab = OpLabel + %18 = OpLoad %image %var_image + %19 = OpImageRead %v4float %18 %10 Sample %uint_2 + OpReturn + OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ImageMSArray_ArrayedTypeDoesNotRequireCapability) { + const std::string code = R"( + OpCapability Shader + OpCapability StorageImageReadWithoutFormat + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %var_image DescriptorSet 0 + OpDecorate %var_image Binding 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + %uint_3 = OpConstant %u32 3 + %uint_2 = OpConstant %u32 2 + %uint_1 = OpConstant %u32 1 + %v3uint = OpTypeVector %u32 3 + %v4float = OpTypeVector %f32 4 + %image = OpTypeImage %f32 2D 2 1 0 2 Unknown +%ptr_image = OpTypePointer UniformConstant %image + %10 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3 +%var_image = OpVariable %ptr_image UniformConstant + %main = OpFunction %void None %func + %main_lab = OpLabel + %18 = OpLoad %image %var_image + %19 = OpImageRead %v4float %18 %10 + OpReturn + OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index 1756528..4f62be7 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp
@@ -419,9 +419,10 @@ )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Variable has conflicting location decorations")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decorated with Location multiple times is not allowed")); } TEST_F(ValidateInterfacesTest, VulkanLocationsVariableAndMemberAssigned) { @@ -505,9 +506,10 @@ )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Member index 1 has conflicting location assignments")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decorated with Location multiple times is not allowed")); } TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentStructMember) { @@ -584,6 +586,8 @@ 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 0")); } @@ -611,6 +615,8 @@ 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 " @@ -699,6 +705,8 @@ 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")); } @@ -732,6 +740,8 @@ 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")); } @@ -762,6 +772,8 @@ 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")); } @@ -792,6 +804,8 @@ 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 2")); } @@ -822,6 +836,8 @@ 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 3")); } @@ -854,6 +870,8 @@ 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")); } @@ -886,6 +904,8 @@ 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 5")); } @@ -918,6 +938,8 @@ 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 7")); } @@ -950,6 +972,8 @@ 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")); } @@ -982,6 +1006,8 @@ 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 3")); } @@ -1016,6 +1042,8 @@ 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 15")); } @@ -1075,6 +1103,8 @@ 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, component 1")); } @@ -1129,9 +1159,10 @@ )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Variable has conflicting component decorations")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decorated with Component multiple times is not allowed")); } TEST_F(ValidateInterfacesTest, @@ -1158,10 +1189,10 @@ )"; CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Member index 0 has conflicting component assignments")); + HasSubstr("decorated with Component multiple times is not allowed")); } TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutputIndex1) { @@ -1189,6 +1220,8 @@ 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 " @@ -1359,6 +1392,8 @@ 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, component 1")); } @@ -1538,6 +1573,58 @@ "Interface struct has no Block decoration but has BuiltIn members.")); } +TEST_F(ValidateInterfacesTest, InvalidLocationTypePointer) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Vertex %1 "Aiqn0" %2 %3 + OpDecorate %2 Location 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Private_void = OpTypePointer Private %void + %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 + %1 = OpFunction %void None %5 + %15 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid type to assign a location")); +} + +TEST_F(ValidateInterfacesTest, ValidLocationTypePhysicalStorageBufferPointer) { + 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 0 +%ptr = OpTypePointer PhysicalStorageBuffer %int +%ptr2 = OpTypePointer Input %ptr +%var = OpVariable %ptr2 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_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index d575318..74a17e9 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp
@@ -23,12 +23,14 @@ #include "test/val/val_fixtures.h" // For pretty-printing tuples with spv_target_env. -std::ostream& operator<<(std::ostream& stream, spv_target_env target) -{ +std::ostream& operator<<(std::ostream& stream, spv_target_env target) { switch (target) { - case SPV_ENV_UNIVERSAL_1_3: return stream << "SPV_ENV_UNIVERSAL_1_3"; - case SPV_ENV_UNIVERSAL_1_4: return stream << "SPV_ENV_UNIVERSAL_1_4"; - default: return stream << (unsigned)target; + case SPV_ENV_UNIVERSAL_1_3: + return stream << "SPV_ENV_UNIVERSAL_1_3"; + case SPV_ENV_UNIVERSAL_1_4: + return stream << "SPV_ENV_UNIVERSAL_1_4"; + default: + return stream << (unsigned)target; } } @@ -2346,6 +2348,186 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateMemory, CoopMatKHRLoadStoreSuccess) { + std::string spirv = + GenCoopMatLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, CoopMatKHRStoreMemoryAccessFail) { + std::string spirv = + GenCoopMatLoadStoreShader("MakePointerVisibleKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR"); + + 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("MakePointerVisibleKHR cannot be used with OpStore")); +} + +TEST_F(ValidateMemory, CoopMatKHRLoadMemoryAccessFail) { + std::string spirv = + GenCoopMatLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerAvailableKHR|NonPrivatePointerKHR"); + + 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("MakePointerAvailableKHR cannot be used with OpLoad")); +} + +TEST_F(ValidateMemory, CoopMatKHRInvalidStorageClassFail) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%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 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A + +%str = OpTypeStruct %f16mat +%str_ptr = OpTypePointer Workgroup %str +%sh = OpVariable %str_ptr Workgroup + +%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( + "Cooperative matrix types (or types containing them) can only be " + "allocated in Function or Private storage classes or as function " + "parameters")); +} + +TEST_F(ValidateMemory, CoopMatMatrixKHRLengthResultTypeBad) { + const std::string body = R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%use_A = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%1 = OpCooperativeMatrixLengthKHR %i32 %f16mat + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The Result Type of OpCooperativeMatrixLengthKHR <id> " + "'12[%12]' must be OpTypeInt with width 32 and signedness 0")); +} + +TEST_F(ValidateMemory, CoopMatMatrixKHRLengthOperandTypeBad) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%use_A = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%1 = OpCooperativeMatrixLengthKHR %u32 %u32 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The type in OpCooperativeMatrixLengthKHR <id> '5[%uint]' " + "must be OpTypeCooperativeMatrixKHR")); +} + +TEST_F(ValidateMemory, CoopMatMatrixKHRLengthGood) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixKHR +OpExtension "SPV_KHR_cooperative_matrix" +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%use_A = OpConstant %u32 0 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%1 = OpCooperativeMatrixLengthKHR %u32 %f16mat + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateMemory, VulkanRTAOutsideOfStructBad) { std::string spirv = R"( OpCapability Shader @@ -3765,9 +3947,8 @@ HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); } -using ValidateSizedVariable = - spvtest::ValidateBase<std::tuple<std::string, std::string, - std::string, spv_target_env>>; +using ValidateSizedVariable = spvtest::ValidateBase< + std::tuple<std::string, std::string, std::string, spv_target_env>>; CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit, bool buffer_block) { CodeGenerator generator; @@ -3777,7 +3958,8 @@ "\"SPV_KHR_8bit_storage\"\n"; generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; if (is_8bit) { - generator.before_types_ = "OpMemberDecorate %char_buffer_block 0 Offset 0\n"; + generator.before_types_ = + "OpMemberDecorate %char_buffer_block 0 Offset 0\n"; if (buffer_block) generator.before_types_ += "OpDecorate %char_buffer_block BufferBlock\n"; @@ -4933,6 +5115,60 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateMemory, AccessChainNegativeStructIndex32) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_struct_4 = OpTypeStruct %int %int %int +%_ptr_Function__struct_4 = OpTypePointer Function %_struct_4 +%_ptr_Function_int = OpTypePointer Function %int +%int_n224 = OpConstant %int -224 +%fn = OpFunction %void Inline %void_fn +%entry = OpLabel +%var = OpVariable %_ptr_Function__struct_4 Function +%gep = OpInBoundsAccessChain %_ptr_Function_int %var %int_n224 +OpReturn +OpFunctionEnd +)"; + + 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")); +} + +TEST_F(ValidateMemory, AccessChainNegativeStructIndex64) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Int64 +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 1 +%long = OpTypeInt 64 1 +%_struct_4 = OpTypeStruct %int %int %int +%_ptr_Function__struct_4 = OpTypePointer Function %_struct_4 +%_ptr_Function_int = OpTypePointer Function %int +%long_n224 = OpConstant %long -224 +%fn = OpFunction %void Inline %void_fn +%entry = OpLabel +%var = OpVariable %_ptr_Function__struct_4 Function +%gep = OpInBoundsAccessChain %_ptr_Function_int %var %long_n224 +OpReturn +OpFunctionEnd +)"; + + 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")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 8dc0fbc..83a0503 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp
@@ -1027,6 +1027,162 @@ "constant instructions.")); } +using AllowMultipleExecutionModes = spvtest::ValidateBase<std::string>; + +TEST_P(AllowMultipleExecutionModes, DifferentOperand) { + const std::string mode = GetParam(); + const std::string spirv = R"( +OpCapability Shader +OpCapability DenormPreserve +OpCapability DenormFlushToZero +OpCapability SignedZeroInfNanPreserve +OpCapability RoundingModeRTE +OpCapability RoundingModeRTZ +OpExtension "SPV_KHR_float_controls" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main )" + mode + + R"( 16 +OpExecutionMode %main )" + mode + + R"( 32 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(AllowMultipleExecutionModes, SameOperand) { + const std::string mode = GetParam(); + const std::string spirv = R"( +OpCapability Shader +OpCapability DenormPreserve +OpCapability DenormFlushToZero +OpCapability SignedZeroInfNanPreserve +OpCapability RoundingModeRTE +OpCapability RoundingModeRTZ +OpExtension "SPV_KHR_float_controls" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main )" + mode + + R"( 32 +OpExecutionMode %main )" + mode + + R"( 32 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("execution mode must not be specified multiple times " + "for the same entry point and operands")); +} + +INSTANTIATE_TEST_SUITE_P(MultipleFloatControlsExecModes, + AllowMultipleExecutionModes, + Values("DenormPreserve", "DenormFlushToZero", + "SignedZeroInfNanPreserve", "RoundingModeRTE", + "RoundingModeRTZ")); + +using MultipleExecModes = spvtest::ValidateBase<std::string>; + +TEST_P(MultipleExecModes, DuplicateMode) { + const std::string mode = GetParam(); + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main )" + mode + + R"( +OpExecutionMode %main )" + mode + + R"( +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("execution mode must not be specified multiple times " + "per entry point")); +} + +INSTANTIATE_TEST_SUITE_P(MultipleFragmentExecMode, MultipleExecModes, + Values("DepthReplacing", "DepthGreater", "DepthLess", + "DepthUnchanged")); + +TEST_F(ValidateMode, FloatControls2FPFastMathDefaultSameOperand) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %none +OpExecutionModeId %main FPFastMathDefault %float %none +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("execution mode must not be specified multiple times " + "for the same entry point and operands")); +} + +TEST_F(ValidateMode, FloatControls2FPFastMathDefaultDifferentOperand) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Float16 +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %none +OpExecutionModeId %main FPFastMathDefault %half %none +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%half = OpTypeFloat 16 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); +} + TEST_F(ValidateMode, FragmentShaderInterlockVertexBad) { const std::string spirv = R"( OpCapability Shader @@ -1306,6 +1462,752 @@ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } +TEST_F(ValidateMode, MaximalReconvergenceRequiresExtension) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main MaximallyReconvergesKHR +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("(6023) requires one of these extensions: " + "SPV_KHR_maximal_reconvergence ")); +} + +TEST_F(ValidateMode, FPFastMathDefaultNotExecutionModeId) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main FPFastMathDefault %int_0 %int_0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + 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, FPFastMathDefaultNotAType) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %int_0 %int_0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%void_fn = OpTypeFunction %void +%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( + "The Target Type operand must be a floating-point scalar type")); +} + +TEST_F(ValidateMode, FPFastMathDefaultNotAFloatType) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %int %int_0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%void_fn = OpTypeFunction %void +%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( + "The Target Type operand must be a floating-point scalar type")); +} + +TEST_F(ValidateMode, FPFastMathDefaultNotAFloatScalarType) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float2 %int_0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%void_fn = OpTypeFunction %void +%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( + "The Target Type operand must be a floating-point scalar type")); +} + +TEST_F(ValidateMode, FPFastMathDefaultSpecConstant) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %int_0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpSpecConstant %int 0 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%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("The Fast Math Default operand must be a " + "non-specialization constant")); +} + +TEST_F(ValidateMode, FPFastMathDefaultInvalidMask) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %constant +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 524288 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%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("The Fast Math Default operand is an invalid bitmask value")); +} + +TEST_F(ValidateMode, FPFastMathDefaultContainsFast) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %constant +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 16 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%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("The Fast Math Default operand must not include Fast")); +} + +TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingAllowReassoc) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %constant +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 327680 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%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("The Fast Math Default operand must include AllowContract and " + "AllowReassoc when AllowTransform is specified")); +} + +TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingAllowContract) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %constant +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 393216 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%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("The Fast Math Default operand must include AllowContract and " + "AllowReassoc when AllowTransform is specified")); +} + +TEST_F(ValidateMode, FPFastMathDefaultAllowTransformMissingContractAndReassoc) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %constant +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 262144 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%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("The Fast Math Default operand must include AllowContract and " + "AllowReassoc when AllowTransform is specified")); +} + +TEST_F(ValidateMode, FPFastMathDefaultSignedZeroInfNanPreserve) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpCapability SignedZeroInfNanPreserve +OpExtension "SPV_KHR_float_controls2" +OpExtension "SPV_KHR_float_controls" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionModeId %main FPFastMathDefault %float %constant +OpExecutionMode %main SignedZeroInfNanPreserve 32 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +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("FPFastMathDefault and SignedZeroInfNanPreserve execution " + "modes cannot be applied to the same entry point")); +} + +TEST_F(ValidateMode, FPFastMathDefaultConractionOff) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability FloatControls2 +OpCapability SignedZeroInfNanPreserve +OpExtension "SPV_KHR_float_controls2" +OpExtension "SPV_KHR_float_controls" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpExecutionMode %main ContractionOff +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +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("FPFastMathDefault and ContractionOff execution modes " + "cannot be applied to the same entry point")); +} + +TEST_F(ValidateMode, FPFastMathDefaultNoContractionNotInCallTree) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add NoContraction +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%zero = OpConstant %float 0 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +%add = OpFAdd %float %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMode, FPFastMathDefaultNoContractionInCallTree) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add NoContraction +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%zero = OpConstant %float 0 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +%add = OpFAdd %float %zero %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("NoContraction cannot be used by an entry point with " + "the FPFastMathDefault execution mode")); +} + +TEST_F(ValidateMode, FPFastMathDefaultNoContractionInCallTree2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Kernel +OpCapability Addresses +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpDecorate %const NoContraction +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%zero = OpConstant %float 0 +%const = OpSpecConstantOp %float FAdd %zero %zero +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +%add = OpFAdd %float %const %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("NoContraction cannot be used by an entry point with " + "the FPFastMathDefault execution mode")); +} + +TEST_F(ValidateMode, FPFastMathDefaultFastMathFastNotInCallTree) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode Fast +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%zero = OpConstant %float 0 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +%add = OpFAdd %float %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMode, FPFastMathDefaultFastMathFastInCallTree) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add FPFastMathMode Fast +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%zero = OpConstant %float 0 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +%add = OpFAdd %float %zero %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("FPFastMathMode Fast cannot be used by an entry point " + "with the FPFastMathDefault execution mode")); +} + +TEST_F(ValidateMode, FPFastMathDefaultFastMathFastInCallTree2) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability FloatControls2 +OpExtension "SPV_KHR_float_controls2" +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %main "main" +OpExecutionModeId %main FPFastMathDefault %float %constant +OpDecorate %const FPFastMathMode Fast +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%constant = OpConstant %int 0 +%float = OpTypeFloat 32 +%zero = OpConstant %float 0 +%const = OpSpecConstantOp %float FAdd %zero %zero +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%call = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +%func = OpFunction %void None %void_fn +%func_entry = OpLabel +%add = OpFAdd %float %const %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("FPFastMathMode Fast cannot be used by an entry point " + "with the FPFastMathDefault execution mode")); +} + +TEST_F(ValidateMode, FragmentShaderRequireFullQuadsKHR) { + const std::string spirv = R"( +OpCapability Shader +OpCapability GroupNonUniform +OpCapability GroupNonUniformVote +OpCapability GroupNonUniformBallot +OpCapability QuadControlKHR +OpExtension "SPV_KHR_quad_control" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %4 "main" +OpExecutionMode %4 OriginUpperLeft +OpExecutionMode %4 RequireFullQuadsKHR +OpDecorate %17 Location 0 +OpDecorate %31 BuiltIn HelperInvocation +OpDecorate %40 Location 0 +OpDecorate %44 DescriptorSet 0 +OpDecorate %44 Binding 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%7 = OpTypeVector %6 4 +%8 = OpTypePointer Function %7 +%10 = OpTypeBool +%11 = OpConstantTrue %10 +%12 = OpConstant %6 7 +%14 = OpTypeFloat 32 +%15 = OpTypeVector %14 4 +%16 = OpTypePointer Output %15 +%17 = OpVariable %16 Output +%18 = OpConstant %14 1 +%19 = OpConstant %14 0 +%20 = OpConstantComposite %15 %18 %19 %19 %18 +%23 = OpConstant %6 4 +%27 = OpConstant %6 1 +%28 = OpTypePointer Output %14 +%30 = OpTypePointer Input %10 +%31 = OpVariable %30 Input +%36 = OpConstant %6 2 +%38 = OpTypeVector %14 2 +%39 = OpTypePointer Input %38 +%40 = OpVariable %39 Input +%41 = OpTypeImage %14 2D 0 0 0 1 Unknown +%42 = OpTypeSampledImage %41 +%43 = OpTypePointer UniformConstant %42 +%44 = OpVariable %43 UniformConstant +%4 = OpFunction %2 None %3 +%5 = OpLabel +%9 = OpVariable %8 Function +%13 = OpGroupNonUniformBallot %7 %12 %11 +OpStore %9 %13 +OpStore %17 %20 +%21 = OpLoad %7 %9 +%22 = OpGroupNonUniformBallotBitCount %6 %12 Reduce %21 +%24 = OpIEqual %10 %22 %23 +OpSelectionMerge %26 None +OpBranchConditional %24 %25 %26 +%25 = OpLabel +%29 = OpAccessChain %28 %17 %27 +OpStore %29 %18 +OpBranch %26 +%26 = OpLabel +%32 = OpLoad %10 %31 +%33 = OpGroupNonUniformAny %10 %12 %32 +OpSelectionMerge %35 None +OpBranchConditional %33 %34 %35 +%34 = OpLabel +%37 = OpAccessChain %28 %17 %36 +OpStore %37 %18 +OpBranch %35 +%35 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Execution mode can only be used with the Fragment execution model")); +} + +TEST_F(ValidateMode, FragmentShaderQuadDerivativesKHR) { + const std::string spirv = R"( +OpCapability Shader +OpCapability GroupNonUniform +OpCapability GroupNonUniformVote +OpCapability QuadControlKHR +OpExtension "SPV_KHR_quad_control" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %4 "main" +OpExecutionMode %4 OriginUpperLeft +OpExecutionMode %4 QuadDerivativesKHR +OpDecorate %12 BuiltIn FragCoord +OpDecorate %41 Location 0 +OpDecorate %45 DescriptorSet 0 +OpDecorate %45 Binding 0 +OpDecorate %49 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeBool +%7 = OpTypePointer Function %6 +%9 = OpTypeFloat 32 +%10 = OpTypeVector %9 4 +%11 = OpTypePointer Input %10 +%12 = OpVariable %11 Input +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 1 +%15 = OpTypePointer Input %9 +%18 = OpConstant %9 8.5 +%21 = OpConstant %9 0.100000001 +%25 = OpConstant %13 0 +%28 = OpConstant %9 3.5 +%30 = OpConstant %9 6 +%36 = OpConstant %13 7 +%40 = OpTypePointer Output %10 +%41 = OpVariable %40 Output +%42 = OpTypeImage %9 2D 0 0 0 1 Unknown +%43 = OpTypeSampledImage %42 +%44 = OpTypePointer UniformConstant %43 +%45 = OpVariable %44 UniformConstant +%47 = OpTypeVector %9 2 +%48 = OpTypePointer Input %47 +%49 = OpVariable %48 Input +%53 = OpConstant %9 0.899999976 +%54 = OpConstant %9 0.200000003 +%55 = OpConstant %9 1 +%56 = OpConstantComposite %10 %53 %54 %54 %55 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%8 = OpVariable %7 Function +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %9 %16 +%19 = OpFSub %9 %17 %18 +%20 = OpExtInst %9 %1 FAbs %19 +%22 = OpFOrdLessThan %6 %20 %21 +OpSelectionMerge %24 None +OpBranchConditional %22 %23 %24 +%23 = OpLabel +%26 = OpAccessChain %15 %12 %25 +%27 = OpLoad %9 %26 +%29 = OpFSub %9 %27 %28 +%31 = OpFMod %9 %29 %30 +%33 = OpFOrdLessThan %6 %31 %21 +OpBranch %24 +%24 = OpLabel +%34 = OpPhi %6 %22 %5 %33 %23 +OpStore %8 %34 +%35 = OpLoad %6 %8 +%37 = OpGroupNonUniformAny %6 %36 %35 +OpSelectionMerge %39 None +OpBranchConditional %37 %38 %52 +%38 = OpLabel +%46 = OpLoad %43 %45 +%50 = OpLoad %47 %49 +%51 = OpImageSampleImplicitLod %10 %46 %50 +OpStore %41 %51 +OpBranch %39 +%52 = OpLabel +OpStore %41 %56 +OpBranch %39 +%39 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Execution mode can only be used with the Fragment execution model")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_non_uniform_test.cpp b/test/val/val_non_uniform_test.cpp index af571d3..530676d 100644 --- a/test/val/val_non_uniform_test.cpp +++ b/test/val/val_non_uniform_test.cpp
@@ -44,6 +44,10 @@ OpCapability GroupNonUniformArithmetic OpCapability GroupNonUniformClustered OpCapability GroupNonUniformQuad +OpCapability GroupNonUniformPartitionedNV +OpCapability QuadControlKHR +OpExtension "SPV_NV_shader_subgroup_partitioned" +OpExtension "SPV_KHR_quad_control" )"; ss << capabilities_and_extensions; @@ -62,16 +66,27 @@ %float = OpTypeFloat 32 %u32vec4 = OpTypeVector %u32 4 %u32vec3 = OpTypeVector %u32 3 +%v2bool = OpTypeVector %bool 2 +%v4float = OpTypeVector %float 4 +%struct = OpTypeStruct %int +%v4int = OpTypeVector %int 4 %true = OpConstantTrue %bool %false = OpConstantFalse %bool %u32_0 = OpConstant %u32 0 +%int_0 = OpConstant %int 0 %float_0 = OpConstant %float 0 %u32vec4_null = OpConstantComposite %u32vec4 %u32_0 %u32_0 %u32_0 %u32_0 %u32vec3_null = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0 +%v2bool_false = OpConstantNull %v2bool +%v4float_null = OpConstantNull %v4float +%struct_null = OpConstantNull %struct +%v4int_null = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 + +%u32_undef = OpUndef %u32 %cross_device = OpConstant %u32 0 %device = OpConstant %u32 1 @@ -122,6 +137,32 @@ } } +std::string ConvertMatch(const std::string& type) { + if (type == "%bool") { + return "%true"; + } else if (type == "%u32") { + return "%u32_0"; + } else if (type == "%int") { + return "%int_0"; + } else if (type == "%float") { + return "%float_0"; + } else if (type == "%u32vec4") { + return "%u32vec4_null"; + } else if (type == "%u32vec3") { + return "%u32vec3_null"; + } else if (type == "%v2bool") { + return "%v2bool_false"; + } else if (type == "%v4float") { + return "%v4float_null"; + } else if (type == "%struct") { + return "%struct_null"; + } else if (type == "%v4int") { + return "%v4int_null"; + } + + return "INVALID"; +} + TEST_P(GroupNonUniform, Vulkan1p1) { std::string opcode = std::get<0>(GetParam()); std::string type = std::get<1>(GetParam()); @@ -129,10 +170,20 @@ std::string args = std::get<3>(GetParam()); std::string error = std::get<4>(GetParam()); + const std::string match = "match_res"; + size_t pos = std::string::npos; + while ((pos = args.find(match)) != std::string::npos) { + const std::string replace = ConvertMatch(type); + args = args.substr(0, pos) + replace + args.substr(pos + match.size()); + } + std::ostringstream sstr; sstr << "%result = " << opcode << " "; sstr << type << " "; - sstr << ConvertScope(execution_scope) << " "; + if (opcode != "OpGroupNonUniformQuadAllKHR" && + opcode != "OpGroupNonUniformQuadAnyKHR") { + sstr << ConvertScope(execution_scope) << " "; + } sstr << args << "\n"; CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_VULKAN_1_1); @@ -162,10 +213,20 @@ std::string args = std::get<3>(GetParam()); std::string error = std::get<4>(GetParam()); + const std::string match = "match_res"; + size_t pos = std::string::npos; + while ((pos = args.find(match)) != std::string::npos) { + const std::string replace = ConvertMatch(type); + args = args.substr(0, pos) + replace + args.substr(pos + match.size()); + } + std::ostringstream sstr; sstr << "%result = " << opcode << " "; sstr << type << " "; - sstr << ConvertScope(execution_scope) << " "; + if (opcode != "OpGroupNonUniformQuadAllKHR" && + opcode != "OpGroupNonUniformQuadAnyKHR") { + sstr << ConvertScope(execution_scope) << " "; + } sstr << args << "\n"; CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_UNIVERSAL_1_3); @@ -292,6 +353,542 @@ Values("Expected Value to be a vector of four " "components of integer type scalar"))); +INSTANTIATE_TEST_SUITE_P(GroupNonUniformElectGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformElect"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values(""), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformElectBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformElect"), + Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3", + "%v2bool", "%v4float", "%struct"), + Values(spv::Scope::Subgroup), Values(""), + Values("Result must be a boolean scalar type"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformAnyAllGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformAny", + "OpGroupNonUniformAll"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%true", "%false"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformAnyAllBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformAny", "OpGroupNonUniformAll"), + Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3", + "%v2bool", "%v4float", "%struct"), + Values(spv::Scope::Subgroup), Values("%true"), + Values("Result must be a boolean scalar type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformAnyAllBadOperand, GroupNonUniform, + Combine(Values("OpGroupNonUniformAny", "OpGroupNonUniformAll"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%u32_0", "%int_0", "%float_0", "%u32vec4_null", + "%u32vec3_null", "%v2bool_false", "%v4float_null", + "%struct_null"), + Values("Predicate must be a boolean scalar type"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformAllEqualGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformAllEqual"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%true", "%false"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformAllEqualBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformAllEqual"), + Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3", + "%v2bool", "%v4float", "%struct"), + Values(spv::Scope::Subgroup), Values("%true"), + Values("Result must be a boolean scalar type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformAllEqualBadOperand, GroupNonUniform, + Combine(Values("OpGroupNonUniformAllEqual"), Values("%bool"), + Values(spv::Scope::Subgroup), Values("%struct_null"), + Values("Value must be a scalar or vector of integer, " + "floating-point, or boolean type"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcast", + "OpGroupNonUniformQuadBroadcast", + "OpGroupNonUniformQuadSwap"), + Values("%bool", "%u32", "%int", "%float", + "%u32vec4", "%u32vec3", "%v2bool", + "%v4float", "%v4int"), + Values(spv::Scope::Subgroup), + Values("match_res %u32_0"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBroadcastShuffleBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcast", "OpGroupNonUniformShuffle", + "OpGroupNonUniformShuffleXor", "OpGroupNonUniformShuffleUp", + "OpGroupNonUniformShuffleDown", + "OpGroupNonUniformQuadBroadcast", + "OpGroupNonUniformQuadSwap"), + Values("%void", "%struct"), Values(spv::Scope::Subgroup), + Values("%u32_0 %u32_0"), + Values("Result must be a scalar or vector of integer, " + "floating-point, or boolean type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBroadcastShuffleBadOperand1, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcast", "OpGroupNonUniformShuffle", + "OpGroupNonUniformShuffleXor", "OpGroupNonUniformShuffleUp", + "OpGroupNonUniformShuffleDown", + "OpGroupNonUniformQuadBroadcast", + "OpGroupNonUniformQuadSwap"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%u32_0 %u32_0", "%int_0 %u32_0", "%float_0 %u32_0", + "%u32vec4_null %u32_0", "%u32vec3_null %u32_0", + "%v2bool_false %u32_0", "%v4float_null %u32_0", + "%struct_null %u32_0", "%v4int_null %u32_0"), + Values("The type of Value must match the Result type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBroadcastShuffleBadOperand2, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcast", "OpGroupNonUniformShuffle", + "OpGroupNonUniformShuffleXor", "OpGroupNonUniformShuffleUp", + "OpGroupNonUniformShuffleDown", + "OpGroupNonUniformQuadBroadcast", + "OpGroupNonUniformQuadSwap"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%true %true", "%true %int_0", "%true %float_0", + "%true %u32vec4_null", "%true %u32vec3_null", + "%true %v4float_null", "%true %v2bool_false", + "%true %struct_null", "%true %v4int_null"), + Values("must be an unsigned integer scalar"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastShuffleOperand2NotConstant, + GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcast", + "OpGroupNonUniformQuadBroadcast", + "OpGroupNonUniformQuadSwap"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%true %u32_undef"), + Values("must be a constant instruction"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastFirstGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcastFirst"), + Values("%bool", "%u32", "%int", "%float", + "%u32vec4", "%u32vec3", "%v2bool", + "%v4float", "%v4int"), + Values(spv::Scope::Subgroup), + Values("match_res"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBroadcasFirsttBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcastFirst"), + Values("%void", "%struct"), Values(spv::Scope::Subgroup), + Values("%u32_0"), + Values("Result must be a scalar or vector of integer, " + "floating-point, or boolean type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBroadcastBadOperand, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcastFirst"), Values("%bool"), + Values(spv::Scope::Subgroup), + Values("%u32_0", "%int_0", "%float_0", "%u32vec4_null", + "%u32vec3_null", "%v2bool_false", "%v4float_null", + "%struct_null", "%v4int_null"), + Values("The type of Value must match the Result type"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallot"), + Values("%u32vec4"), + Values(spv::Scope::Subgroup), + Values("%true", "%false"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallot"), + Values("%void", "%bool", "%u32", "%int", "%float", "%u32vec3", + "%v2bool", "%v4float", "%struct", "%v4int"), + Values(spv::Scope::Subgroup), Values("%true", "%false"), + Values("Result must be a 4-component unsigned integer vector"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBadOperand, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallot"), + Values("%u32vec4"), + Values(spv::Scope::Subgroup), + Values("%u32_0", "%int_0", "%float_0", + "%u32vec4_null", "%u32vec3_null", + "%v2bool_false", "%v4float_null", + "%struct_null", "%v4int_null"), + Values("Predicate must be a boolean scalar"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformInverseBallotGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformInverseBallot"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%u32vec4_null"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformInverseBallotBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformInverseBallot"), + Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3", + "%v2bool", "%v4float", "%struct", "%v4int"), + Values(spv::Scope::Subgroup), Values("%u32vec4_null"), + Values("Result must be a boolean scalar"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformInverseBallotBadOperand, GroupNonUniform, + Combine(Values("OpGroupNonUniformInverseBallot"), Values("%bool"), + Values(spv::Scope::Subgroup), + Values("%true", "%false", "%u32_0", "%int_0", "%float_0", + "%u32vec3_null", "%v2bool_false", "%v4float_null", + "%struct_null", "%v4int_null"), + Values("Value must be a 4-component unsigned integer vector"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitExtractGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitExtract"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%u32vec4_null %u32_0"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotBitExtractBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitExtract"), + Values("%void", "%u32", "%int", "%float", "%u32vec4", "%u32vec3", + "%v2bool", "%v4float", "%struct", "%v4int"), + Values(spv::Scope::Subgroup), Values("%u32vec4_null %u32_0"), + Values("Result must be a boolean scalar"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotBitExtractBadOperand1, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitExtract"), Values("%bool"), + Values(spv::Scope::Subgroup), + Values("%true %u32_0", "%false %u32_0", "%u32_0 %u32_0", + "%int_0 %u32_0", "%float_0 %u32_0", "%u32vec3_null %u32_0", + "%v2bool_false %u32_0", "%v4float_null %u32_0", + "%struct_null %u32_0", "%v4int_null %u32_0"), + Values("Value must be a 4-component unsigned integer vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotBitExtractBadOperand2, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitExtract"), Values("%bool"), + Values(spv::Scope::Subgroup), + Values("%u32vec4_null %true", "%u32vec4_null %false", + "%u32vec4_null %int_0", "%u32vec4_null %float_0", + "%u32vec4_null %u32vec3_null", "%u32vec4_null %v2bool_false", + "%u32vec4_null %v4float_null", "%u32vec4_null %struct_null", + "%u32vec4_null %v4int_null"), + Values("Id must be an unsigned integer scalar"))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotFindGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotFindLSB", + "OpGroupNonUniformBallotFindMSB"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("%u32vec4_null"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotFindBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotFindLSB", + "OpGroupNonUniformBallotFindMSB"), + Values("%void", "%bool", "%int", "%float", "%u32vec4", "%u32vec3", + "%v2bool", "%v4float", "%struct", "%v4int"), + Values(spv::Scope::Subgroup), Values("%u32vec4_null"), + Values("Result must be an unsigned integer scalar"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotFindBadOperand, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotFindLSB", + "OpGroupNonUniformBallotFindMSB"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("%true", "%false", "%u32_0", "%int_0", "%float_0", + "%u32vec3_null", "%v2bool_false", "%v4float_null", + "%struct_null", "%v4int_null"), + Values("Value must be a 4-component unsigned integer vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformSMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32", "%int", "%u32vec4", "%u32vec3", "%v4int"), + Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0", + "PartitionedReduceNV match_res %u32vec4_null", + "PartitionedInclusiveScanNV match_res %u32vec4_null", + "PartitionedExclusiveScanNV match_res %v4int_null"), + Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformSMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%bool", "%float", "%v4float", "%struct"), + Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values("Result must be an integer scalar or vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticBadValue, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformSMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%int", "%u32vec4", "%u32vec3", "%v4int"), + Values(spv::Scope::Subgroup), + Values("Reduce %u32_0", "InclusiveScan %u32_0", + "ExclusiveScan %u32_0", "ClusteredReduce %u32_0 %u32_0"), + Values("The type of Value must match the Result type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticMissingClusterSize, GroupNonUniform, + Combine( + Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", + "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res"), + Values( + "ClusterSize must be present when Operation is ClusteredReduce"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticMissingBallot, GroupNonUniform, + Combine( + Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", + "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("PartitionedReduceNV match_res", + "PartitionedInclusiveScanNV match_res", + "PartitionedExclusiveScanNV match_res"), + Values("Ballot must be present when Operation is PartitionedReduceNV, " + "PartitionedInclusiveScanNV, or PartitionedExclusiveScanNV"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticBadClusterSizeType, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", + "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res %true", + "ClusteredReduce match_res %false", + "ClusteredReduce match_res %int_0", + "ClusteredReduce match_res %float_0", + "ClusteredReduce match_res %u32vec4_null", + "ClusteredReduce match_res %u32vec3_null", + "ClusteredReduce match_res %v2bool_false", + "ClusteredReduce match_res %v4float_null", + "ClusteredReduce match_res %struct_null", + "ClusteredReduce match_res %v4int_null"), + Values("ClusterSize must be an unsigned integer scalar"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticBadBallotType, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", + "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("PartitionedReduceNV match_res %true", + "PartitionedReduceNV match_res %false", + "PartitionedReduceNV match_res %int_0", + "PartitionedReduceNV match_res %float_0", + "PartitionedReduceNV match_res %u32_0", + "PartitionedReduceNV match_res %u32vec3_null", + "PartitionedReduceNV match_res %v2bool_false", + "PartitionedReduceNV match_res %v4float_null", + "PartitionedReduceNV match_res %struct_null"), + Values("Ballot must be a 4-component integer vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmeticClusterSizeNotConstant, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", + "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res %u32_undef"), + Values("ClusterSize must be a constant instruction"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformUnsignedIntegerArithmeticGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformUMin", "OpGroupNonUniformUMax"), + Values("%u32", "%u32vec4", "%u32vec3"), + Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformUnsignedIntegerArithmeticBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformUMin", "OpGroupNonUniformUMax"), + Values("%bool", "%int", "%float", "%v4float", "%struct", "%v4int"), + Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values("Result must be an unsigned integer scalar or vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformUnsignedIntegerArithmeticBadValue, GroupNonUniform, + Combine(Values("OpGroupNonUniformUMin", "OpGroupNonUniformUMax"), + Values("%u32vec4", "%u32vec3"), Values(spv::Scope::Subgroup), + Values("Reduce %u32_0", "InclusiveScan %u32_0", + "ExclusiveScan %u32_0", "ClusteredReduce %u32_0 %u32_0"), + Values("The type of Value must match the Result type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmeticGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%float", "%v4float"), Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmeticBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%bool", "%u32", "%int", "%u32vec4", "%u32vec3", "%struct", + "%v4int"), + Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values("Result must be a floating-point scalar or vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmeticBadValue, GroupNonUniform, + Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%v4float"), Values(spv::Scope::Subgroup), + Values("Reduce %float_0", "InclusiveScan %float_0", + "ExclusiveScan %float_0", "ClusteredReduce %float_0 %u32_0"), + Values("The type of Value must match the Result type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmeticMissingClusterSize, GroupNonUniform, + Combine( + Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%float"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res"), + Values( + "ClusterSize must be present when Operation is ClusteredReduce"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmeticBadClusterSizeType, GroupNonUniform, + Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%float"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res %true", + "ClusteredReduce match_res %false", + "ClusteredReduce match_res %int_0", + "ClusteredReduce match_res %float_0", + "ClusteredReduce match_res %u32vec4_null", + "ClusteredReduce match_res %u32vec3_null", + "ClusteredReduce match_res %v2bool_false", + "ClusteredReduce match_res %v4float_null", + "ClusteredReduce match_res %struct_null", + "ClusteredReduce match_res %v4int_null"), + Values("ClusterSize must be an unsigned integer scalar"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmeticClusterSizeNotConstant, GroupNonUniform, + Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%float"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res %u32_undef"), + Values("ClusterSize must be a constant instruction"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBooleanArithmeticGood, GroupNonUniform, + Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%bool", "%v2bool"), Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBooleanArithmeticBadResultType, GroupNonUniform, + Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%u32", "%int", "%float", "%u32vec4", "%u32vec3", "%struct", + "%v4float", "%v4int"), + Values(spv::Scope::Subgroup), + Values("Reduce match_res", "InclusiveScan match_res", + "ExclusiveScan match_res", + "ClusteredReduce match_res %u32_0"), + Values("Result must be a boolean scalar or vector"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBooleanArithmeticBadValue, GroupNonUniform, + Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%v2bool"), Values(spv::Scope::Subgroup), + Values("Reduce %true", "InclusiveScan %true", + "ExclusiveScan %false", "ClusteredReduce %false %u32_0"), + Values("The type of Value must match the Result type"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBooleanArithmeticMissingClusterSize, GroupNonUniform, + Combine( + Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res"), + Values( + "ClusterSize must be present when Operation is ClusteredReduce"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBooleanArithmeticBadClusterSizeType, GroupNonUniform, + Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res %true", + "ClusteredReduce match_res %false", + "ClusteredReduce match_res %int_0", + "ClusteredReduce match_res %float_0", + "ClusteredReduce match_res %u32vec4_null", + "ClusteredReduce match_res %u32vec3_null", + "ClusteredReduce match_res %v2bool_false", + "ClusteredReduce match_res %v4float_null", + "ClusteredReduce match_res %struct_null", + "ClusteredReduce match_res %v4int_null"), + Values("ClusterSize must be an unsigned integer scalar"))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBooleanArithmeticClusterSizeNotConstant, GroupNonUniform, + Combine(Values("OpGroupNonUniformLogicalAnd", "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("ClusteredReduce match_res %u32_undef"), + Values("ClusterSize must be a constant instruction"))); + +// Subgroup scope is not actual parameter, but used for test expectations, +INSTANTIATE_TEST_SUITE_P(GroupNonUniformQuadAllKHR, GroupNonUniform, + Combine(Values("OpGroupNonUniformQuadAllKHR"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%true"), Values(""))); + +// Subgroup scope is not actual parameter, but used for test expectations, +INSTANTIATE_TEST_SUITE_P(GroupNonUniformQuadAnyKHR, GroupNonUniform, + Combine(Values("OpGroupNonUniformQuadAnyKHR"), + Values("%bool"), Values(spv::Scope::Subgroup), + Values("%true"), Values(""))); + TEST_F(ValidateGroupNonUniform, VulkanGroupNonUniformBallotBitCountOperation) { std::string test = R"( OpCapability Shader @@ -327,6 +924,146 @@ "be only: Reduce, InclusiveScan, or ExclusiveScan.")); } +TEST_F(ValidateGroupNonUniform, BroadcastNonConstantSpv1p4) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniformBallot +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%subgroup = OpConstant %int 3 +%struct = OpTypeStruct %int +%ptr_struct = OpTypePointer StorageBuffer %struct +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ld = OpLoad %int %gep +%broadcast = OpGroupNonUniformBroadcast %int %subgroup %int_0 %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Before SPIR-V 1.5, Id must be a constant instruction")); +} + +TEST_F(ValidateGroupNonUniform, BroadcastNonConstantSpv1p5) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniformBallot +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%subgroup = OpConstant %int 3 +%struct = OpTypeStruct %int +%ptr_struct = OpTypePointer StorageBuffer %struct +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ld = OpLoad %int %gep +%broadcast = OpGroupNonUniformBroadcast %int %subgroup %int_0 %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateGroupNonUniform, QuadBroadcastNonConstantSpv1p4) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniformQuad +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%subgroup = OpConstant %int 3 +%struct = OpTypeStruct %int +%ptr_struct = OpTypePointer StorageBuffer %struct +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ld = OpLoad %int %gep +%broadcast = OpGroupNonUniformQuadBroadcast %int %subgroup %int_0 %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Before SPIR-V 1.5, Index must be a constant instruction")); +} + +TEST_F(ValidateGroupNonUniform, QuadBroadcastNonConstantSpv1p5) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniformQuad +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%subgroup = OpConstant %int 3 +%struct = OpTypeStruct %int +%ptr_struct = OpTypePointer StorageBuffer %struct +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_int %var %int_0 +%ld = OpLoad %int %gep +%broadcast = OpGroupNonUniformQuadBroadcast %int %subgroup %int_0 %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/test/val/val_ray_tracing_test.cpp b/test/val/val_ray_tracing_test.cpp index 58b9356..60f2f89 100644 --- a/test/val/val_ray_tracing_test.cpp +++ b/test/val/val_ray_tracing_test.cpp
@@ -578,6 +578,95 @@ "IncomingRayPayloadKHR")); } +TEST_F(ValidateRayTracing, InterfaceIncomingRayPayload) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableKHR %main "main" %inData1 %inData2 +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%inData_ptr = OpTypePointer IncomingRayPayloadKHR %int +%inData1 = OpVariable %inData_ptr IncomingRayPayloadKHR +%inData2 = OpVariable %inData_ptr IncomingRayPayloadKHR +%main = OpFunction %void None %func +%label = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-IncomingRayPayloadKHR-04700")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry-point has more than one variable with the " + "IncomingRayPayloadKHR storage class in the interface")); +} + +TEST_F(ValidateRayTracing, InterfaceHitAttribute) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableKHR %main "main" %inData1 %inData2 +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%inData_ptr = OpTypePointer HitAttributeKHR %int +%inData1 = OpVariable %inData_ptr HitAttributeKHR +%inData2 = OpVariable %inData_ptr HitAttributeKHR +%main = OpFunction %void None %func +%label = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04702")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has more than one variable with the " + "HitAttributeKHR storage class in the interface")); +} + +TEST_F(ValidateRayTracing, InterfaceIncomingCallableData) { + const std::string body = R"( +OpCapability RayTracingKHR +OpExtension "SPV_KHR_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableKHR %main "main" %inData1 %inData2 +OpName %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%int = OpTypeInt 32 1 +%inData_ptr = OpTypePointer IncomingCallableDataKHR %int +%inData1 = OpVariable %inData_ptr IncomingCallableDataKHR +%inData2 = OpVariable %inData_ptr IncomingCallableDataKHR +%main = OpFunction %void None %func +%label = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-IncomingCallableDataKHR-04706")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry-point has more than one variable with the " + "IncomingCallableDataKHR storage class in the interface")); +} + } // namespace } // namespace val } // namespace spvtools
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index ca6633a..5f2a008 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp
@@ -41,12 +41,6 @@ enum class FuzzingTarget { kSpirv, kWgsl }; -// Check that the std::system function can actually be used. -bool CheckExecuteCommand() { - int res = std::system(nullptr); - return res != 0; -} - // Execute a command using the shell. // Returns true if and only if the command's exit status was 0. bool ExecuteCommand(const std::string& command) { @@ -770,11 +764,6 @@ } break; case FuzzActions::SHRINK: { - if (!CheckExecuteCommand()) { - std::cerr << "could not find shell interpreter for executing a command" - << std::endl; - return 1; - } if (!Shrink(target_env, fuzzer_options, validator_options, binary_in, initial_facts, shrink_transformations_file, shrink_temp_file_prefix, interestingness_test, &binary_out,
diff --git a/tools/io.h b/tools/io.h index 8bbee3a..a48e3c3 100644 --- a/tools/io.h +++ b/tools/io.h
@@ -144,6 +144,7 @@ ~OutputFile() { if (fp_ == stdout) { + fflush(stdout); SET_STDOUT_MODE(old_mode_); } else if (fp_ != nullptr) { fclose(fp_);
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 381d8b9..f3898aa 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp
@@ -59,6 +59,13 @@ NOTE: The SPIR-V version used by the linked binary module depends only on the version of the inputs, and is not affected by this option. + --use-highest-version + Upgrade the output SPIR-V version to the highest of the input + files, instead of requiring all of them to have the same + version. + NOTE: If one of the older input files uses an instruction that + is deprecated in the highest SPIR-V version, the output will + be invalid. --verify-ids Verify that IDs in the resulting modules are truly unique. --version @@ -78,6 +85,7 @@ FLAG_LONG_bool( allow_partial_linkage, /* 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_bool( use_highest_version, /* default_value= */ false, /* required= */ false); // clang-format on int main(int, const char* argv[]) { @@ -120,6 +128,7 @@ options.SetAllowPartialLinkage(flags::allow_partial_linkage.value()); options.SetCreateLibrary(flags::create_library.value()); options.SetVerifyIds(flags::verify_ids.value()); + options.SetUseHighestVersion(flags::use_highest_version.value()); if (inFiles.empty()) { fprintf(stderr, "error: No input file specified\n");
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index ce2103c..f8456d7 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp
@@ -335,6 +335,12 @@ These conditions are guaranteed to be met after running dead-branch elimination.)"); printf(R"( + --modify-maximal-reconvergence=[add|remove] + Add or remove the MaximallyReconvergesKHR execution mode to all + entry points in the module. + Note: when adding the execution mode, no attempt is made to + determine if any ray tracing repack instructions are used.)"); + printf(R"( --loop-unswitch Hoists loop-invariant conditionals out of loops by duplicating the loop on each branch of the conditional and adjusting each @@ -392,6 +398,11 @@ Ensure that the optimizer preserves all bindings declared within the module, even when those bindings are unused.)"); printf(R"( + --preserve-interface + Ensure that input and output variables are not removed from the + shader, even if they are unused. Note that this option applies to + all passes that will be run regardless of the order of the flags.)"); + printf(R"( --preserve-spec-constants Ensure that the optimizer preserves all specialization constants declared within the module, even when those constants are unused.)"); @@ -496,6 +507,10 @@ covers reflection information defined by SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)"); printf(R"( + --switch-descriptorset=<from>:<to> + Switch any DescriptoSet decorations using the value <from> to + the new value <to>.)"); + printf(R"( --target-env=<env> Set the target environment. Without this flag the target environment defaults to spv1.5. <env> must be one of @@ -510,6 +525,10 @@ USR/SYS time are returned by getrusage() and can have a small error.)"); printf(R"( + --trim-capabilities + Remove unnecessary capabilities and extensions declared within the + module.)"); + printf(R"( --upgrade-memory-model Upgrades the Logical GLSL450 memory model to Logical VulkanKHR. Transforms memory, image, atomic and barrier operations to conform @@ -697,6 +716,7 @@ spvtools::ValidatorOptions* validator_options, spvtools::OptimizerOptions* optimizer_options) { std::vector<std::string> pass_flags; + bool preserve_interface = true; for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; if ('-' == cur_arg[0]) { @@ -786,6 +806,8 @@ validator_options->SetSkipBlockLayout(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { validator_options->SetRelaxStructStore(true); + } else if (0 == strcmp(cur_arg, "--preserve-interface")) { + preserve_interface = true; } else { // Some passes used to accept the form '--pass arg', canonicalize them // to '--pass=arg'. @@ -808,7 +830,7 @@ } } - if (!optimizer->RegisterPassesFromFlags(pass_flags)) { + if (!optimizer->RegisterPassesFromFlags(pass_flags, preserve_interface)) { return {OPT_STOP, 1}; }
diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 3760054..959f5a2 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp
@@ -14,6 +14,7 @@ #include <cassert> #include <cerrno> +#include <cstdlib> #include <cstring> #include <functional> #include <sstream> @@ -29,12 +30,6 @@ namespace { -// Check that the std::system function can actually be used. -bool CheckExecuteCommand() { - int res = std::system(nullptr); - return res != 0; -} - // Execute a command using the shell. // Returns true if and only if the command's exit status was 0. bool ExecuteCommand(const std::string& command) { @@ -282,12 +277,6 @@ return status.code; } - if (!CheckExecuteCommand()) { - std::cerr << "could not find shell interpreter for executing a command" - << std::endl; - return 2; - } - spvtools::reduce::Reducer reducer(target_env); std::stringstream joined;
diff --git a/tools/sva/package.json b/tools/sva/package.json index 3072d4c..15feaca 100644 --- a/tools/sva/package.json +++ b/tools/sva/package.json
@@ -15,11 +15,11 @@ "bundle": "rollup -c" }, "devDependencies": { - "chai": "^4.2.0", - "eslint": "^6.3.0", + "chai": "^4.3.7", + "eslint": "^8.41.0", "esm": "^3.2.25", - "mocha": "^6.2.0", - "rollup": "^1.21.4", - "serve": "^11.1.0" + "mocha": "^10.2.0", + "rollup": "^3.23.0", + "serve": "^14.2.0" } }
diff --git a/tools/sva/src/spirv.data.js b/tools/sva/src/spirv.data.js index ba969d8..67c0966 100644 --- a/tools/sva/src/spirv.data.js +++ b/tools/sva/src/spirv.data.js
@@ -4376,6 +4376,9 @@ "ShaderClockKHR": { "value": 5055 }, + "QuadControlKHR": { + "value": 5087 + }, "FragmentFullyCoveredEXT": { "value": 5265 },
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock index 2dc95d8..eed94ce 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock
@@ -2,158 +2,206 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== +"@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: - "@babel/highlight" "^7.0.0" + eslint-visitor-keys "^3.3.0" -"@babel/highlight@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" - integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== +"@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: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" + 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" -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@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== -"@types/node@^12.7.5": - version "12.7.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f" - integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w== +"@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" -"@zeit/schemas@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3" - integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg== +"@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.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + 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.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" -acorn-jsx@^5.0.2: +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/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" - integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== -acorn@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" - integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== - -ajv@6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9" - integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^6.10.0, ajv@^6.10.2: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= - dependencies: - string-width "^2.0.0" - -ansi-colors@3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== - -ansi-escapes@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -arch@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" - integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== - -arg@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" - integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +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== -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -boxen@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== +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 "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" + 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" @@ -163,6 +211,20 @@ 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.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -171,163 +233,154 @@ bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + 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@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +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.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" - integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== +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 "^3.0.1" + deep-eql "^4.1.2" get-func-name "^2.0.0" - pathval "^1.1.0" + loupe "^2.3.1" + pathval "^1.1.1" type-detect "^4.0.5" -chalk@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== +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: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + chalk "^4.1.2" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== +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 "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + ansi-styles "^4.1.0" + supports-color "^7.1.0" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +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 sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - restore-cursor "^2.0.0" + 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-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +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@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" - integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA== +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.1.0" - execa "^0.8.0" + arch "^2.2.0" + execa "^5.1.1" + is-wsl "^2.2.0" -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== +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 "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== +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.3" + color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +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.14: - version "2.0.17" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" - integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== +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.40.0 < 2" + mime-db ">= 1.43.0 < 2" -compression@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" - integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== +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.14" + compressible "~2.0.16" debug "2.6.9" - on-headers "~1.0.1" + 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 sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + 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 sha1-DPaLud318r55YcOoUXjLhdunjLQ= + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= +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: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" debug@2.6.9: version "2.6.9" @@ -336,29 +389,22 @@ dependencies: ms "2.0.0" -debug@3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +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.1" + ms "2.1.2" -debug@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" +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== -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== +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" @@ -367,22 +413,15 @@ 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.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +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== -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +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" @@ -391,319 +430,254 @@ dependencies: esutils "^2.0.2" -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +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== -end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== +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: - once "^1.4.0" + esrecurse "^4.3.0" + estraverse "^5.2.0" -es-abstract@^1.5.1: - version "1.14.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" - integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== +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: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" - object-keys "^1.1.1" - string.prototype.trimleft "^2.0.0" - string.prototype.trimright "^2.0.0" - -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-utils@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" - integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== - dependencies: - eslint-visitor-keys "^1.0.0" - -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint@^6.3.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.4.0.tgz#5aa9227c3fbe921982b2eda94ba0d7fae858611a" - integrity sha512-WTVEzK3lSFoXUovDHEbkJqCVPEPwbhCq4trDktNI6ygs7aO41d4cDT0JFAT5MivzZeVLWlg7vHL+bgrQv/t3vA== - dependencies: - "@babel/code-frame" "^7.0.0" + "@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 "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.2" - eslint-visitor-keys "^1.1.0" - espree "^6.1.1" - esquery "^1.0.1" + 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" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^11.7.0" - ignore "^4.0.6" + 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" - inquirer "^6.4.1" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" - minimatch "^3.0.4" - mkdirp "^0.5.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.8.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" + optionator "^0.9.1" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" 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@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" - integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== +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 "^7.0.0" - acorn-jsx "^5.0.2" - eslint-visitor-keys "^1.1.0" + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== +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 "^4.0.0" + estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + 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@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= +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 "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" + 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" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +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.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + 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.4: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + 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 sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== dependencies: punycode "^1.3.2" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= +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: - escape-string-regexp "^1.0.5" + reusify "^1.0.4" -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +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 "^2.0.1" + flat-cache "^3.0.4" -find-up@3.0.0, find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: - locate-path "^3.0.0" + to-regex-range "^5.0.1" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +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: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + locate-path "^6.0.0" + path-exists "^4.0.0" -flat@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" - integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== +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: - is-buffer "~2.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== +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 sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +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== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-caller-file@^2.0.1: +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.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + 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@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= +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== -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== +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: - pump "^3.0.0" + is-glob "^4.0.3" -glob-parent@^5.0.0: +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.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +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" @@ -713,65 +687,53 @@ path-is-absolute "^1.0.0" glob@^7.1.3: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + 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.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.7.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= - -has@^1.0.1, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +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: - function-bind "^1.1.1" + 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== -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" +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@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +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: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" - integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== +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" @@ -779,12 +741,12 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -795,193 +757,147 @@ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@~1.3.0: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" - integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@^6.4.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== +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: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" + binary-extensions "^2.0.0" -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - -is-buffer@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" - integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== - -is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= +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 sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" +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-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-glob@^4.0.0, is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== +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-promise@^2.1.0: +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-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +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: - has "^1.0.1" - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== - dependencies: - has-symbols "^1.0.0" + 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 sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@3.13.1, js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== +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 "^1.0.7" - esprima "^4.0.0" + 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 sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -lcid@^2.0.0: +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/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - -mime-db@1.40.0: - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -"mime-db@>= 1.40.0 < 2": - version "1.41.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.41.0.tgz#9110408e1f6aa1b34aef51f2c9df3caddf46b6a0" - integrity sha512-B5gxBI+2K431XW8C2rcc/lhppbuji67nf9v39eH8pkWoZDxnAL0PxdpH32KYRScniF8qDHBDlI+ipgg5WrCUYw== +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" @@ -995,237 +911,150 @@ dependencies: mime-db "~1.33.0" -mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== +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.40.0" + mime-db "1.52.0" -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -mimic-fn@^2.0.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.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +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" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= +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.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mkdirp@0.5.1, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= +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: - minimist "0.0.8" - -mocha@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.0.tgz#f896b642843445d1bb8bca60eabd9206b8916e56" - integrity sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ== - dependencies: - ansi-colors "3.2.3" + ansi-colors "4.1.1" browser-stdout "1.3.1" - debug "3.2.6" - diff "3.5.0" - escape-string-regexp "1.0.5" - find-up "3.0.0" - glob "7.1.3" - growl "1.10.5" + 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 "3.13.1" - log-symbols "2.2.0" - minimatch "3.0.4" - mkdirp "0.5.1" - ms "2.1.1" - node-environment-flags "1.0.5" - object.assign "4.1.0" - strip-json-comments "2.0.1" - supports-color "6.0.0" - which "1.3.1" - wide-align "1.1.3" - yargs "13.2.2" - yargs-parser "13.0.0" - yargs-unparser "1.5.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 sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: +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== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +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 sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +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== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +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== -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== +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: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" + path-key "^3.0.0" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -object-inspect@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -on-headers@~1.0.1: +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, once@^1.3.1, once@^1.4.0: +once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= +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 "^1.0.0" + mimic-fn "^2.1.0" -optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= +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.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" + 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" -os-locale@^3.0.0, os-locale@^3.1.0: +p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" + yocto-queue "^0.1.0" -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - -p-limit@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== +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-try "^2.0.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + p-limit "^3.0.2" parent-module@^1.0.0: version "1.0.1" @@ -1234,73 +1063,72 @@ dependencies: callsites "^3.0.0" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +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 sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + 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 sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +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@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== -pathval@^1.1.0: +pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +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== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" +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 sha1-wNWmOycYgArY4esPpSachN1BhF4= + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + 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 sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== rc@^1.0.1, rc@^1.1.6: version "1.2.8" @@ -1312,10 +1140,12 @@ minimist "^1.2.0" strip-json-comments "~2.0.1" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +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" @@ -1328,451 +1158,317 @@ registry-url@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= + 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 sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +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== -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" +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@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== +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@^1.21.4: - version "1.21.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.21.4.tgz#00a41a30f90095db890301b226cbe2918e4cf54d" - integrity sha512-Pl512XVCmVzgcBz5h/3Li4oTaoDcmpuFZ+kdhS/wLreALz//WuDAMfomD3QEYl84NkDu6Z6wV9twlcREb4qQsw== - dependencies: - "@types/estree" "0.0.39" - "@types/node" "^12.7.5" - acorn "^7.0.0" +rollup@^3.23.0: + version "3.23.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.23.0.tgz#b8d6146dac4bf058ee817f92820988e9b358b564" + integrity sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ== + optionalDependencies: + fsevents "~2.3.2" -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= +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: - is-promise "^2.1.0" - -rxjs@^6.4.0: - version "6.5.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" - integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== - dependencies: - tslib "^1.9.0" + 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: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +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== -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +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" -semver@^5.5.0, semver@^5.7.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.1.2: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -serve-handler@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.0.tgz#f1606dc6ff8f9029a1ee042c11dfe7903a5cb92e" - integrity sha512-63N075Tn3PsFYcu0NVV7tb367UbiW3gnC+/50ohL4oqOhAG6bmbaWqiRcXQgbzqc0ALBjSAzg7VTfa0Qw4E3hA== +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.0.4" + minimatch "3.1.2" path-is-inside "1.0.2" path-to-regexp "2.2.1" range-parser "1.2.0" -serve@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/serve/-/serve-11.1.0.tgz#1bfe2f4a08d0130cbf44711cdb7996cb742172e0" - integrity sha512-+4wpDtOSS+4ZLyDWMxThutA3iOTawX2+yDovOI8cjOUOmemyvNlHyFAsezBlSgbZKTYChI3tzA1Mh0z6XZ62qA== +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.6.0" - ajv "6.5.3" - arg "2.0.0" - boxen "1.3.0" - chalk "2.4.1" - clipboardy "1.2.3" - compression "1.7.3" - serve-handler "6.1.0" - update-check "1.5.2" + "@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" -set-blocking@^2.0.0: +shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: - shebang-regex "^1.0.0" + shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +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.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= +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== -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== +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: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= +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: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== +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: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + ansi-regex "^5.0.1" -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== +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: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" + ansi-regex "^6.0.1" -string.prototype.trimleft@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" - integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.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== -string.prototype.trimright@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" - integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" +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-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-json-comments@2.0.1, strip-json-comments@~2.0.1: +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 sha1-PFMZQukIwml8DsNEhYwobHygpgo= + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== - -supports-color@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" - integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== +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 "^3.0.0" + has-flag "^4.0.0" -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== +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 "^3.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" + 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 sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== +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: - os-tmpdir "~1.0.2" + is-number "^7.0.0" -tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= +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.1.2" + 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== -update-check@1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28" - integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ== +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.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + 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" -v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@1.3.1, which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== +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" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +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 "^1.0.2 || 2" + string-width "^5.0.1" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== +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: - string-width "^2.1.1" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= +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: - string-width "^1.0.1" - strip-ansi "^3.0.1" + 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 sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== +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: - mkdirp "^0.5.1" + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yargs-parser@13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b" - integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw== +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: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^13.0.0: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-unparser@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.5.0.tgz#f2bb2a7e83cbc87bb95c8e572828a06c9add6e0d" - integrity sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw== - dependencies: - flat "^4.1.0" - lodash "^4.17.11" - yargs "^12.0.5" - -yargs@13.2.2: - version "13.2.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993" - integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA== - dependencies: - cliui "^4.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.0.0" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" -yargs@^12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" +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/utils/check_copyright.py b/utils/check_copyright.py index e3e74bc..a849d04 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # coding=utf-8 # Copyright (c) 2016 Google Inc. #
diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py index e1ca0b7..e44294f 100755 --- a/utils/check_symbol_exports.py +++ b/utils/check_symbol_exports.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2017 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/fixup_fuzz_result.py b/utils/fixup_fuzz_result.py index 9fe54a3..5b14a7d 100755 --- a/utils/fixup_fuzz_result.py +++ b/utils/fixup_fuzz_result.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2018 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/generate_changelog.py b/utils/generate_changelog.py index 54db728..348bc50 100644 --- a/utils/generate_changelog.py +++ b/utils/generate_changelog.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2023 Google Inc. #
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index 6b7167b..88534ff 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2016 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License"); @@ -540,7 +540,7 @@ # We have a few operand kinds that require their optional counterpart to # exist in the operand info table. - optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat'] + optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat', 'CooperativeMatrixOperands', 'RawAccessChainOperands'] optional_enums = [e for e in enums if e[0] in optional_enums] enums.extend(optional_enums)
diff --git a/utils/generate_language_headers.py b/utils/generate_language_headers.py index 83fa99e..18a8d5e 100755 --- a/utils/generate_language_headers.py +++ b/utils/generate_language_headers.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2017 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/generate_registry_tables.py b/utils/generate_registry_tables.py index 28152ef..69628fa 100755 --- a/utils/generate_registry_tables.py +++ b/utils/generate_registry_tables.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2016 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,9 @@ """Generates the vendor tool table from the SPIR-V XML registry.""" import errno +import io import os.path -import xml.etree.ElementTree +from xml.etree.ElementTree import XML, XMLParser, TreeBuilder def mkdir_p(directory): @@ -78,8 +79,9 @@ help='output file for SPIR-V generators table') args = parser.parse_args() - with open(args.xml) as xml_in: - registry = xml.etree.ElementTree.fromstring(xml_in.read()) + with io.open(args.xml, encoding='utf-8') as xml_in: + parser = XMLParser(target=TreeBuilder(), encoding='utf-8') + registry = XML(xml_in.read(), parser=parser) mkdir_p(os.path.dirname(args.generator_output)) with open(args.generator_output, 'w') as f:
diff --git a/utils/generate_vim_syntax.py b/utils/generate_vim_syntax.py index da7e99b..5c9c6b2 100755 --- a/utils/generate_vim_syntax.py +++ b/utils/generate_vim_syntax.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2016 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh index 6289c94..d19ee00 100755 --- a/utils/roll_deps.sh +++ b/utils/roll_deps.sh
@@ -29,7 +29,6 @@ } -# We are not rolling google test for now. The latest version requires C++14. dependencies=("external/effcee/" "external/googletest/" "external/re2/"
diff --git a/utils/update_build_version.py b/utils/update_build_version.py index 5a78ada..bb66e18 100755 --- a/utils/update_build_version.py +++ b/utils/update_build_version.py
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2016 Google Inc. # @@ -62,9 +62,7 @@ def command_output(cmd, directory): """Runs a command in a directory and returns its standard output stream. - Captures the standard error stream. - - Raises a RuntimeError if the command fails to launch or otherwise fails. + Returns (False, None) if the command fails to launch or otherwise fails. """ try: # Set shell=True on Windows so that Chromium's git.bat can be found when @@ -74,11 +72,10 @@ stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=os.name == 'nt') - (stdout, stderr) = p.communicate() + (stdout, _) = p.communicate() if p.returncode != 0: - logging.error('Failed to run "{}" in "{}": {}'.format(cmd, directory, stderr.decode())) + return False, None except Exception as e: - logging.error('Failed to run "{}" in "{}": {}'.format(cmd, directory, str(e))) return False, None return p.returncode == 0, stdout @@ -111,26 +108,30 @@ Runs 'git describe', or alternately 'git rev-parse HEAD', in directory. If successful, returns the output; otherwise returns 'unknown hash, <date>'.""" - success, output = command_output(['git', 'describe'], repo_path) - if not success: - output = command_output(['git', 'rev-parse', 'HEAD'], repo_path) - + # if we're in a git repository, attempt to extract version info + success, output = command_output(["git", "rev-parse", "--show-toplevel"], repo_path) if success: - # decode() is needed here for Python3 compatibility. In Python2, - # str and bytes are the same type, but not in Python3. - # Popen.communicate() returns a bytes instance, which needs to be - # decoded into text data first in Python3. And this decode() won't - # hurt Python2. - return output.rstrip().decode() + success, output = command_output(["git", "describe", "--tags", "--match=v*", "--long"], repo_path) + if not success: + success, output = command_output(["git", "rev-parse", "HEAD"], repo_path) + + if success: + # decode() is needed here for Python3 compatibility. In Python2, + # str and bytes are the same type, but not in Python3. + # Popen.communicate() returns a bytes instance, which needs to be + # decoded into text data first in Python3. And this decode() won't + # hurt Python2. + return output.rstrip().decode() # This is the fallback case where git gives us no information, - # e.g. because the source tree might not be in a git tree. + # e.g. because the source tree might not be in a git tree or + # git is not available on the system. # In this case, usually use a timestamp. However, to ensure # reproducible builds, allow the builder to override the wall # clock time with environment variable SOURCE_DATE_EPOCH # containing a (presumably) fixed timestamp. timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) - iso_date = datetime.datetime.utcfromtimestamp(timestamp).isoformat() + iso_date = datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc).isoformat() return "unknown hash, {}".format(iso_date) def main():