Squashed 'third_party/SPIRV-Tools/' changes from e9dc2c8ce..51892874b

51892874b Run ADCE when the printf extension is used. (#5215)
e803fe671 Don't convert struct members to half (#5201)
d6310f416 Roll external/googletest/ a3580180d..bc860af08 (1 commit) (#5216)
8993f9f52 Apply scalar replacement on vars with Pointer decorations (#5208)
01055c60c Update the contributing guidelines (#5212)
65f03bea4 Roll external/spirv-headers/ 7f1d2f415..268a06176 (2 commits) (#5214)
2189ad7a5 spirv-tools: Add support for QNX (#5211)
17be6bb7c Roll external/googletest/ f345b2ca6..a3580180d (2 commits) (#5210)
a525dccbe roll deps (#5209)
0ce36ad78 instrument: Add set and binding to bindless error records (#5204)
08d8fae58 Roll external/googletest/ 0bdaac5a1..797b0ad2a (1 commit) (#5207)
8e1001e93 Roll external/googletest/ dea0484e4..0bdaac5a1 (1 commit) (#5206)
baa46e103 Update spirv_headers to include SPV_KHR_ray_tracing_position_fetch (#5205)
6110f30a3 Roll external/googletest/ 783d00fd1..ccdeec888 (1 commit) (#5203)
d4c0abdca instrument: Change descriptor state storage format (#5178)
9e627132a Roll external/googletest/ 12a5852e4..783d00fd1 (9 commits) (#5198)
25ad5e19f Do not define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE if it is already defined. (#5200)
bec566a32 opt: Fix null deref in OpMatrixTimesVector and OpVectorTimesMatrix (#5199)
d5f69dba5 Remove dead code (#5195)
55bc37f82 Roll external/googletest/ 8fa9461cc..12a5852e4 (1 commit) (#5193)
6f276e05c Add support for SPV_EXT_shader_tile_image (#5188)
1877a7f90 Fix vector OpConstantComposite type validation (#5191)
f449fb4ad Add default case for spv::Dim for TileImageEXT (#5194)
9a4e7a1eb Update protobuf to v21.12 (#5189)
31c546e31 Roll external/googletest/ 057b4e904..8fa9461cc (2 commits) (#5192)
048faf2e5 Remove use of deprecated std::aligned_storage (#5183)
13ea01b3e Roll external/googletest/ 7ee260c54..057b4e904 (1 commit) (#5190)
dd03c1fca Fix LICMPass (#5087)
97a55196d Roll external/googletest/ b5fd99bbd..7ee260c54 (1 commit) (#5187)
ac335c763 roll deps (#5185)
a2db3fb17 kokoro: always chown files (#5186)
6b72fe20c Add missing header guard (#5181)
7ab0a2781 Remove non-ascii char from comments. (#5182)
f5ff7ae4a Roll external/googletest/ 88af49efa..ca0d46e95 (4 commits) (#5180)
a0fcd06f8 Add Vulkan memory model to allow lists (#5173)
d8a8af8e6 spirv-val: Remove unused includes and code (#5176)
4680d2bef Roll external/googletest/ e9fb5c7ba..88af49efa (1 commit) (#5179)
fa69b09cf spirv-opt: Remove unused includes and code (#5177)
d24a39a7f Do not remove control barrier after spv1.3 (#5174)
f9f31fa5a Roll external/googletest/ 6f01e3dc1..e9fb5c7ba (2 commits) (#5175)
b4e0850ef Ignore NonSemanticInstructions (#5171)
32a4d840b roll deps (#5172)
9fbe1738b Update SPIRV-Headers deps (#5170)
5f4e694e1 Implement source extraction logic for spirv-objdump (#5150)
5aab2a8fe Include stdint.h (#5164)
1a52c14ee Roll external/googletest/ fa8720982..974e18ee6 (2 commits) (#5168)
90f6a2875 Roll external/googletest/ 471087fbf..fa8720982 (2 commits) (#5167)
4dc3edeb9 build: set std=c++17 for BUILD.gn (#5162)
f56f2b6fd roll deps (#5166)
9c9144b65 ci: fix dependency updater bot (#5165)
ae435df4a git: add release workflow (#5158)
44bc9bd2a build: cmake: support passing a custom source folder for GoogleTest (#5157)
8e6563b91 spirv-val: Label new Vulkan VUID 07951 (#5154)
9743701ed Explicitly #include <utility> in spirv_target_env.cpp (#5159)
25f6c0cea Revert "Add workflow for github releases. (#5145)" (#5156)
a45a4cd21 Roll external/googletest/ 038e392eb..391ce627d (1 commit) (#5153)
a1f555651 build(deps): bump y18n from 4.0.0 to 4.0.3 in /tools/sva (#5155)
25b7608cb Add workflow for github releases. (#5145)
fcd53c8ed Add spirv-dump tool (#5146)
7cefd5f5f Change default permissions for workflows (#5149)
44d72a9b3 Prepare release v2023.2 (#5151)
fba26fd60 roll deps (#5152)
01828dac7 Roll external/googletest/ 48a1b1105..50e07d1c9 (1 commit) (#5148)
b029037aa Revert changes around CHANGES file. (#5143)
c069a5c7e roll deps (#5144)
42267721e build: tool to generate a CHANGELOG from tags (#5136)
db0c3d83d Roll external/googletest/ e1ee0fa3e..a798c2f10 (4 commits) (#5142)
690a9a406 Add explicit deduction guide for FlagRegistration (#5141)
3033cf428 Roll external/googletest/ cead3d57c..e1ee0fa3e (9 commits) (#5140)
016bb3ae7 roll deps (#5139)
7cb5898e1 diff: refactorize flag parsing (#5133)
e0df12997 Fix using invalid install path for cmake support files with MINGW platform (#5128) (#5129)
3eff21366 Fix MinGW build error (#4935) (#5127)
bd83b772c Fix operand index out of bounds when folding OpCompositeExtract. (#5107)
5fe3bbe77 Update Effcee so it doesn't hardcode C++11 (#5137)
6c714f191 Roll external/googletest/ c7d0bc830..2d4f20876 (3 commits) (#5130)
d6c707676 linter: refactorize linter flag parsing (#5134)
8cee46198 tools: add uint32_t flags parsing (#5131)
970ba4e8b linker: refactorize flag parsing (#5132)
9d71fb676 Roll external/googletest/ 3d787f5a0..c7d0bc830 (4 commits) (#5126)
9d4c95a57 build: fix build.gn missing comma (#5125)
2e0f4b52c tools: refactorize tools flags parsing. (#5111)
3b2ea1a42 Roll external/googletest/ 39a26e12d..3d787f5a0 (2 commits) (#5123)
b955c468b Add missing header include (#5124)
5d2bc6f06 Fix removal of dependent non-semantic instructions (#5122)
4183faa2e Roll external/googletest/ 750d67d80..39a26e12d (2 commits) (#5120)
2cf48e953 Allow invoking git.bat as git on Windows (#5118)
9017cfcf6 Roll external/googletest/ 7a7231c44..750d67d80 (1 commit) (#5119)
8bcaad28f Roll external/re2/ 9049cd28d..3a8436ac4 (1 commit) (#5117)
987a5f136 build: change the way we set cxx version for bazel. (#5114)
69ed5adf1 Roll external/googletest/ f063cd25c..7a7231c44 (1 commit) (#5116)
c9947cc8d Roll external/googletest/ 2057566e4..f063cd25c (1 commit) (#5115)
956114df2 opt: fix spirv ABI on Linux again. (#5113)
f4343515a Remove duplicate lists of constant and type opcodes (#5106)
95f93810b Roll external/googletest/ b80a07ffe..2057566e4 (1 commit) (#5112)
b84c86f71 libspirv.cpp: adds c++ api for spvBinaryParse (#5109)
e150e716f roll deps (#5110)
722138136 Roll external/re2/ b025c6a3a..891fa6684 (1 commit) (#5108)
5b48c549b Roll external/googletest/ 0570e2d93..b73f27fd1 (1 commit) (#5105)
dfcbf310a Change exec_tools -> tools for local genrule (#5104)
b0504f990 Roll external/googletest/ 3d568bdda..0570e2d93 (2 commits) (#5102)
c965624e3 Roll external/googletest/ 2f2e72bae..3d568bdda (1 commit) (#5100)
0ce2bc4ce spirv-val: Conditional Branch without an exit is invalid in loop header (#5069)

git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: 51892874ba08f3ac0d9b1fcf3893c8516693a88e
Change-Id: I4a42eed4fc8a249a67dce8a793b482ed48d6ebdb
diff --git a/.bazelrc b/.bazelrc
deleted file mode 100644
index 5b3d13f..0000000
--- a/.bazelrc
+++ /dev/null
@@ -1 +0,0 @@
-build --cxxopt=-std=c++17
diff --git a/.github/workflows/autoroll.yml b/.github/workflows/autoroll.yml
index a33034b..4520309 100644
--- a/.github/workflows/autoroll.yml
+++ b/.github/workflows/autoroll.yml
@@ -1,4 +1,6 @@
 name: Update dependencies
+permissions:
+  contents: read
 
 on:
   schedule:
@@ -7,6 +9,9 @@
 
 jobs:
   update-dependencies:
+    permissions:
+      contents: write
+      pull-requests: write
     name: Update dependencies
     runs-on: ubuntu-latest
 
@@ -38,7 +43,6 @@
             echo "changed=true" >> $GITHUB_OUTPUT
           fi
         id: update_dependencies
-     
       - name: Push changes and create PR
         if: steps.update_dependencies.outputs.changed == 'true'
         run: |
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml
index 77ef657..88700c4 100644
--- a/.github/workflows/bazel.yml
+++ b/.github/workflows/bazel.yml
@@ -1,4 +1,6 @@
 name: Build and Test with Bazel
+permissions:
+  contents: read
 
 on:
   push:
@@ -26,7 +28,15 @@
         with:
           path: ~/.bazel/cache
           key: bazel-cache-${{ runner.os }}
-      - name: Build All
-        run: bazel --output_user_root=~/.bazel/cache build //...
-      - name: Test All
-        run: bazel --output_user_root=~/.bazel/cache test //...
+      - 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 //...
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..ada9431
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,24 @@
+name: Create a release branch from release tag
+permissions:
+  contents: write
+
+on:
+  push:
+    tags:
+      - 'v[0-9]+.[0-9]+'
+      - '!v[0-9]+.[0-9]+.rc*'
+
+jobs:
+  prepare-release-job:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Prepare CHANGELOG for version
+        run: |
+          python utils/generate_changelog.py CHANGES "${{ github.ref_name }}" VERSION_CHANGELOG
+      - name: Create release
+        run: |
+          gh release create -t "Release ${{ github.ref_name }}" -F VERSION_CHANGELOG "${{ github.ref_name }}"
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index fa8951a..62c9af3 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -1,4 +1,6 @@
 name: Wasm Build
+permissions:
+  contents: read
 
 on: [push, pull_request]
 
diff --git a/Android.mk b/Android.mk
index 8a70206..a4e7615 100644
--- a/Android.mk
+++ b/Android.mk
@@ -311,9 +311,9 @@
 $(call generate-file-dir,$(1)/dummy_filename)
 $(1)/build-version.inc: \
         $(LOCAL_PATH)/utils/update_build_version.py \
-        $(LOCAL_PATH)
+        $(LOCAL_PATH)/CHANGES
 		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/update_build_version.py \
-		                $(LOCAL_PATH) $(1)/build-version.inc
+		                $(LOCAL_PATH)/CHANGES $(1)/build-version.inc
 		@echo "[$(TARGET_ARCH_ABI)] Generate       : build-version.inc <= CHANGES"
 $(LOCAL_PATH)/source/software_version.cpp: $(1)/build-version.inc
 endef
diff --git a/BUILD.bazel b/BUILD.bazel
index 71399b2..ae7f35c 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -104,10 +104,13 @@
 
 genrule(
     name = "build_version_inc",
+    srcs = ["CHANGES"],
     outs = ["build-version.inc"],
-    cmd = "SOURCE_DATE_EPOCH=0 $(location :update_build_version) $(RULEDIR) $(location build-version.inc)",
-    cmd_bat = "set SOURCE_DATE_EPOCH=0  && $(location :update_build_version) $(RULEDIR) $(location build-version.inc)",
-    exec_tools = [":update_build_version"],
+    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,
 )
 
@@ -283,6 +286,7 @@
     deps = [
         ":spirv_tools_internal",
         ":tools_io",
+        ":tools_util",
     ],
 )
 
@@ -296,6 +300,25 @@
     deps = [
         ":spirv_tools",
         ":tools_io",
+        ":tools_util",
+    ],
+)
+
+cc_binary(
+    name = "spirv-objdump",
+    srcs = [
+        "tools/objdump/objdump.cpp",
+        "tools/objdump/extract_source.cpp",
+        "tools/objdump/extract_source.h",
+    ],
+    copts = COMMON_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":tools_io",
+        ":tools_util",
+        ":spirv_tools_internal",
+        ":spirv_tools_opt_internal",
+        "@spirv_headers//:spirv_cpp_headers",
     ],
 )
 
@@ -355,6 +378,7 @@
         ":spirv_tools_internal",
         ":spirv_tools_link",
         ":tools_io",
+        ":tools_util",
     ],
 )
 
@@ -385,6 +409,7 @@
     deps = [
         ":spirv_tools_internal",
         ":tools_io",
+        ":tools_util",
     ],
 )
 
@@ -414,7 +439,7 @@
     name = "base_{testcase}_test".format(testcase = f[len("test/"):-len("_test.cpp")]),
     size = "small",
     srcs = [f],
-    copts = TEST_COPTS,
+    copts = TEST_COPTS + ['-DTESTING'],
     linkstatic = 1,
     target_compatible_with = {
         "test/timer_test.cpp": incompatible_with(["@bazel_tools//src/conditions:windows"]),
@@ -422,11 +447,12 @@
     deps = [
         ":spirv_tools_internal",
         ":test_lib",
+        "tools_util",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
     ],
 ) for f in glob(
-    ["test/*_test.cpp"],
+    ["test/*_test.cpp", "test/tools/*_test.cpp"],
     exclude = [
         "test/cpp_interface_test.cpp",
         "test/log_test.cpp",
diff --git a/BUILD.gn b/BUILD.gn
index ee3743b..44954b2 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -262,12 +262,12 @@
 action("spvtools_build_version") {
   script = "utils/update_build_version.py"
 
-  repo_path = "."
+  changes_file = "CHANGES"
   inc_file = "${target_gen_dir}/build-version.inc"
 
   outputs = [ inc_file ]
   args = [
-    rebase_path(repo_path, root_build_dir),
+    rebase_path(changes_file, root_build_dir),
     rebase_path(inc_file, root_build_dir),
   ]
 }
@@ -374,6 +374,12 @@
     # Make MSVC report the correct value for __cplusplus
     cflags += [ "/Zc:__cplusplus" ]
   }
+
+  if (!is_win) {
+    cflags += [ "-std=c++17" ]
+  } else {
+    cflags += [ "/std:c++17" ]
+  }
 }
 
 source_set("spvtools_headers") {
@@ -1430,15 +1436,6 @@
   }
 }
 
-source_set("spvtools_util_cli_consumer") {
-  sources = [
-    "tools/util/cli_consumer.cpp",
-    "tools/util/cli_consumer.h",
-  ]
-  deps = [ ":spvtools_headers" ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
 source_set("spvtools_software_version") {
   sources = [ "source/software_version.cpp" ]
   deps = [
@@ -1448,12 +1445,23 @@
   configs += [ ":spvtools_internal_config" ]
 }
 
+source_set("spvtools_tools_util") {
+  sources = [
+    "tools/util/flags.cpp",
+    "tools/util/cli_consumer.cpp",
+    "tools/util/cli_consumer.h",
+  ]
+  deps = [ ":spvtools_headers" ]
+  configs += [ ":spvtools_internal_config" ]
+}
+
 if (spvtools_build_executables) {
   executable("spirv-as") {
     sources = [ "tools/as/as.cpp" ]
     deps = [
       ":spvtools",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
     ]
     configs += [ ":spvtools_internal_config" ]
   }
@@ -1463,6 +1471,7 @@
     deps = [
       ":spvtools",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
     ]
     configs += [ ":spvtools_internal_config" ]
   }
@@ -1472,7 +1481,7 @@
     deps = [
       ":spvtools",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
@@ -1487,6 +1496,7 @@
     deps = [
       ":spvtools",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
     ]
     configs += [ ":spvtools_internal_config" ]
   }
@@ -1497,7 +1507,7 @@
       ":spvtools",
       ":spvtools_opt",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
@@ -1510,6 +1520,7 @@
       ":spvtools_link",
       ":spvtools_opt",
       ":spvtools_software_version",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
@@ -1529,7 +1540,7 @@
       ":spvtools_opt",
       ":spvtools_reduce",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
       "//third_party/protobuf:protobuf_full",
     ]
@@ -1548,7 +1559,7 @@
       ":spvtools_opt",
       ":spvtools_reduce",
       ":spvtools_software_version",
-      ":spvtools_util_cli_consumer",
+      ":spvtools_tools_util",
       ":spvtools_val",
     ]
     configs += [ ":spvtools_internal_config" ]
diff --git a/CHANGES b/CHANGES
index f24da41..dbe31a0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,22 @@
 Revision history for SPIRV-Tools
 
-v2023.2-dev 2023-01-17
-   - Start v2023.2-dev
+v2023.2 2023-03-10
+  - General
+    - build: move from c++11 to c++17 (#4983)
+    - tools: refactorize tools flags parsing. (#5111)
+    - Add C interface for Optimizer (#5030)
+    - libspirv.cpp: adds c++ api for spvBinaryParse (#5109)
+    - build: change the way we set cxx version for bazel. (#5114)
+  - Optimizer
+    - Fix null pointer in FoldInsertWithConstants. (#5093)
+    - Fix removal of dependent non-semantic instructions (#5122)
+    - Remove duplicate lists of constant and type opcodes (#5106)
+    - opt: fix spirv ABI on Linux again. (#5113)
+  - Validator
+    - Validate decoration of structs with RuntimeArray (#5094)
+    - Validate operand type before operating on it (#5092)
+    - spirv-val: Conditional Branch without an exit is invalid in loop header (#5069)
+    - spirv-val: Initial SPV_EXT_mesh_shader builtins (#5080)
 
 v2023.1 2023-01-17
   - General
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1c7b071..71cdc00 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016 The Khronos Group Inc.
+# Copyright (c) 2015-2023 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.
@@ -64,6 +64,8 @@
   add_definitions(-DSPIRV_FUCHSIA)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU")
   add_definitions(-DSPIRV_GNU)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "QNX")
+  add_definitions(-DSPIRV_QNX)
 else()
   message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
 endif()
@@ -270,7 +272,7 @@
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  if(WIN32)
+  if(WIN32 AND NOT MINGW)
     macro(spvtools_config_package_dir TARGET PATH)
       set(${PATH} ${TARGET}/cmake)
     endmacro()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1eb8b68..893998e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,9 +2,8 @@
 
 ## 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)
+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)
 for more.
 
 To report a new bug or request a new feature, please file a GitHub issue. Please
@@ -36,9 +35,9 @@
 
 ## For developers: Contributing a patch
 
-Before we can use your code, you must sign the [Khronos Open Source Contributor
-License Agreement](https://cla-assistant.io/KhronosGroup/SPIRV-Tools) (CLA),
-which you can do online. The CLA is necessary mainly because you own the
+Before we can use your code, you must sign the
+[Khronos Open Source Contributor License Agreement](https://cla-assistant.io/KhronosGroup/SPIRV-Tools)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
 copyright to your changes, even after your contribution becomes part of our
 codebase, so we need your permission to use and distribute your code. We also
 need to be sure of various other things -- for instance that you'll tell us if
@@ -51,16 +50,16 @@
 for instruction on how to get, build, and test the source. Once you have made
 your changes:
 
-*   Ensure the code follows the [Google C++ Style
-    Guide](https://google.github.io/styleguide/cppguide.html). Running
-    `clang-format -style=file -i [modified-files]` can help.
+*   Ensure the code follows the
+    [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
+    Running `clang-format -style=file -i [modified-files]` can help.
 *   Create a pull request (PR) with your patch.
 *   Make sure the PR description clearly identified the problem, explains the
     solution, and references the issue if applicable.
 *   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.
+    `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.
 *   Watch the continuous builds to make sure they pass.
 *   Request a code review.
 
@@ -82,8 +81,8 @@
 The formal code reviews are done on GitHub. Reviewers are to look for all of the
 usual things:
 
-*   Coding style follows the [Google C++ Style
-    Guide](https://google.github.io/styleguide/cppguide.html)
+*   Coding style follows the
+    [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
 *   Identify potential functional problems.
 *   Identify code duplication.
 *   Ensure the unit tests have enough coverage.
@@ -102,84 +101,49 @@
     updated. For example, a new instruction is added, but the def-use manager is
     not updated. Later on, it is possible that the def-use manager will be used,
     and give wrong results.
+*   If a pass gets the id of a type from the type manager, make sure the type is
+    not a struct or array. It there are two structs that look the same, the type
+    manager can return the wrong one.
 
 ## For maintainers: Merging a PR
 
 We intend to maintain a linear history on the GitHub master 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.
+find which commit introduced a bug. The
+[Squash and Merge](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits)
+button on the GitHub web interface. All other ways of merging on the web
+interface have been disabled.
 
-### Initial merge setup
+Before merging, we generally require:
 
-The following steps should be done exactly once (when you are about to merge a
-PR for the first time):
+1.  All tests except for the smoke test pass. See
+    [failing smoke test](#failing-smoke-test).
+1.  The PR is approved by at least one of the maintainers. If the PR modifies
+    different parts of the code, then multiple reviewers might be necessary.
 
-*   It is assumed that upstream points to
-    [git@github.com](mailto:git@github.com):KhronosGroup/SPIRV-Tools.git or
-    https://github.com/KhronosGroup/SPIRV-Tools.git.
+The squash-and-merge button will turn green when these requirements are met.
+Maintainers have the to power to merge even if the button is not green, but that
+is discouraged.
 
-*   Find out the local name for the main github repo in your git configuration.
-    For example, in this configuration, it is labeled `upstream`.
+### Failing smoke test
 
-    ```
-    git remote -v
-    [ ... ]
-    upstream https://github.com/KhronosGroup/SPIRV-Tools.git (fetch)
-    upstream https://github.com/KhronosGroup/SPIRV-Tools.git (push)
-    ```
+The purpose of the smoke test is to let us know if
+[shaderc](https://github.com/google/shaderc) fails to build with the change. If
+it fails, the maintainer needs to determine if the reason for the failure is a
+problem in the current PR or if another repository needs to be changed. Most of
+the time [Glslang](https://github.com/KhronosGroup/glslang) needs to be updated
+to account for the change in SPIR-V Tools.
 
-*   Make sure that the `upstream` remote is set to fetch from the `refs/pull`
-    namespace:
+The PR can still be merged if the problem is not with that PR.
 
-    ```
-    git config --get-all remote.upstream.fetch
-    +refs/heads/*:refs/remotes/upstream/*
-    +refs/pull/*/head:refs/remotes/upstream/pr/*
-    ```
+## For maintainers: Running tests
 
-*   If the line `+refs/pull/*/head:refs/remotes/upstream/pr/*` is not present in
-    your configuration, you can add it with the command:
+For security reasons, not all tests will run automatically. When they do not, a
+maintainer will have to start the tests.
 
-    ```
-    git config --local --add remote.upstream.fetch '+refs/pull/*/head:refs/remotes/upstream/pr/*'
-    ```
+If the Github actions tests do not run on a PR, they can be initiated by closing
+and reopening the PR.
 
-### Merge workflow
-
-The following steps should be done for every PR that you intend to merge:
-
-*   Make sure your local copy of the master branch is up to date:
-
-    ```
-    git checkout master
-    git pull
-    ```
-
-*   Fetch all pull requests refs:
-
-    ```
-    git fetch upstream
-    ```
-
-*   Checkout the particular pull request you are going to review:
-
-    ```
-    git checkout pr/1048
-    ```
-
-*   Rebase the PR on top of the master branch. If there are conflicts, send it
-    back to the author and ask them to rebase. During the interactive rebase be
-    sure to squash all of the commits down to a single commit.
-
-    ```
-    git rebase -i master
-    ```
-
-*   **Build and test the PR.**
-
-*   If all of the tests pass, push the commit `git push upstream HEAD:master`
-
-*   Close the PR and add a comment saying it was push using the commit that you
-    just pushed. See https://github.com/KhronosGroup/SPIRV-Tools/pull/935 as an
-    example.
+If the kokoro tests are not run, they can be run by adding the label
+`kokoro:run` to the PR.
diff --git a/DEPS b/DEPS
index c6be512..9b6039e 100644
--- a/DEPS
+++ b/DEPS
@@ -3,15 +3,15 @@
 vars = {
   'github': 'https://github.com',
 
-  'effcee_revision': 'c7b4db79f340f7a9981e8a484f6d5785e24242d1',
+  'effcee_revision': '66edefd2bb641de8a2f46b476de21f227fc03a28',
 
-  'googletest_revision': '2f2e72bae991138cedd0e3d06a115022736cd568',
+  'googletest_revision': 'bc860af08783b8113005ca7697da5f5d49a8056f',
 
   # Use protobufs before they gained the dependency on abseil
-  'protobuf_revision': 'v3.13.0.1',
+  'protobuf_revision': 'v21.12',
 
-  're2_revision': 'b025c6a3ae05995660e3b882eb3277f4399ced1a',
-  'spirv_headers_revision': 'aa331ab0ffcb3a67021caa1a0c1c9017712f2f31',
+  're2_revision': 'c9cba76063cf4235c1a15dd14a24a4ef8d623761',
+  'spirv_headers_revision': '268a061764ee69f09a477a695bf6a11ffe311b8d',
 }
 
 deps = {
diff --git a/README.md b/README.md
index baa44e7..92e4d3c 100644
--- a/README.md
+++ b/README.md
@@ -380,10 +380,18 @@
 
 ### Build using Bazel
 You can also use [Bazel](https://bazel.build/) to build the project.
+
+On linux:
 ```sh
 cd <spirv-dir>
-bazel build :all
+bazel build --cxxopt=-std=c++17 :all
 ```
+
+On windows:
+```sh
+bazel build --cxxopt=/std:c++17 :all
+```
+
 ### Build a node.js package using Emscripten
 
 The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org)
@@ -723,10 +731,16 @@
 To run a single test target, specify `:my_test_target` instead of `:all`. Test target
 names get printed when you run `bazel test :all`. For example, you can run
 `opt_def_use_test` with:
+
+on linux:
 ```shell
-bazel test :opt_def_use_test
+bazel test --cxxopt=-std=c++17 :opt_def_use_test
 ```
 
+on windows:
+```shell
+bazel test --cxxopt=/std:c++17 :opt_def_use_test
+```
 
 ## Future Work
 <a name="future"></a>
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 676ee97..6ee37d9 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -56,7 +56,9 @@
   if (TARGET gmock)
     message(STATUS "Google Mock already configured")
   else()
-    set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
+    if (NOT GMOCK_DIR)
+      set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
+    endif()
     if(EXISTS ${GMOCK_DIR})
       if(MSVC)
         # Our tests use ::testing::Combine.  Work around a compiler
@@ -73,7 +75,7 @@
       # gtest requires special defines for building as a shared
       # library, simply always build as static.
       push_variable(BUILD_SHARED_LIBS 0)
-      add_subdirectory(${GMOCK_DIR} EXCLUDE_FROM_ALL)
+      add_subdirectory(${GMOCK_DIR} ${CMAKE_CURRENT_BINARY_DIR}/googletest EXCLUDE_FROM_ALL)
       pop_variable(BUILD_SHARED_LIBS)
     endif()
   endif()
@@ -152,7 +154,7 @@
 
   if(NOT TARGET protobuf::libprotobuf OR NOT TARGET protobuf::protoc)
 
-    set(SPIRV_TOOLS_PROTOBUF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf/cmake)
+    set(SPIRV_TOOLS_PROTOBUF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf)
     if (NOT IS_DIRECTORY ${SPIRV_TOOLS_PROTOBUF_DIR})
       message(
           FATAL_ERROR
diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp
index a75561b..448cf8a 100644
--- a/include/spirv-tools/instrument.hpp
+++ b/include/spirv-tools/instrument.hpp
@@ -146,23 +146,29 @@
 // about the validation error.
 //
 // A bindless bounds error will output the index and the bound.
-static const int kInstBindlessBoundsOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 2;
-static const int kInstBindlessBoundsOutUnused = kInstStageOutCnt + 3;
-static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 4;
+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 kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 2;
-static const int kInstBindlessUninitOutUnused2 = kInstStageOutCnt + 3;
-static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 4;
+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 kInstBindlessBuffOOBOutDescIndex = kInstStageOutCnt + 1;
-static const int kInstBindlessBuffOOBOutBuffOff = kInstStageOutCnt + 2;
-static const int kInstBindlessBuffOOBOutBuffSize = kInstStageOutCnt + 3;
-static const int kInstBindlessBuffOOBOutCnt = kInstStageOutCnt + 4;
+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.
@@ -171,7 +177,7 @@
 static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
 
 // Maximum Output Record Member Count
-static const int kInstMaxOutCnt = kInstStageOutCnt + 4;
+static const int kInstMaxOutCnt = kInstStageOutCnt + 6;
 
 // Validation Error Codes
 //
@@ -217,26 +223,37 @@
 // 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 consists of a single array of
-// unsigned integers we will call Data[]. This array is formatted as follows.
+// 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[];
+// };
 //
-// At offset kDebugInputBindlessInitOffset in Data[] is a single uint which
-// gives an offset to the start of the bindless initialization data. More
-// specifically, if the following value is zero, we know that the descriptor at
-// (set = s, binding = b, index = i) is not initialized; if the value is
-// non-zero, and the descriptor points to a buffer, the value is the length of
-// the buffer in bytes and can be used to check for out-of-bounds buffer
-// references:
-// Data[ i + Data[ b + Data[ s + Data[ kDebugInputBindlessInitOffset ] ] ] ]
-static const int kDebugInputBindlessInitOffset = 0;
-
-// At offset kDebugInputBindlessOffsetLengths is some number of uints which
-// provide the bindless length data. More specifically, the number of
-// descriptors at (set=s, binding=b) is:
-// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
-static const int kDebugInputBindlessOffsetLengths = 1;
+// 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
 //
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 84a7726..542b745 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -402,6 +402,19 @@
   uint16_t num_operands;
 } spv_parsed_instruction_t;
 
+typedef struct spv_parsed_header_t {
+  // The magic number of the SPIR-V module.
+  uint32_t magic;
+  // Version number.
+  uint32_t version;
+  // Generator's magic number.
+  uint32_t generator;
+  // IDs bound for this module (0 < id < bound).
+  uint32_t bound;
+  // reserved.
+  uint32_t reserved;
+} spv_parsed_header_t;
+
 typedef struct spv_const_binary_t {
   const uint32_t* code;
   const size_t wordCount;
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 408e3eb..ee6c846 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -31,6 +31,11 @@
     const spv_position_t& /* position */, const char* /* message */
     )>;
 
+using HeaderParser = std::function<spv_result_t(
+    const spv_endianness_t endianess, const spv_parsed_header_t& instruction)>;
+using InstructionParser =
+    std::function<spv_result_t(const spv_parsed_instruction_t& instruction)>;
+
 // C++ RAII wrapper around the C context object spv_context.
 class Context {
  public:
@@ -336,6 +341,23 @@
                    std::string* text,
                    uint32_t options = kDefaultDisassembleOption) const;
 
+  // Parses a SPIR-V binary, specified as counted sequence of 32-bit words.
+  // Parsing feedback is provided via two callbacks provided as std::function.
+  // In a valid parse the parsed-header callback is called once, and
+  // then the parsed-instruction callback is called once for each instruction
+  // in the stream.
+  // Returns true on successful parsing.
+  // If diagnostic is non-null, a diagnostic is emitted on failed parsing.
+  // If diagnostic is null the context's message consumer
+  // will be used to emit any errors. If a callback returns anything other than
+  // SPV_SUCCESS, then that status code is returned, no further callbacks are
+  // issued, and no additional diagnostics are emitted.
+  // This is a wrapper around the C API spvBinaryParse.
+  bool Parse(const std::vector<uint32_t>& binary,
+             const HeaderParser& header_parser,
+             const InstructionParser& instruction_parser,
+             spv_diagnostic* diagnostic = nullptr);
+
   // Validates the given SPIR-V |binary|. Returns true if no issues are found.
   // Otherwise, returns false and communicates issues via the message consumer
   // registered.
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index aa6a614..8bdd4e8 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -525,8 +525,10 @@
 // If |remove_outputs| is true, allow outputs to be removed from the interface.
 // This is only safe if the caller knows that there is no corresponding input
 // variable in the following shader. It is false by default.
-Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface = false,
-                                             bool remove_outputs = false);
+Optimizer::PassToken CreateAggressiveDCEPass();
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface);
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
+                                             bool remove_outputs);
 
 // Creates a remove-unused-interface-variables pass.
 // Removes variables referenced on the |OpEntryPoint| instruction that are not
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh
index 50ad42b..2465d9c 100644
--- a/kokoro/macos-clang-release-bazel/build.sh
+++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -41,9 +41,9 @@
 chmod +x bazel-5.0.0-darwin-x86_64
 
 echo $(date): Build everything...
-./bazel-5.0.0-darwin-x86_64 build :all
+./bazel-5.0.0-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 :all
+./bazel-5.0.0-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 52fbb9e..f2a06e0 100755
--- a/kokoro/scripts/linux/build-docker.sh
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -43,8 +43,10 @@
   mkdir "$dir"
 }
 
-# Get source for dependencies, as specified in the DEPS file
-/usr/bin/python3 utils/git-sync-deps --treeless
+if [ $TOOL != "cmake-smoketest" ]; then
+  # Get source for dependencies, as specified in the DEPS file
+  /usr/bin/python3 utils/git-sync-deps --treeless
+fi
 
 if [ $TOOL = "cmake" ]; then
   using cmake-3.17.2
@@ -191,10 +193,10 @@
   using bazel-5.0.0
 
   echo $(date): Build everything...
-  bazel build :all
+  bazel build --cxxopt=-std=c++17 :all
   echo $(date): Build completed.
 
   echo $(date): Starting bazel test...
-  bazel test :all
+  bazel test --cxxopt=-std=c++17 :all
   echo $(date): Bazel test completed.
 fi
diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh
index 85d4b61..688ba79 100644
--- a/kokoro/scripts/linux/build.sh
+++ b/kokoro/scripts/linux/build.sh
@@ -26,6 +26,18 @@
 TOOL=$3
 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
 
+# chown the given directory to the current user, if it exists.
+# Docker creates files with the root user - this can upset the Kokoro artifact copier.
+function chown_dir() {
+  dir=$1
+  if [[ -d "$dir" ]]; then
+    sudo chown -R "$(id -u):$(id -g)" "$dir"
+  fi
+}
+
+set +e
+# Allow build failures
+
 # "--privileged" is required to run ptrace in the asan builds.
 docker run --rm -i \
   --privileged \
@@ -41,16 +53,11 @@
   --env BUILD_SHA="${BUILD_SHA}" \
   --entrypoint "${SCRIPT_DIR}/build-docker.sh" \
   "gcr.io/shaderc-build/radial-build:latest"
+RESULT=$?
 
-
-# chown the given directory to the current user, if it exists.
-# Docker creates files with the root user - this can upset the Kokoro artifact copier.
-function chown_dir() {
-  dir=$1
-  if [[ -d "$dir" ]]; then
-    sudo chown -R "$(id -u):$(id -g)" "$dir"
-  fi
-}
-
+# This is important. If the permissions are not fixed, kokoro will fail
+# to pull build artifacts, and put the build in tool-failure state, which
+# blocks the logs.
 chown_dir "${ROOT_DIR}/build"
 chown_dir "${ROOT_DIR}/external"
+exit $RESULT
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index b5924f5..acfa0c1 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -195,11 +195,14 @@
   ${spirv-tools_BINARY_DIR}/build-version.inc)
 set(SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR
   ${spirv-tools_SOURCE_DIR}/utils/update_build_version.py)
+set(SPIRV_TOOLS_CHANGES_FILE
+  ${spirv-tools_SOURCE_DIR}/CHANGES)
 add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC}
    COMMAND ${PYTHON_EXECUTABLE}
            ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
-           ${spirv-tools_SOURCE_DIR} ${SPIRV_TOOLS_BUILD_VERSION_INC}
+           ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC}
    DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
+           ${SPIRV_TOOLS_CHANGES_FILE}
    COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).")
 # Convenience target for standalone generation of the build-version.inc file.
 # This is not required for any dependence chain.
diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h
index 46c2188..44aecfd 100644
--- a/source/fuzz/protobufs/spirvfuzz_protobufs.h
+++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -21,6 +21,10 @@
 // of these header files without having to compromise on freedom from warnings
 // in the rest of the project.
 
+#ifndef GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
+#define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE 1
+#endif
+
 #if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunknown-warning-option"  // Must come first
@@ -28,6 +32,8 @@
 #pragma clang diagnostic ignored "-Wshadow"
 #pragma clang diagnostic ignored "-Wsuggest-destructor-override"
 #pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
 #elif defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wconversion"
diff --git a/source/libspirv.cpp b/source/libspirv.cpp
index be76caa..83e8629 100644
--- a/source/libspirv.cpp
+++ b/source/libspirv.cpp
@@ -108,6 +108,40 @@
   return status == SPV_SUCCESS;
 }
 
+struct CxxParserContext {
+  const HeaderParser& header_parser;
+  const InstructionParser& instruction_parser;
+};
+
+bool SpirvTools::Parse(const std::vector<uint32_t>& binary,
+                       const HeaderParser& header_parser,
+                       const InstructionParser& instruction_parser,
+                       spv_diagnostic* diagnostic) {
+  CxxParserContext parser_context = {header_parser, instruction_parser};
+
+  spv_parsed_header_fn_t header_fn_wrapper =
+      [](void* user_data, spv_endianness_t endianness, uint32_t magic,
+         uint32_t version, uint32_t generator, uint32_t id_bound,
+         uint32_t reserved) {
+        CxxParserContext* ctx = reinterpret_cast<CxxParserContext*>(user_data);
+        spv_parsed_header_t header = {magic, version, generator, id_bound,
+                                      reserved};
+
+        return ctx->header_parser(endianness, header);
+      };
+
+  spv_parsed_instruction_fn_t instruction_fn_wrapper =
+      [](void* user_data, const spv_parsed_instruction_t* instruction) {
+        CxxParserContext* ctx = reinterpret_cast<CxxParserContext*>(user_data);
+        return ctx->instruction_parser(*instruction);
+      };
+
+  spv_result_t status = spvBinaryParse(
+      impl_->context, &parser_context, binary.data(), binary.size(),
+      header_fn_wrapper, instruction_fn_wrapper, diagnostic);
+  return status == SPV_SUCCESS;
+}
+
 bool SpirvTools::Validate(const std::vector<uint32_t>& binary) const {
   return Validate(binary.data(), binary.size());
 }
diff --git a/source/opcode.cpp b/source/opcode.cpp
index b1785cc..d26024a 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -240,6 +240,7 @@
     case spv::Op::OpConstantComposite:
     case spv::Op::OpConstantSampler:
     case spv::Op::OpConstantNull:
+    case spv::Op::OpConstantFunctionPointerINTEL:
     case spv::Op::OpSpecConstantTrue:
     case spv::Op::OpSpecConstantFalse:
     case spv::Op::OpSpecConstant:
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 53d13f1..1645638 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -21,10 +21,8 @@
 #include <stack>
 
 #include "source/cfa.h"
-#include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/eliminate_dead_functions_util.h"
 #include "source/opt/ir_builder.h"
-#include "source/opt/iterator.h"
 #include "source/opt/reflect.h"
 #include "source/spirv_constant.h"
 #include "source/util/string_utils.h"
@@ -158,7 +156,8 @@
            "Expecting an import of an extension's instruction set.");
     const std::string extension_name = inst.GetInOperand(0).AsString();
     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
-        extension_name != "NonSemantic.Shader.DebugInfo.100") {
+        (extension_name != "NonSemantic.Shader.DebugInfo.100") &&
+        (extension_name != "NonSemantic.DebugPrintf")) {
       return false;
     }
   }
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp
index d12178e..a9fc8e2 100644
--- a/source/opt/basic_block.cpp
+++ b/source/opt/basic_block.cpp
@@ -16,9 +16,7 @@
 
 #include <ostream>
 
-#include "source/opt/function.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/module.h"
 #include "source/opt/reflect.h"
 #include "source/util/make_unique.h"
 
diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp
index ef7f31f..d6c33e5 100644
--- a/source/opt/block_merge_pass.cpp
+++ b/source/opt/block_merge_pass.cpp
@@ -16,11 +16,8 @@
 
 #include "source/opt/block_merge_pass.h"
 
-#include <vector>
-
 #include "source/opt/block_merge_util.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/iterator.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp
index 63627a2..46bfc90 100644
--- a/source/opt/ccp_pass.cpp
+++ b/source/opt/ccp_pass.cpp
@@ -24,7 +24,6 @@
 
 #include "source/opt/fold.h"
 #include "source/opt/function.h"
-#include "source/opt/module.h"
 #include "source/opt/propagator.h"
 
 namespace spvtools {
diff --git a/source/opt/cfg_cleanup_pass.cpp b/source/opt/cfg_cleanup_pass.cpp
index 6d48637..26fed89 100644
--- a/source/opt/cfg_cleanup_pass.cpp
+++ b/source/opt/cfg_cleanup_pass.cpp
@@ -16,13 +16,9 @@
 // constructs (e.g., unreachable basic blocks, empty control flow structures,
 // etc)
 
-#include <queue>
-#include <unordered_set>
-
 #include "source/opt/cfg_cleanup_pass.h"
 
 #include "source/opt/function.h"
-#include "source/opt/module.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/source/opt/code_sink.cpp b/source/opt/code_sink.cpp
index 35a8df2..9023179 100644
--- a/source/opt/code_sink.cpp
+++ b/source/opt/code_sink.cpp
@@ -14,11 +14,9 @@
 
 #include "code_sink.h"
 
-#include <set>
 #include <vector>
 
 #include "source/opt/instruction.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/util/bit_vector.h"
 
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp
index 516c34b..2610808 100644
--- a/source/opt/const_folding_rules.cpp
+++ b/source/opt/const_folding_rules.cpp
@@ -398,12 +398,14 @@
     if (float_type->width() == 32) {
       for (uint32_t i = 0; i < resultVectorSize; ++i) {
         float result_scalar = 0.0f;
-        const analysis::VectorConstant* c2_vec =
-            c2_components[i]->AsVectorConstant();
-        for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
-          float c1_scalar = c1_components[j]->GetFloat();
-          float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
-          result_scalar += c1_scalar * c2_scalar;
+        if (!c2_components[i]->AsNullConstant()) {
+          const analysis::VectorConstant* c2_vec =
+              c2_components[i]->AsVectorConstant();
+          for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+            float c1_scalar = c1_components[j]->GetFloat();
+            float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<float> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -415,12 +417,14 @@
     } else if (float_type->width() == 64) {
       for (uint32_t i = 0; i < c2_components.size(); ++i) {
         double result_scalar = 0.0;
-        const analysis::VectorConstant* c2_vec =
-            c2_components[i]->AsVectorConstant();
-        for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
-          double c1_scalar = c1_components[j]->GetDouble();
-          double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
-          result_scalar += c1_scalar * c2_scalar;
+        if (!c2_components[i]->AsNullConstant()) {
+          const analysis::VectorConstant* c2_vec =
+              c2_components[i]->AsVectorConstant();
+          for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+            double c1_scalar = c1_components[j]->GetDouble();
+            double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<double> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -491,12 +495,14 @@
       for (uint32_t i = 0; i < resultVectorSize; ++i) {
         float result_scalar = 0.0f;
         for (uint32_t j = 0; j < c1_components.size(); ++j) {
-          float c1_scalar = c1_components[j]
-                                ->AsVectorConstant()
-                                ->GetComponents()[i]
-                                ->GetFloat();
-          float c2_scalar = c2_components[j]->GetFloat();
-          result_scalar += c1_scalar * c2_scalar;
+          if (!c1_components[j]->AsNullConstant()) {
+            float c1_scalar = c1_components[j]
+                                  ->AsVectorConstant()
+                                  ->GetComponents()[i]
+                                  ->GetFloat();
+            float c2_scalar = c2_components[j]->GetFloat();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<float> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
@@ -509,12 +515,14 @@
       for (uint32_t i = 0; i < resultVectorSize; ++i) {
         double result_scalar = 0.0;
         for (uint32_t j = 0; j < c1_components.size(); ++j) {
-          double c1_scalar = c1_components[j]
-                                 ->AsVectorConstant()
-                                 ->GetComponents()[i]
-                                 ->GetDouble();
-          double c2_scalar = c2_components[j]->GetDouble();
-          result_scalar += c1_scalar * c2_scalar;
+          if (!c1_components[j]->AsNullConstant()) {
+            double c1_scalar = c1_components[j]
+                                   ->AsVectorConstant()
+                                   ->GetComponents()[i]
+                                   ->GetDouble();
+            double c2_scalar = c2_components[j]->GetDouble();
+            result_scalar += c1_scalar * c2_scalar;
+          }
         }
         utils::FloatProxy<double> result(result_scalar);
         std::vector<uint32_t> words = result.GetWords();
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index d70e27b..9b4c89a 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/constants.h"
 
-#include <unordered_map>
 #include <vector>
 
 #include "source/opt/ir_context.h"
diff --git a/source/opt/control_dependence.cpp b/source/opt/control_dependence.cpp
index a153cab..3d48139 100644
--- a/source/opt/control_dependence.cpp
+++ b/source/opt/control_dependence.cpp
@@ -16,8 +16,6 @@
 
 #include <cassert>
 #include <tuple>
-#include <utility>
-#include <vector>
 
 #include "source/opt/basic_block.h"
 #include "source/opt/cfg.h"
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index 7a4c1f4..2c4a631 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/source/opt/convert_to_half_pass.cpp
@@ -39,6 +39,13 @@
   return Pass::IsFloat(ty_id, width);
 }
 
+bool ConvertToHalfPass::IsStruct(Instruction* inst) {
+  uint32_t ty_id = inst->type_id();
+  if (ty_id == 0) return false;
+  Instruction* ty_inst = Pass::GetBaseType(ty_id);
+  return (ty_inst->opcode() == spv::Op::OpTypeStruct);
+}
+
 bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) {
   uint32_t r_id = inst->result_id();
   for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false))
@@ -294,6 +301,7 @@
   bool relax = true;
   inst->ForEachInId([&relax, this](uint32_t* idp) {
     Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
+    if (IsStruct(op_inst)) relax = false;
     if (!IsFloat(op_inst, 32)) return;
     if (!IsRelaxed(*idp)) relax = false;
   });
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index feabfba..24a478f 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/source/opt/convert_to_half_pass.h
@@ -45,6 +45,7 @@
   // Return true if |inst| returns scalar, vector or matrix type with base
   // float and |width|
   bool IsFloat(Instruction* inst, uint32_t width);
+  bool IsStruct(Instruction* inst);
 
   // Return true if |inst| is decorated with RelaxedPrecision
   bool IsDecoratedRelaxed(Instruction* inst);
diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp
index 2effc3e..c82db41 100644
--- a/source/opt/convert_to_sampled_image_pass.cpp
+++ b/source/opt/convert_to_sampled_image_pass.cpp
@@ -16,7 +16,6 @@
 
 #include <cctype>
 #include <cstring>
-#include <tuple>
 
 #include "source/opt/ir_builder.h"
 #include "source/util/make_unique.h"
diff --git a/source/opt/dataflow.cpp b/source/opt/dataflow.cpp
index 8d74e41..63737f1 100644
--- a/source/opt/dataflow.cpp
+++ b/source/opt/dataflow.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/dataflow.h"
 
-#include <algorithm>
 #include <cstdint>
 
 namespace spvtools {
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 319b8d1..1526b9e 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -23,7 +23,6 @@
 
 #include "source/cfa.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/iterator.h"
 #include "source/opt/struct_cfg_analysis.h"
 #include "source/util/make_unique.h"
 
diff --git a/source/opt/eliminate_dead_constant_pass.cpp b/source/opt/eliminate_dead_constant_pass.cpp
index d021515..500fd8a 100644
--- a/source/opt/eliminate_dead_constant_pass.cpp
+++ b/source/opt/eliminate_dead_constant_pass.cpp
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "source/opt/def_use_manager.h"
-#include "source/opt/ir_context.h"
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
 
diff --git a/source/opt/eliminate_dead_functions_util.cpp b/source/opt/eliminate_dead_functions_util.cpp
index cf7f92f..e95b7f6 100644
--- a/source/opt/eliminate_dead_functions_util.cpp
+++ b/source/opt/eliminate_dead_functions_util.cpp
@@ -37,7 +37,9 @@
               assert(inst->IsNonSemanticInstruction());
               if (to_kill.find(inst) != to_kill.end()) return;
               std::unique_ptr<Instruction> clone(inst->Clone(context));
-              context->ForgetUses(inst);
+              // Clear uses of "inst" to in case this moves a dependent chain of
+              // instructions.
+              context->get_def_use_mgr()->ClearInst(inst);
               context->AnalyzeDefUse(clone.get());
               if (first_func) {
                 context->AddGlobalValue(std::move(clone));
diff --git a/source/opt/eliminate_dead_io_components_pass.cpp b/source/opt/eliminate_dead_io_components_pass.cpp
index 916fc27..5553a33 100644
--- a/source/opt/eliminate_dead_io_components_pass.cpp
+++ b/source/opt/eliminate_dead_io_components_pass.cpp
@@ -15,11 +15,9 @@
 
 #include "source/opt/eliminate_dead_io_components_pass.h"
 
-#include <set>
 #include <vector>
 
 #include "source/opt/instruction.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/util/bit_vector.h"
 
diff --git a/source/opt/eliminate_dead_output_stores_pass.cpp b/source/opt/eliminate_dead_output_stores_pass.cpp
index f2f64f8..99711a1 100644
--- a/source/opt/eliminate_dead_output_stores_pass.cpp
+++ b/source/opt/eliminate_dead_output_stores_pass.cpp
@@ -219,7 +219,7 @@
         var_id, [this, &var, is_builtin](Instruction* user) {
           auto op = user->opcode();
           if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
-              op == spv::Op::OpDecorate)
+              op == spv::Op::OpDecorate || user->IsNonSemanticInstruction())
             return;
           if (is_builtin)
             KillAllDeadStoresOfBuiltinRef(user, &var);
diff --git a/source/opt/eliminate_dead_output_stores_pass.h b/source/opt/eliminate_dead_output_stores_pass.h
index 13785f3..676d4f4 100644
--- a/source/opt/eliminate_dead_output_stores_pass.h
+++ b/source/opt/eliminate_dead_output_stores_pass.h
@@ -51,14 +51,8 @@
   void InitializeElimination();
 
   // Do dead output store analysis
-  Status DoDeadOutputStoreAnalysis();
-
-  // Do dead output store analysis
   Status DoDeadOutputStoreElimination();
 
-  // Mark all locations live
-  void MarkAllLocsLive();
-
   // Kill all stores resulting from |ref|.
   void KillAllStoresOfRef(Instruction* ref);
 
diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp
index 2a1c006..07e053b 100644
--- a/source/opt/feature_manager.cpp
+++ b/source/opt/feature_manager.cpp
@@ -14,8 +14,6 @@
 
 #include "source/opt/feature_manager.h"
 
-#include <queue>
-#include <stack>
 #include <string>
 
 #include "source/enum_string_mapping.h"
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp
index 3c234c4..453756f 100644
--- a/source/opt/fold.cpp
+++ b/source/opt/fold.cpp
@@ -21,7 +21,6 @@
 #include "source/opt/const_folding_rules.h"
 #include "source/opt/def_use_manager.h"
 #include "source/opt/folding_rules.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
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 132be0c..f6d6155 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -15,12 +15,9 @@
 #include "source/opt/fold_spec_constant_op_and_composite_pass.h"
 
 #include <algorithm>
-#include <initializer_list>
 #include <tuple>
 
 #include "source/opt/constants.h"
-#include "source/opt/fold.h"
-#include "source/opt/ir_context.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index 1a4c03d..7730ac1 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/folding_rules.h"
 
-#include <climits>
 #include <limits>
 #include <memory>
 #include <utility>
@@ -1656,8 +1655,11 @@
 
   analysis::Type* result_type = type_mgr->GetType(inst->type_id());
   if (result_type->AsVector() == nullptr) {
-    uint32_t id = inst->GetSingleWordInOperand(result_index);
-    return {Operand(SPV_OPERAND_TYPE_ID, {id})};
+    if (result_index < inst->NumInOperands()) {
+      uint32_t id = inst->GetSingleWordInOperand(result_index);
+      return {Operand(SPV_OPERAND_TYPE_ID, {id})};
+    }
+    return {};
   }
 
   // If the result type is a vector, then vector operands are concatenated.
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index 6c7c949..2ee88ec 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -15,9 +15,7 @@
 #include "source/opt/function.h"
 
 #include <ostream>
-#include <sstream>
 
-#include "function.h"
 #include "ir_context.h"
 #include "source/util/bit_vector.h"
 
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index da2764f..8fff8a0 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -141,18 +141,12 @@
 
 #include "graphics_robust_access_pass.h"
 
-#include <algorithm>
-#include <cstring>
 #include <functional>
 #include <initializer_list>
-#include <limits>
 #include <utility>
 
-#include "constants.h"
-#include "def_use_manager.h"
 #include "function.h"
 #include "ir_context.h"
-#include "module.h"
 #include "pass.h"
 #include "source/diagnostic.h"
 #include "source/util/make_unique.h"
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp
index cd712f0..e8c412f 100644
--- a/source/opt/inst_bindless_check_pass.cpp
+++ b/source/opt/inst_bindless_check_pass.cpp
@@ -16,6 +16,8 @@
 
 #include "inst_bindless_check_pass.h"
 
+#include "source/spirv_constant.h"
+
 namespace spvtools {
 namespace opt {
 namespace {
@@ -40,37 +42,521 @@
 constexpr int kSpvTypeImageSampled = 5;
 }  // namespace
 
-uint32_t InstBindlessCheckPass::GenDebugReadLength(
-    uint32_t var_id, InstructionBuilder* builder) {
-  uint32_t desc_set_idx =
-      var2desc_set_[var_id] + kDebugInputBindlessOffsetLengths;
-  uint32_t desc_set_idx_id = builder->GetUintConstantId(desc_set_idx);
-  uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]);
-  return GenDebugDirectRead({desc_set_idx_id, binding_idx_id}, builder);
+void InstBindlessCheckPass::SetupInputBufferIds() {
+  if (input_buffer_id_ != 0) {
+    return;
+  }
+  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;
+
+  // 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"));
+
+  // 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);
+
+  // 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 = {
+      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+       {uint32_t(spv::StorageClass::StorageBuffer)}},
+  };
+  auto new_var_op = spvtools::MakeUnique<Instruction>(
+      context(), spv::Op::OpVariable, input_buffer_ptr_id_, input_buffer_id_,
+      var_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;
+  // Make sure function doesn't get processed by
+  // InstrumentPass::InstProcessCallTreeFromRoots()
+  param2output_func_id_[3] = func_id;
+  return read_init_func_id_;
+}
+
+// clang-format off
+// GLSL:
+// result = inst_bindless_read_desc_init(desc_set_id, binding_id, desc_idx_id);
+//
+// clang-format on
 uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id,
                                                  uint32_t desc_idx_id,
                                                  InstructionBuilder* builder) {
-  uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]);
-  uint32_t u_desc_idx_id = GenUintCastCode(desc_idx_id, builder);
-  // If desc index checking is not enabled, we know the offset of initialization
-  // entries is 1, so we can avoid loading this value and just add 1 to the
-  // descriptor set.
-  if (!desc_idx_enabled_) {
-    uint32_t desc_set_idx_id =
-        builder->GetUintConstantId(var2desc_set_[var_id] + 1);
-    return GenDebugDirectRead({desc_set_idx_id, binding_idx_id, u_desc_idx_id},
-                              builder);
-  } else {
-    uint32_t desc_set_base_id =
-        builder->GetUintConstantId(kDebugInputBindlessInitOffset);
-    uint32_t desc_set_idx_id =
-        builder->GetUintConstantId(var2desc_set_[var_id]);
-    return GenDebugDirectRead(
-        {desc_set_base_id, desc_set_idx_id, binding_idx_id, u_desc_idx_id},
-        builder);
-  }
+  const uint32_t func_id = GenDebugReadInitFunctionId();
+  const std::vector<uint32_t> args = {
+      builder->GetUintConstantId(var2desc_set_[var_id]),
+      builder->GetUintConstantId(var2binding_[var_id]),
+      GenUintCastCode(desc_idx_id, builder)};
+  return GenReadFunctionCall(func_id, args, builder);
 }
 
 uint32_t InstBindlessCheckPass::CloneOriginalImage(
@@ -248,9 +734,18 @@
             ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx);
         break;
       default:
-        ref->desc_idx_id = 0;
         break;
     }
+    auto decos =
+        context()->get_decoration_mgr()->GetDecorationsFor(ref->var_id, false);
+    for (const auto& deco : decos) {
+      spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u));
+      if (d == spv::Decoration::DescriptorSet) {
+        ref->set = deco->GetSingleWordInOperand(2u);
+      } else if (d == spv::Decoration::Binding) {
+        ref->binding = deco->GetSingleWordInOperand(2u);
+      }
+    }
     return true;
   }
   // Reference is not load or store. If not an image-based reference, return.
@@ -300,6 +795,16 @@
     // TODO(greg-lunarg): Handle additional possibilities?
     return false;
   }
+  auto decos =
+      context()->get_decoration_mgr()->GetDecorationsFor(ref->var_id, false);
+  for (const auto& deco : decos) {
+    spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u));
+    if (d == spv::Decoration::DescriptorSet) {
+      ref->set = deco->GetSingleWordInOperand(2u);
+    } else if (d == spv::Decoration::Binding) {
+      ref->binding = deco->GetSingleWordInOperand(2u);
+    }
+  }
   return true;
 }
 
@@ -542,27 +1047,29 @@
   // Gen invalid block
   new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
   builder.SetInsertPoint(&*new_blk_ptr);
-  uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder);
+  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
-    uint32_t u_offset_id = GenUintCastCode(offset_id, &builder);
-    uint32_t u_length_id = GenUintCastCode(length_id, &builder);
     GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
-                        {error_id, u_index_id, u_offset_id, u_length_id},
+                        {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
-    uint32_t u_length_id = GenUintCastCode(length_id, &builder);
-    GenDebugStreamWrite(
-        uid2offset_[ref->ref_inst->unique_id()], stage_idx,
-        {error_id, u_index_id, u_length_id, builder.GetUintConstantId(0)},
-        &builder);
+    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
-    uint32_t u_length_id = GenUintCastCode(length_id, &builder);
-    GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
-                        {error_id, u_index_id, u_length_id}, &builder);
+    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) {
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h
index e6e6ef4..f89af02 100644
--- a/source/opt/inst_bindless_check_pass.h
+++ b/source/opt/inst_bindless_check_pass.h
@@ -110,10 +110,14 @@
       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.
@@ -124,14 +128,16 @@
   // AnalyzeDescriptorReference. It is necessary and sufficient for further
   // analysis and regeneration of the reference.
   typedef struct RefAnalysis {
-    uint32_t desc_load_id;
-    uint32_t image_id;
-    uint32_t load_id;
-    uint32_t ptr_id;
-    uint32_t var_id;
-    uint32_t desc_idx_id;
-    uint32_t strg_class;
-    Instruction* ref_inst;
+    uint32_t desc_load_id{0};
+    uint32_t image_id{0};
+    uint32_t load_id{0};
+    uint32_t ptr_id{0};
+    uint32_t var_id{0};
+    uint32_t set{0};
+    uint32_t binding{0};
+    uint32_t desc_idx_id{0};
+    uint32_t strg_class{0};
+    Instruction* ref_inst{nullptr};
   } RefAnalysis;
 
   // Return size of type |ty_id| in bytes. Use |matrix_stride| and |col_major|
@@ -201,6 +207,13 @@
 
   // 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};
 };
 
 }  // namespace opt
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
index c18f91d..4954706 100644
--- a/source/opt/inst_buff_addr_check_pass.cpp
+++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -19,6 +19,24 @@
 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)
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index fb43c39..2ec212b 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -41,6 +41,9 @@
 
   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.
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 22736bf..d50e625 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -906,7 +906,7 @@
 bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); }
 
 bool Instruction::IsConstant() const {
-  return IsCompileTimeConstantInst(opcode());
+  return IsConstantInst(opcode()) && !IsSpecConstantInst(opcode());
 }
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index c6e4050..9233ffd 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -564,6 +564,19 @@
   return type->AsRuntimeArray();
 }
 
+analysis::Array* InstrumentPass::GetArray(const analysis::Type* element,
+                                          uint32_t length) {
+  uint32_t length_id = context()->get_constant_mgr()->GetUIntConstId(length);
+  analysis::Array::LengthInfo length_info{
+      length_id, {analysis::Array::LengthInfo::Case::kConstant, length}};
+
+  analysis::Array r(element, length_info);
+
+  analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&r);
+  assert(type && type->AsArray());
+  return type->AsArray();
+}
+
 analysis::Function* InstrumentPass::GetFunction(
     const analysis::Type* return_val,
     const std::vector<const analysis::Type*>& args) {
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index 1311929..4bbbb09 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -270,6 +270,7 @@
   analysis::Integer* GetInteger(uint32_t width, bool is_signed);
   analysis::Struct* GetStruct(const std::vector<const analysis::Type*>& fields);
   analysis::RuntimeArray* GetRuntimeArray(const analysis::Type* element);
+  analysis::Array* GetArray(const analysis::Type* element, uint32_t size);
   analysis::Function* GetFunction(
       const analysis::Type* return_val,
       const std::vector<const analysis::Type*>& args);
@@ -339,8 +340,8 @@
   // If code is generated for an instruction, replace the instruction's
   // block with the new blocks that are generated. Continue processing at the
   // top of the last new block.
-  bool InstrumentFunction(Function* func, uint32_t stage_idx,
-                          InstProcessFunction& pfn);
+  virtual bool InstrumentFunction(Function* func, uint32_t stage_idx,
+                                  InstProcessFunction& pfn);
 
   // Call |pfn| on all functions in the call tree of the function
   // ids in |roots|.
diff --git a/source/opt/interface_var_sroa.h b/source/opt/interface_var_sroa.h
index df7511b..45ed371 100644
--- a/source/opt/interface_var_sroa.h
+++ b/source/opt/interface_var_sroa.h
@@ -90,10 +90,6 @@
   // |component|. Returns true whether the component exists or not.
   bool GetVariableComponent(Instruction* var, uint32_t* component);
 
-  // Returns the interface variable instruction whose result id is
-  // |interface_var_id|.
-  Instruction* GetInterfaceVariable(uint32_t interface_var_id);
-
   // Returns the type of |var| as an instruction.
   Instruction* GetTypeOfVariable(Instruction* var);
 
diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp
index bb6f610..2ec2147 100644
--- a/source/opt/interp_fixup_pass.cpp
+++ b/source/opt/interp_fixup_pass.cpp
@@ -19,7 +19,6 @@
 #include <set>
 #include <string>
 
-#include "ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "type_manager.h"
 
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index 93289a6..48e08ee 100644
--- a/source/opt/ir_builder.h
+++ b/source/opt/ir_builder.h
@@ -480,9 +480,16 @@
     return AddInstruction(std::move(new_inst));
   }
 
-  Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id) {
+  Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id,
+                       uint32_t alignment = 0) {
     std::vector<Operand> operands;
     operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}});
+    if (alignment != 0) {
+      operands.push_back(
+          {SPV_OPERAND_TYPE_MEMORY_ACCESS,
+           {static_cast<uint32_t>(spv::MemoryAccessMask::Aligned)}});
+      operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}});
+    }
 
     // TODO(1841): Handle id overflow.
     std::unique_ptr<Instruction> new_inst(
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 889a671..26501c2 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -19,7 +19,6 @@
 #include "OpenCLDebugInfo100.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/log.h"
-#include "source/opt/mem_pass.h"
 #include "source/opt/reflect.h"
 
 namespace spvtools {
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 35075de..8419ee7 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -645,6 +645,17 @@
   // all have the same stage.
   spv::ExecutionModel GetStage();
 
+  // Returns true of the current target environment is at least that of the
+  // given environment.
+  bool IsTargetEnvAtLeast(spv_target_env env) {
+    // A bit of a hack. We assume that the target environments are appended to
+    // the enum, so that there is an appropriate order.
+    return syntax_context_->target_env >= env;
+  }
+
+  // Return the target environment for the current context.
+  spv_target_env GetTargetEnv() const { return syntax_context_->target_env; }
+
  private:
   // Builds the def-use manager from scratch, even if it was already valid.
   void BuildDefUseManager() {
diff --git a/source/opt/licm_pass.cpp b/source/opt/licm_pass.cpp
index 514518b..f2a6e4d 100644
--- a/source/opt/licm_pass.cpp
+++ b/source/opt/licm_pass.cpp
@@ -15,7 +15,6 @@
 #include "source/opt/licm_pass.h"
 
 #include <queue>
-#include <utility>
 
 #include "source/opt/module.h"
 #include "source/opt/pass.h"
@@ -85,7 +84,7 @@
   bool modified = false;
   std::function<bool(Instruction*)> hoist_inst =
       [this, &loop, &modified](Instruction* inst) {
-        if (loop->ShouldHoistInstruction(this->context(), inst)) {
+        if (loop->ShouldHoistInstruction(*inst)) {
           if (!HoistInstruction(loop, inst)) {
             return false;
           }
diff --git a/source/opt/liveness.cpp b/source/opt/liveness.cpp
index fdf3f4e..336f3ae 100644
--- a/source/opt/liveness.cpp
+++ b/source/opt/liveness.cpp
@@ -309,7 +309,7 @@
     def_use_mgr->ForEachUser(var_id, [this, &var](Instruction* user) {
       auto op = user->opcode();
       if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
-          op == spv::Op::OpDecorate) {
+          op == spv::Op::OpDecorate || user->IsNonSemanticInstruction()) {
         return;
       }
       MarkRefLive(user, &var);
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 66e8813..6ec0c2d 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -16,7 +16,6 @@
 
 #include "source/opt/local_access_chain_convert_pass.h"
 
-#include "ir_builder.h"
 #include "ir_context.h"
 #include "iterator.h"
 #include "source/util/string_utils.h"
@@ -398,60 +397,36 @@
 
 void LocalAccessChainConvertPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      // SPV_KHR_variable_pointers
-      //   Currently do not support extended pointer expressions
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_demote_to_helper_invocation",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_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",
-  });
+  extensions_allowlist_.insert(
+      {"SPV_AMD_shader_explicit_vertex_parameter",
+       "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
+       "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
+       "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
+       "SPV_KHR_subgroup_vote", "SPV_KHR_8bit_storage", "SPV_KHR_16bit_storage",
+       "SPV_KHR_device_group", "SPV_KHR_multiview",
+       "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2",
+       "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage",
+       "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
+       "SPV_KHR_storage_buffer_storage_class",
+       // SPV_KHR_variable_pointers
+       //   Currently do not support extended pointer expressions
+       "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
+       "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export",
+       "SPV_EXT_shader_viewport_index_layer",
+       "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
+       "SPV_EXT_fragment_fully_covered", "SPV_AMD_gpu_shader_half_float_fetch",
+       "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
+       "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned",
+       "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing",
+       "SPV_NV_fragment_shader_barycentric",
+       "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint",
+       "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_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"});
 }
 
 bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index c1789c8..063d1b9 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -18,7 +18,6 @@
 
 #include <vector>
 
-#include "source/opt/iterator.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
@@ -234,60 +233,59 @@
 
 void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_demote_to_helper_invocation",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
+                                "SPV_AMD_shader_trinary_minmax",
+                                "SPV_AMD_gcn_shader",
+                                "SPV_KHR_shader_ballot",
+                                "SPV_AMD_shader_ballot",
+                                "SPV_AMD_gpu_shader_half_float",
+                                "SPV_KHR_shader_draw_parameters",
+                                "SPV_KHR_subgroup_vote",
+                                "SPV_KHR_8bit_storage",
+                                "SPV_KHR_16bit_storage",
+                                "SPV_KHR_device_group",
+                                "SPV_KHR_multiview",
+                                "SPV_NVX_multiview_per_view_attributes",
+                                "SPV_NV_viewport_array2",
+                                "SPV_NV_stereo_view_rendering",
+                                "SPV_NV_sample_mask_override_coverage",
+                                "SPV_NV_geometry_shader_passthrough",
+                                "SPV_AMD_texture_gather_bias_lod",
+                                "SPV_KHR_storage_buffer_storage_class",
+                                "SPV_KHR_variable_pointers",
+                                "SPV_AMD_gpu_shader_int16",
+                                "SPV_KHR_post_depth_coverage",
+                                "SPV_KHR_shader_atomic_counter_ops",
+                                "SPV_EXT_shader_stencil_export",
+                                "SPV_EXT_shader_viewport_index_layer",
+                                "SPV_AMD_shader_image_load_store_lod",
+                                "SPV_AMD_shader_fragment_mask",
+                                "SPV_EXT_fragment_fully_covered",
+                                "SPV_AMD_gpu_shader_half_float_fetch",
+                                "SPV_GOOGLE_decorate_string",
+                                "SPV_GOOGLE_hlsl_functionality1",
+                                "SPV_GOOGLE_user_type",
+                                "SPV_NV_shader_subgroup_partitioned",
+                                "SPV_EXT_demote_to_helper_invocation",
+                                "SPV_EXT_descriptor_indexing",
+                                "SPV_NV_fragment_shader_barycentric",
+                                "SPV_NV_compute_shader_derivatives",
+                                "SPV_NV_shader_image_footprint",
+                                "SPV_NV_shading_rate",
+                                "SPV_NV_mesh_shader",
+                                "SPV_NV_ray_tracing",
+                                "SPV_KHR_ray_tracing",
+                                "SPV_KHR_ray_query",
+                                "SPV_EXT_fragment_invocation_density",
+                                "SPV_EXT_physical_storage_buffer",
+                                "SPV_KHR_terminate_invocation",
+                                "SPV_KHR_subgroup_uniform_control_flow",
+                                "SPV_KHR_integer_dot_product",
+                                "SPV_EXT_shader_image_int64",
+                                "SPV_KHR_non_semantic_info",
+                                "SPV_KHR_uniform_group_instructions",
+                                "SPV_KHR_fragment_shader_barycentric",
+                                "SPV_KHR_vulkan_memory_model"});
 }
 
 }  // namespace opt
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index e494689..a0de44c 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -17,8 +17,6 @@
 #include "source/opt/local_single_store_elim_pass.h"
 
 #include "source/cfa.h"
-#include "source/latest_version_glsl_std_450_header.h"
-#include "source/opt/iterator.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
@@ -88,57 +86,56 @@
 }
 
 void LocalSingleStoreElimPass::InitExtensionAllowList() {
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
+                                "SPV_AMD_shader_trinary_minmax",
+                                "SPV_AMD_gcn_shader",
+                                "SPV_KHR_shader_ballot",
+                                "SPV_AMD_shader_ballot",
+                                "SPV_AMD_gpu_shader_half_float",
+                                "SPV_KHR_shader_draw_parameters",
+                                "SPV_KHR_subgroup_vote",
+                                "SPV_KHR_8bit_storage",
+                                "SPV_KHR_16bit_storage",
+                                "SPV_KHR_device_group",
+                                "SPV_KHR_multiview",
+                                "SPV_NVX_multiview_per_view_attributes",
+                                "SPV_NV_viewport_array2",
+                                "SPV_NV_stereo_view_rendering",
+                                "SPV_NV_sample_mask_override_coverage",
+                                "SPV_NV_geometry_shader_passthrough",
+                                "SPV_AMD_texture_gather_bias_lod",
+                                "SPV_KHR_storage_buffer_storage_class",
+                                "SPV_KHR_variable_pointers",
+                                "SPV_AMD_gpu_shader_int16",
+                                "SPV_KHR_post_depth_coverage",
+                                "SPV_KHR_shader_atomic_counter_ops",
+                                "SPV_EXT_shader_stencil_export",
+                                "SPV_EXT_shader_viewport_index_layer",
+                                "SPV_AMD_shader_image_load_store_lod",
+                                "SPV_AMD_shader_fragment_mask",
+                                "SPV_EXT_fragment_fully_covered",
+                                "SPV_AMD_gpu_shader_half_float_fetch",
+                                "SPV_GOOGLE_decorate_string",
+                                "SPV_GOOGLE_hlsl_functionality1",
+                                "SPV_NV_shader_subgroup_partitioned",
+                                "SPV_EXT_descriptor_indexing",
+                                "SPV_NV_fragment_shader_barycentric",
+                                "SPV_NV_compute_shader_derivatives",
+                                "SPV_NV_shader_image_footprint",
+                                "SPV_NV_shading_rate",
+                                "SPV_NV_mesh_shader",
+                                "SPV_NV_ray_tracing",
+                                "SPV_KHR_ray_query",
+                                "SPV_EXT_fragment_invocation_density",
+                                "SPV_EXT_physical_storage_buffer",
+                                "SPV_KHR_terminate_invocation",
+                                "SPV_KHR_subgroup_uniform_control_flow",
+                                "SPV_KHR_integer_dot_product",
+                                "SPV_EXT_shader_image_int64",
+                                "SPV_KHR_non_semantic_info",
+                                "SPV_KHR_uniform_group_instructions",
+                                "SPV_KHR_fragment_shader_barycentric",
+                                "SPV_KHR_vulkan_memory_model"});
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
   std::vector<Instruction*> users;
diff --git a/source/opt/loop_dependence.cpp b/source/opt/loop_dependence.cpp
index d7256bf..e41c044 100644
--- a/source/opt/loop_dependence.cpp
+++ b/source/opt/loop_dependence.cpp
@@ -15,14 +15,12 @@
 #include "source/opt/loop_dependence.h"
 
 #include <functional>
-#include <memory>
 #include <numeric>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "source/opt/instruction.h"
-#include "source/opt/scalar_analysis.h"
 #include "source/opt/scalar_analysis_nodes.h"
 
 namespace spvtools {
diff --git a/source/opt/loop_dependence_helpers.cpp b/source/opt/loop_dependence_helpers.cpp
index 929c940..5d7d994 100644
--- a/source/opt/loop_dependence_helpers.cpp
+++ b/source/opt/loop_dependence_helpers.cpp
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/opt/loop_dependence.h"
-
 #include <ostream>
 #include <set>
 #include <string>
@@ -23,7 +21,7 @@
 
 #include "source/opt/basic_block.h"
 #include "source/opt/instruction.h"
-#include "source/opt/scalar_analysis.h"
+#include "source/opt/loop_dependence.h"
 #include "source/opt/scalar_analysis_nodes.h"
 
 namespace spvtools {
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp
index 172b978..cbfc2e7 100644
--- a/source/opt/loop_descriptor.cpp
+++ b/source/opt/loop_descriptor.cpp
@@ -15,17 +15,14 @@
 #include "source/opt/loop_descriptor.h"
 
 #include <algorithm>
-#include <iostream>
 #include <limits>
 #include <stack>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "source/opt/cfg.h"
 #include "source/opt/constants.h"
 #include "source/opt/dominator_tree.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/iterator.h"
 #include "source/opt/tree_iterator.h"
@@ -453,25 +450,20 @@
   return true;
 }
 
-bool Loop::ShouldHoistInstruction(IRContext* context, Instruction* inst) {
-  return AreAllOperandsOutsideLoop(context, inst) &&
-         inst->IsOpcodeCodeMotionSafe();
+bool Loop::ShouldHoistInstruction(const Instruction& inst) const {
+  return inst.IsOpcodeCodeMotionSafe() && AreAllOperandsOutsideLoop(inst) &&
+         (!inst.IsLoad() || inst.IsReadOnlyLoad());
 }
 
-bool Loop::AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst) {
-  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
-  bool all_outside_loop = true;
+bool Loop::AreAllOperandsOutsideLoop(const Instruction& inst) const {
+  analysis::DefUseManager* def_use_mgr = GetContext()->get_def_use_mgr();
 
-  const std::function<void(uint32_t*)> operand_outside_loop =
-      [this, &def_use_mgr, &all_outside_loop](uint32_t* id) {
-        if (this->IsInsideLoop(def_use_mgr->GetDef(*id))) {
-          all_outside_loop = false;
-          return;
-        }
+  const std::function<bool(const uint32_t*)> operand_outside_loop =
+      [this, &def_use_mgr](const uint32_t* id) {
+        return !this->IsInsideLoop(def_use_mgr->GetDef(*id));
       };
 
-  inst->ForEachInId(operand_outside_loop);
-  return all_outside_loop;
+  return inst.WhileEachInId(operand_outside_loop);
 }
 
 void Loop::ComputeLoopStructuredOrder(
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h
index 35256bc..d451496 100644
--- a/source/opt/loop_descriptor.h
+++ b/source/opt/loop_descriptor.h
@@ -296,12 +296,12 @@
   // as a nested child loop.
   inline void SetParent(Loop* parent) { parent_ = parent; }
 
-  // Returns true is the instruction is invariant and safe to move wrt loop
-  bool ShouldHoistInstruction(IRContext* context, Instruction* inst);
+  // Returns true is the instruction is invariant and safe to move wrt loop.
+  bool ShouldHoistInstruction(const Instruction& inst) const;
 
   // Returns true if all operands of inst are in basic blocks not contained in
-  // loop
-  bool AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst);
+  // loop.
+  bool AreAllOperandsOutsideLoop(const Instruction& inst) const;
 
   // Extract the initial value from the |induction| variable and store it in
   // |value|. If the function couldn't find the initial value of |induction|
diff --git a/source/opt/loop_fusion_pass.cpp b/source/opt/loop_fusion_pass.cpp
index bd8444a..097430f 100644
--- a/source/opt/loop_fusion_pass.cpp
+++ b/source/opt/loop_fusion_pass.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/loop_fusion_pass.h"
 
-#include "source/opt/ir_context.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/loop_fusion.h"
 #include "source/opt/register_pressure.h"
diff --git a/source/opt/loop_peeling.cpp b/source/opt/loop_peeling.cpp
index d512273..25c6db1 100644
--- a/source/opt/loop_peeling.cpp
+++ b/source/opt/loop_peeling.cpp
@@ -12,17 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
+#include "source/opt/loop_peeling.h"
+
 #include <functional>
 #include <memory>
-#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
 #include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/loop_descriptor.h"
-#include "source/opt/loop_peeling.h"
 #include "source/opt/loop_utils.h"
 #include "source/opt/scalar_analysis.h"
 #include "source/opt/scalar_analysis_nodes.h"
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index 07b529d..d9e34f2 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -15,7 +15,6 @@
 #include "source/opt/loop_unroller.h"
 
 #include <limits>
-#include <map>
 #include <memory>
 #include <unordered_map>
 #include <utility>
diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp
index b00d66d..41f1a80 100644
--- a/source/opt/loop_unswitch_pass.cpp
+++ b/source/opt/loop_unswitch_pass.cpp
@@ -17,7 +17,6 @@
 #include <functional>
 #include <list>
 #include <memory>
-#include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
@@ -31,7 +30,6 @@
 #include "source/opt/ir_builder.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/loop_descriptor.h"
-
 #include "source/opt/loop_utils.h"
 
 namespace spvtools {
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 5f59291..9f95785 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -22,9 +22,7 @@
 
 #include "source/cfa.h"
 #include "source/opt/basic_block.h"
-#include "source/opt/dominator_analysis.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/iterator.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index cbc4b82..46a92dd 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -785,6 +785,16 @@
       MakeUnique<opt::SSARewritePass>());
 }
 
+Optimizer::PassToken CreateAggressiveDCEPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::AggressiveDCEPass>(false, false));
+}
+
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
+}
+
 Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
                                              bool remove_outputs) {
   return MakeUnique<Optimizer::PassToken::Impl>(
diff --git a/source/opt/reflect.h b/source/opt/reflect.h
index 45bb5c5..ec7c2dd 100644
--- a/source/opt/reflect.h
+++ b/source/opt/reflect.h
@@ -16,6 +16,7 @@
 #define SOURCE_OPT_REFLECT_H_
 
 #include "source/latest_version_spirv_header.h"
+#include "source/opcode.h"
 
 namespace spvtools {
 namespace opt {
@@ -46,27 +47,14 @@
          opcode == spv::Op::OpMemberDecorateStringGOOGLE;
 }
 inline bool IsTypeInst(spv::Op opcode) {
-  return (opcode >= spv::Op::OpTypeVoid &&
-          opcode <= spv::Op::OpTypeForwardPointer) ||
-         opcode == spv::Op::OpTypePipeStorage ||
-         opcode == spv::Op::OpTypeNamedBarrier ||
-         opcode == spv::Op::OpTypeAccelerationStructureNV ||
-         opcode == spv::Op::OpTypeAccelerationStructureKHR ||
-         opcode == spv::Op::OpTypeRayQueryKHR ||
-         opcode == spv::Op::OpTypeCooperativeMatrixNV ||
-         opcode == spv::Op::OpTypeHitObjectNV;
+  return spvOpcodeGeneratesType(opcode) ||
+         opcode == spv::Op::OpTypeForwardPointer;
 }
 inline bool IsConstantInst(spv::Op opcode) {
-  return (opcode >= spv::Op::OpConstantTrue &&
-          opcode <= spv::Op::OpSpecConstantOp) ||
-         opcode == spv::Op::OpConstantFunctionPointerINTEL;
-}
-inline bool IsCompileTimeConstantInst(spv::Op opcode) {
-  return opcode >= spv::Op::OpConstantTrue && opcode <= spv::Op::OpConstantNull;
+  return spvOpcodeIsConstant(opcode);
 }
 inline bool IsSpecConstantInst(spv::Op opcode) {
-  return opcode >= spv::Op::OpSpecConstantTrue &&
-         opcode <= spv::Op::OpSpecConstantOp;
+  return spvOpcodeIsSpecConstant(opcode);
 }
 
 }  // namespace opt
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp
index 90c3acf..0df559b 100644
--- a/source/opt/remove_duplicates_pass.cpp
+++ b/source/opt/remove_duplicates_pass.cpp
@@ -15,8 +15,6 @@
 #include "source/opt/remove_duplicates_pass.h"
 
 #include <algorithm>
-#include <cstring>
-#include <limits>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -25,7 +23,6 @@
 #include "source/opcode.h"
 #include "source/opt/decoration_manager.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/reflect.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/source/opt/remove_unused_interface_variables_pass.h b/source/opt/remove_unused_interface_variables_pass.h
index 7f11187..a4cb108 100644
--- a/source/opt/remove_unused_interface_variables_pass.h
+++ b/source/opt/remove_unused_interface_variables_pass.h
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#ifndef SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_
+#define SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_
+
 #include "source/opt/pass.h"
 namespace spvtools {
 namespace opt {
@@ -23,4 +26,6 @@
   Status Process() override;
 };
 }  // namespace opt
-}  // namespace spvtools
\ No newline at end of file
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_
\ No newline at end of file
diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp
index 2140973..1b97c0e 100644
--- a/source/opt/replace_invalid_opc.cpp
+++ b/source/opt/replace_invalid_opc.cpp
@@ -86,7 +86,8 @@
         }
 
         if (model != spv::ExecutionModel::TessellationControl &&
-            model != spv::ExecutionModel::GLCompute) {
+            model != spv::ExecutionModel::GLCompute &&
+            !context()->IsTargetEnvAtLeast(SPV_ENV_UNIVERSAL_1_3)) {
           if (inst->opcode() == spv::Op::OpControlBarrier) {
             assert(model != spv::ExecutionModel::Kernel &&
                    "Expecting to be working on a shader module.");
diff --git a/source/opt/scalar_analysis.cpp b/source/opt/scalar_analysis.cpp
index 0c8babe..26cc8b3 100644
--- a/source/opt/scalar_analysis.cpp
+++ b/source/opt/scalar_analysis.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/scalar_analysis.h"
 
-#include <algorithm>
 #include <functional>
 #include <string>
 #include <utility>
diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp
index 3c1ecc0..3c0947c 100644
--- a/source/opt/scalar_analysis_simplification.cpp
+++ b/source/opt/scalar_analysis_simplification.cpp
@@ -12,16 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/opt/scalar_analysis.h"
-
 #include <functional>
 #include <map>
 #include <memory>
 #include <set>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
+#include "source/opt/scalar_analysis.h"
+
 // Simplifies scalar analysis DAGs.
 //
 // 1. Given a node passed to SimplifyExpression we first simplify the graph by
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index bfebb01..38c8aec 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -19,12 +19,10 @@
 #include <tuple>
 #include <utility>
 
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/opt/reflect.h"
 #include "source/opt/types.h"
 #include "source/util/make_unique.h"
-#include "types.h"
 
 namespace spvtools {
 namespace opt {
@@ -468,9 +466,9 @@
 }
 
 void ScalarReplacementPass::CreateVariable(
-    uint32_t typeId, Instruction* varInst, uint32_t index,
+    uint32_t type_id, Instruction* var_inst, uint32_t index,
     std::vector<Instruction*>* replacements) {
-  uint32_t ptrId = GetOrCreatePointerType(typeId);
+  uint32_t ptr_id = GetOrCreatePointerType(type_id);
   uint32_t id = TakeNextId();
 
   if (id == 0) {
@@ -478,51 +476,22 @@
   }
 
   std::unique_ptr<Instruction> variable(
-      new Instruction(context(), spv::Op::OpVariable, ptrId, id,
+      new Instruction(context(), spv::Op::OpVariable, ptr_id, id,
                       std::initializer_list<Operand>{
                           {SPV_OPERAND_TYPE_STORAGE_CLASS,
                            {uint32_t(spv::StorageClass::Function)}}}));
 
-  BasicBlock* block = context()->get_instr_block(varInst);
+  BasicBlock* block = context()->get_instr_block(var_inst);
   block->begin().InsertBefore(std::move(variable));
   Instruction* inst = &*block->begin();
 
   // If varInst was initialized, make sure to initialize its replacement.
-  GetOrCreateInitialValue(varInst, index, inst);
+  GetOrCreateInitialValue(var_inst, index, inst);
   get_def_use_mgr()->AnalyzeInstDefUse(inst);
   context()->set_instr_block(inst, block);
 
-  // Copy decorations from the member to the new variable.
-  Instruction* typeInst = GetStorageType(varInst);
-  for (auto dec_inst :
-       get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) {
-    uint32_t decoration;
-    if (dec_inst->opcode() != spv::Op::OpMemberDecorate) {
-      continue;
-    }
-
-    if (dec_inst->GetSingleWordInOperand(1) != index) {
-      continue;
-    }
-
-    decoration = dec_inst->GetSingleWordInOperand(2u);
-    switch (spv::Decoration(decoration)) {
-      case spv::Decoration::RelaxedPrecision: {
-        std::unique_ptr<Instruction> new_dec_inst(
-            new Instruction(context(), spv::Op::OpDecorate, 0, 0, {}));
-        new_dec_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id}));
-        for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) {
-          new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i)));
-        }
-        context()->AddAnnotationInst(std::move(new_dec_inst));
-      } break;
-      default:
-        break;
-    }
-  }
-
-  // Update the DebugInfo debug information.
-  inst->UpdateDebugInfoFrom(varInst);
+  CopyDecorationsToVariable(var_inst, inst, index);
+  inst->UpdateDebugInfoFrom(var_inst);
 
   replacements->push_back(inst);
 }
@@ -531,52 +500,11 @@
   auto iter = pointee_to_pointer_.find(id);
   if (iter != pointee_to_pointer_.end()) return iter->second;
 
-  analysis::Type* pointeeTy;
-  std::unique_ptr<analysis::Pointer> pointerTy;
-  std::tie(pointeeTy, pointerTy) =
-      context()->get_type_mgr()->GetTypeAndPointerType(
-          id, spv::StorageClass::Function);
-  uint32_t ptrId = 0;
-  if (pointeeTy->IsUniqueType()) {
-    // Non-ambiguous type, just ask the type manager for an id.
-    ptrId = context()->get_type_mgr()->GetTypeInstruction(pointerTy.get());
-    pointee_to_pointer_[id] = ptrId;
-    return ptrId;
-  }
-
-  // Ambiguous type. We must perform a linear search to try and find the right
-  // type.
-  for (auto global : context()->types_values()) {
-    if (global.opcode() == spv::Op::OpTypePointer &&
-        spv::StorageClass(global.GetSingleWordInOperand(0u)) ==
-            spv::StorageClass::Function &&
-        global.GetSingleWordInOperand(1u) == id) {
-      if (get_decoration_mgr()->GetDecorationsFor(id, false).empty()) {
-        // Only reuse a decoration-less pointer of the correct type.
-        ptrId = global.result_id();
-        break;
-      }
-    }
-  }
-
-  if (ptrId != 0) {
-    pointee_to_pointer_[id] = ptrId;
-    return ptrId;
-  }
-
-  ptrId = TakeNextId();
-  context()->AddType(MakeUnique<Instruction>(
-      context(), spv::Op::OpTypePointer, 0, ptrId,
-      std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS,
-                                      {uint32_t(spv::StorageClass::Function)}},
-                                     {SPV_OPERAND_TYPE_ID, {id}}}));
-  Instruction* ptr = &*--context()->types_values_end();
-  get_def_use_mgr()->AnalyzeInstDefUse(ptr);
-  pointee_to_pointer_[id] = ptrId;
-  // Register with the type manager if necessary.
-  context()->get_type_mgr()->RegisterType(ptrId, *pointerTy);
-
-  return ptrId;
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  uint32_t ptr_type_id =
+      type_mgr->FindPointerToType(id, spv::StorageClass::Function);
+  pointee_to_pointer_[id] = ptr_type_id;
+  return ptr_type_id;
 }
 
 void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source,
@@ -763,6 +691,8 @@
       case spv::Decoration::AlignmentId:
       case spv::Decoration::MaxByteOffset:
       case spv::Decoration::RelaxedPrecision:
+      case spv::Decoration::AliasedPointer:
+      case spv::Decoration::RestrictPointer:
         break;
       default:
         return false;
@@ -783,6 +713,8 @@
       case spv::Decoration::Alignment:
       case spv::Decoration::AlignmentId:
       case spv::Decoration::MaxByteOffset:
+      case spv::Decoration::AliasedPointer:
+      case spv::Decoration::RestrictPointer:
         break;
       default:
         return false;
@@ -1013,5 +945,69 @@
   return 0;
 }
 
+void ScalarReplacementPass::CopyDecorationsToVariable(Instruction* from,
+                                                      Instruction* to,
+                                                      uint32_t member_index) {
+  CopyPointerDecorationsToVariable(from, to);
+  CopyNecessaryMemberDecorationsToVariable(from, to, member_index);
+}
+
+void ScalarReplacementPass::CopyPointerDecorationsToVariable(Instruction* from,
+                                                             Instruction* to) {
+  // The RestrictPointer and AliasedPointer decorations are copied to all
+  // members even if the new variable does not contain a pointer. It does
+  // not hurt to do so.
+  for (auto dec_inst :
+       get_decoration_mgr()->GetDecorationsFor(from->result_id(), false)) {
+    uint32_t decoration;
+    decoration = dec_inst->GetSingleWordInOperand(1u);
+    switch (spv::Decoration(decoration)) {
+      case spv::Decoration::AliasedPointer:
+      case spv::Decoration::RestrictPointer: {
+        std::unique_ptr<Instruction> new_dec_inst(dec_inst->Clone(context()));
+        new_dec_inst->SetInOperand(0, {to->result_id()});
+        context()->AddAnnotationInst(std::move(new_dec_inst));
+      } break;
+      default:
+        break;
+    }
+  }
+}
+
+void ScalarReplacementPass::CopyNecessaryMemberDecorationsToVariable(
+    Instruction* from, Instruction* to, uint32_t member_index) {
+  Instruction* type_inst = GetStorageType(from);
+  for (auto dec_inst :
+       get_decoration_mgr()->GetDecorationsFor(type_inst->result_id(), false)) {
+    uint32_t decoration;
+    if (dec_inst->opcode() == spv::Op::OpMemberDecorate) {
+      if (dec_inst->GetSingleWordInOperand(1) != member_index) {
+        continue;
+      }
+
+      decoration = dec_inst->GetSingleWordInOperand(2u);
+      switch (spv::Decoration(decoration)) {
+        case spv::Decoration::ArrayStride:
+        case spv::Decoration::Alignment:
+        case spv::Decoration::AlignmentId:
+        case spv::Decoration::MaxByteOffset:
+        case spv::Decoration::MaxByteOffsetId:
+        case spv::Decoration::RelaxedPrecision: {
+          std::unique_ptr<Instruction> new_dec_inst(
+              new Instruction(context(), spv::Op::OpDecorate, 0, 0, {}));
+          new_dec_inst->AddOperand(
+              Operand(SPV_OPERAND_TYPE_ID, {to->result_id()}));
+          for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) {
+            new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i)));
+          }
+          context()->AddAnnotationInst(std::move(new_dec_inst));
+        } break;
+        default:
+          break;
+      }
+    }
+  }
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index 0bcd2a4..c73ecfd 100644
--- a/source/opt/scalar_replacement_pass.h
+++ b/source/opt/scalar_replacement_pass.h
@@ -262,9 +262,26 @@
   // that we will be willing to split.
   bool IsLargerThanSizeLimit(uint64_t length) const;
 
+  // Copies all relevant decorations from `from` to `to`. This includes
+  // decorations applied to the variable, and to the members of the type.
+  // It is assumed that `to` is a variable that is intended to replace the
+  // `member_index`th member of `from`.
+  void CopyDecorationsToVariable(Instruction* from, Instruction* to,
+                                 uint32_t member_index);
+
+  // Copies pointer related decoration from `from` to `to` if they exist.
+  void CopyPointerDecorationsToVariable(Instruction* from, Instruction* to);
+
+  // Copies decorations that are needed from the `member_index` of `from` to
+  // `to, if there was one.
+  void CopyNecessaryMemberDecorationsToVariable(Instruction* from,
+                                                Instruction* to,
+                                                uint32_t member_index);
+
   // Limit on the number of members in an object that will be replaced.
   // 0 means there is no limit.
   uint32_t max_num_elements_;
+
   // This has to be big enough to fit "scalar-replacement=" followed by a
   // uint32_t number written in decimal (so 10 digits), and then a
   // terminating nul.
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index 5125bd1..d2aa9b1 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -21,8 +21,6 @@
 #include <vector>
 
 #include "source/opt/def_use_manager.h"
-#include "source/opt/ir_context.h"
-#include "source/opt/type_manager.h"
 #include "source/opt/types.h"
 #include "source/util/make_unique.h"
 #include "source/util/parse_number.h"
diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp
index dbda397..f8ffc03 100644
--- a/source/opt/simplification_pass.cpp
+++ b/source/opt/simplification_pass.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/simplification_pass.h"
 
-#include <set>
 #include <unordered_set>
 #include <vector>
 
diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp
index 3037274..e552ba5 100644
--- a/source/opt/spread_volatile_semantics.cpp
+++ b/source/opt/spread_volatile_semantics.cpp
@@ -15,7 +15,6 @@
 #include "source/opt/spread_volatile_semantics.h"
 
 #include "source/opt/decoration_manager.h"
-#include "source/opt/ir_builder.h"
 #include "source/spirv_constant.h"
 
 namespace spvtools {
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index b8e2290..3eb4ec3 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -48,7 +48,6 @@
 #include "source/opt/cfg.h"
 #include "source/opt/mem_pass.h"
 #include "source/opt/types.h"
-#include "source/util/make_unique.h"
 
 // Debug logging (0: Off, 1-N: Verbosity level).  Replace this with the
 // implementation done for
diff --git a/source/opt/strength_reduction_pass.cpp b/source/opt/strength_reduction_pass.cpp
index f2e8498..16a7869 100644
--- a/source/opt/strength_reduction_pass.cpp
+++ b/source/opt/strength_reduction_pass.cpp
@@ -14,12 +14,8 @@
 
 #include "source/opt/strength_reduction_pass.h"
 
-#include <algorithm>
-#include <cstdio>
 #include <cstring>
 #include <memory>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
diff --git a/source/opt/strip_nonsemantic_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp
index 8899690..3886835 100644
--- a/source/opt/strip_nonsemantic_info_pass.cpp
+++ b/source/opt/strip_nonsemantic_info_pass.cpp
@@ -14,7 +14,6 @@
 
 #include "source/opt/strip_nonsemantic_info_pass.h"
 
-#include <cstring>
 #include <vector>
 
 #include "source/opt/instruction.h"
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index 6e4c054..1b1aead 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -178,7 +178,7 @@
   if (iter == id_to_type_.end()) return;
 
   auto& type = iter->second;
-  if (!type->IsUniqueType(true)) {
+  if (!type->IsUniqueType()) {
     auto tIter = type_to_id_.find(type);
     if (tIter != type_to_id_.end() && tIter->second == id) {
       // |type| currently maps to |id|.
@@ -437,7 +437,7 @@
                                         spv::StorageClass storage_class) {
   Type* pointeeTy = GetType(type_id);
   Pointer pointerTy(pointeeTy, storage_class);
-  if (pointeeTy->IsUniqueType(true)) {
+  if (pointeeTy->IsUniqueType()) {
     // Non-ambiguous type. Get the pointer type through the type manager.
     return GetTypeInstruction(&pointerTy);
   }
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index ab95906..49eec9b 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -16,7 +16,6 @@
 
 #include <algorithm>
 #include <cassert>
-#include <climits>
 #include <cstdint>
 #include <sstream>
 #include <string>
@@ -85,10 +84,9 @@
   return CompareTwoVectors(decorations_, that->decorations_);
 }
 
-bool Type::IsUniqueType(bool allowVariablePointers) const {
+bool Type::IsUniqueType() const {
   switch (kind_) {
     case kPointer:
-      return !allowVariablePointers;
     case kStruct:
     case kArray:
     case kRuntimeArray:
diff --git a/source/opt/types.h b/source/opt/types.h
index 1f32937..26c058c 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -148,12 +148,16 @@
   // Returns a clone of |this| minus any decorations.
   std::unique_ptr<Type> RemoveDecorations() const;
 
-  // Returns true if this type must be unique.
+  // Returns true if this cannot hash to the same value as another type in the
+  // module. For example, structs are not unique types because the module could
+  // have two types
   //
-  // If variable pointers are allowed, then pointers are not required to be
-  // unique.
-  // TODO(alanbaker): Update this if variable pointers become a core feature.
-  bool IsUniqueType(bool allowVariablePointers = false) const;
+  //  %1 = OpTypeStruct %int
+  //  %2 = OpTypeStruct %int
+  //
+  // The only way to distinguish these types is the result id. The type manager
+  // will hash them to the same value.
+  bool IsUniqueType() const;
 
   bool operator==(const Type& other) const;
 
diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp
index f774aa6..83dd438 100644
--- a/source/opt/unify_const_pass.cpp
+++ b/source/opt/unify_const_pass.cpp
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "source/opt/def_use_manager.h"
-#include "source/opt/ir_context.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp
index 9a03817..585f8b6 100644
--- a/source/spirv_target_env.cpp
+++ b/source/spirv_target_env.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 #include <cstring>
 #include <string>
+#include <utility>
 
 #include "source/spirv_constant.h"
 #include "spirv-tools/libspirv.h"
diff --git a/source/util/small_vector.h b/source/util/small_vector.h
index 648a348..1351475 100644
--- a/source/util/small_vector.h
+++ b/source/util/small_vector.h
@@ -15,7 +15,9 @@
 #ifndef SOURCE_UTIL_SMALL_VECTOR_H_
 #define SOURCE_UTIL_SMALL_VECTOR_H_
 
+#include <array>
 #include <cassert>
+#include <cstddef>
 #include <iostream>
 #include <memory>
 #include <utility>
@@ -461,14 +463,18 @@
   // The number of elements in |small_data_| that have been constructed.
   size_t size_;
 
-  // The pointed used to access the array of elements when the number of
-  // elements is small.
-  T* small_data_;
+  // A type with the same alignment and size as T, but will is POD.
+  struct alignas(T) PodType {
+    std::array<int8_t, sizeof(T)> data;
+  };
 
   // The actual data used to store the array elements.  It must never be used
   // directly, but must only be accessed through |small_data_|.
-  typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type
-      buffer[small_size];
+  PodType buffer[small_size];
+
+  // The pointed used to access the array of elements when the number of
+  // elements is small.
+  T* small_data_;
 
   // A pointer to a vector that is used to store the elements of the vector when
   // this size exceeds |small_size|.  If |large_data_| is nullptr, then the data
diff --git a/source/val/basic_block.cpp b/source/val/basic_block.cpp
index da05db3..9a358fc 100644
--- a/source/val/basic_block.cpp
+++ b/source/val/basic_block.cpp
@@ -15,7 +15,6 @@
 #include "source/val/basic_block.h"
 
 #include <algorithm>
-#include <utility>
 #include <vector>
 
 namespace spvtools {
diff --git a/source/val/construct.cpp b/source/val/construct.cpp
index 1ca81d4..10af155 100644
--- a/source/val/construct.cpp
+++ b/source/val/construct.cpp
@@ -16,7 +16,6 @@
 
 #include <cassert>
 #include <cstddef>
-#include <unordered_set>
 
 #include "source/val/function.h"
 #include "source/val/validation_state.h"
diff --git a/source/val/function.cpp b/source/val/function.cpp
index 8b4423a..290574b 100644
--- a/source/val/function.cpp
+++ b/source/val/function.cpp
@@ -18,7 +18,6 @@
 #include <cassert>
 #include <sstream>
 #include <unordered_map>
-#include <unordered_set>
 #include <utility>
 
 #include "source/cfa.h"
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index 52cb0d8..e73e466 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -14,13 +14,9 @@
 
 #include "source/val/validate.h"
 
-#include <algorithm>
-#include <cassert>
-#include <cstdio>
 #include <functional>
 #include <iterator>
 #include <memory>
-#include <sstream>
 #include <string>
 #include <vector>
 
@@ -28,15 +24,11 @@
 #include "source/diagnostic.h"
 #include "source/enum_string_mapping.h"
 #include "source/extensions.h"
-#include "source/instruction.h"
 #include "source/opcode.h"
-#include "source/operand.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_endian.h"
 #include "source/spirv_target_env.h"
-#include "source/spirv_validator_options.h"
 #include "source/val/construct.h"
-#include "source/val/function.h"
 #include "source/val/instruction.h"
 #include "source/val/validation_state.h"
 #include "spirv-tools/libspirv.h"
diff --git a/source/val/validate.h b/source/val/validate.h
index 8987438..2cd229f 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -31,11 +31,6 @@
 class BasicBlock;
 class Instruction;
 
-/// A function that returns a vector of BasicBlocks given a BasicBlock. Used to
-/// get the successor and predecessor nodes of a CFG block
-using get_blocks_func =
-    std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
-
 /// @brief Performs the Control Flow Graph checks
 ///
 /// @param[in] _ the validation state of the module
diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp
index 50c2e92..7e371c2 100644
--- a/source/val/validate_adjacency.cpp
+++ b/source/val/validate_adjacency.cpp
@@ -15,13 +15,10 @@
 // Validates correctness of the intra-block preconditions of SPIR-V
 // instructions.
 
-#include "source/val/validate.h"
-
 #include <string>
 
-#include "source/diagnostic.h"
-#include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index bef753d..73d0285 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -193,7 +193,8 @@
     switch (dec) {
       case spv::Decoration::Location:
       case spv::Decoration::Component:
-        // Location is used for input, output and ray tracing stages.
+        // Location is used for input, output, tile image, and ray tracing
+        // stages.
         if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output &&
             sc != spv::StorageClass::RayPayloadKHR &&
             sc != spv::StorageClass::IncomingRayPayloadKHR &&
@@ -201,7 +202,8 @@
             sc != spv::StorageClass::CallableDataKHR &&
             sc != spv::StorageClass::IncomingCallableDataKHR &&
             sc != spv::StorageClass::ShaderRecordBufferKHR &&
-            sc != spv::StorageClass::HitObjectAttributeNV) {
+            sc != spv::StorageClass::HitObjectAttributeNV &&
+            sc != spv::StorageClass::TileImageEXT) {
           return _.diag(SPV_ERROR_INVALID_ID, target)
                  << _.VkErrorID(6672) << _.SpvDecorationString(dec)
                  << " decoration must not be applied to this storage class";
diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp
index a082eeb..4e7dd5e 100644
--- a/source/val/validate_arithmetics.cpp
+++ b/source/val/validate_arithmetics.cpp
@@ -14,13 +14,11 @@
 
 // Performs validation of arithmetic instructions.
 
-#include "source/val/validate.h"
-
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index d6b094c..b745a9e 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -16,13 +16,11 @@
 
 // Validates correctness of atomic SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/util/bitutils.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validate_memory_semantics.h"
 #include "source/val/validate_scopes.h"
 #include "source/val/validation_state.h"
diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp
index 59d886a..0abd5c8 100644
--- a/source/val/validate_barriers.cpp
+++ b/source/val/validate_barriers.cpp
@@ -16,11 +16,8 @@
 
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_target_env.h"
-#include "source/util/bitutils.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validate_memory_semantics.h"
diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp
index 6ab1fae..d8d9958 100644
--- a/source/val/validate_bitwise.cpp
+++ b/source/val/validate_bitwise.cpp
@@ -14,7 +14,6 @@
 
 // Validates correctness of bitwise instructions.
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index c07dcad..3e81712 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -24,10 +24,8 @@
 #include <sstream>
 #include <stack>
 #include <string>
-#include <unordered_map>
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/util/bitutils.h"
diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp
index d70c827..98dab42 100644
--- a/source/val/validate_capability.cpp
+++ b/source/val/validate_capability.cpp
@@ -16,9 +16,7 @@
 
 #include <cassert>
 #include <string>
-#include <unordered_set>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 9ba66f4..7b3f748 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
 #include <cassert>
 #include <functional>
 #include <iostream>
-#include <iterator>
 #include <map>
 #include <string>
 #include <tuple>
@@ -28,7 +26,6 @@
 #include "source/cfa.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_target_env.h"
 #include "source/spirv_validator_options.h"
 #include "source/val/basic_block.h"
 #include "source/val/construct.h"
@@ -671,7 +668,8 @@
       // previously.
       const bool true_label_unseen = seen.insert(true_label).second;
       const bool false_label_unseen = seen.insert(false_label).second;
-      if (!merge && true_label_unseen && false_label_unseen) {
+      if ((!merge || merge->opcode() == spv::Op::OpLoopMerge) &&
+          true_label_unseen && false_label_unseen) {
         return _.diag(SPV_ERROR_INVALID_CFG, terminator)
                << "Selection must be structured";
       }
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp
index e777f16..2b83c63 100644
--- a/source/val/validate_composites.cpp
+++ b/source/val/validate_composites.cpp
@@ -14,12 +14,10 @@
 
 // Validates correctness of composite SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp
index a8ee5a6..006e504 100644
--- a/source/val/validate_constants.cpp
+++ b/source/val/validate_constants.cpp
@@ -76,7 +76,7 @@
         }
         const auto constituent_result_type = _.FindDef(constituent->type_id());
         if (!constituent_result_type ||
-            component_type->opcode() != constituent_result_type->opcode()) {
+            component_type->id() != constituent_result_type->id()) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << opcode_name << " Constituent <id> "
                  << _.getIdName(constituent_id)
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp
index c67b196..476c1fe 100644
--- a/source/val/validate_conversion.cpp
+++ b/source/val/validate_conversion.cpp
@@ -14,7 +14,6 @@
 
 // Validates correctness of conversion instructions.
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
diff --git a/source/val/validate_debug.cpp b/source/val/validate_debug.cpp
index c433c93..ef537ea 100644
--- a/source/val/validate_debug.cpp
+++ b/source/val/validate_debug.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/val/validate.h"
-
-#include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index f9c8435..c1fca45 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -21,7 +21,6 @@
 #include <utility>
 #include <vector>
 
-#include "source/binary.h"
 #include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
@@ -48,13 +47,6 @@
   }
 };
 
-// A functor for hashing decoration types.
-struct SpvDecorationHash {
-  std::size_t operator()(spv::Decoration dec) const {
-    return static_cast<std::size_t>(dec);
-  }
-};
-
 // Struct member layout attributes that are inherited through arrays.
 struct LayoutConstraints {
   explicit LayoutConstraints(
diff --git a/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp
index d87240f..90cf664 100644
--- a/source/val/validate_derivatives.cpp
+++ b/source/val/validate_derivatives.cpp
@@ -14,13 +14,11 @@
 
 // Validates correctness of derivative SPIR-V instructions.
 
-#include "source/val/validate.h"
-
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_execution_limitations.cpp b/source/val/validate_execution_limitations.cpp
index 00c6603..0221d7e 100644
--- a/source/val/validate_execution_limitations.cpp
+++ b/source/val/validate_execution_limitations.cpp
@@ -13,8 +13,6 @@
 // limitations under the License.
 
 #include "source/val/validate.h"
-
-#include "source/val/function.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index fa58e0f..0ac62bf 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -18,22 +18,18 @@
 #include <string>
 #include <vector>
 
-#include "spirv/unified1/NonSemanticClspvReflection.h"
-
 #include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
 #include "source/common_debug_info.h"
-#include "source/diagnostic.h"
 #include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/latest_version_opencl_std_header.h"
-#include "source/opcode.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
+#include "spirv/unified1/NonSemanticClspvReflection.h"
 
 namespace spvtools {
 namespace val {
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index db402aa..639817f 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -14,6 +14,7 @@
 
 #include <algorithm>
 
+#include "source/enum_string_mapping.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index 89a5ddd..92a4e8e 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -12,25 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/val/validate.h"
-
-#include <cassert>
-
-#include <algorithm>
-#include <iostream>
-#include <iterator>
-#include <stack>
-#include <string>
 #include <unordered_set>
-#include <utility>
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/instruction.h"
 #include "source/opcode.h"
 #include "source/operand.h"
-#include "source/spirv_validator_options.h"
 #include "source/val/function.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 #include "spirv-tools/libspirv.h"
 
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index 8f0e6c4..733556b 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -18,7 +18,6 @@
 
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -211,6 +210,7 @@
     case spv::Dim::Dim2D:
     case spv::Dim::Rect:
     case spv::Dim::SubpassData:
+    case spv::Dim::TileImageDataEXT:
       plane_size = 2;
       break;
     case spv::Dim::Dim3D:
@@ -219,6 +219,7 @@
       plane_size = 3;
       break;
     case spv::Dim::Max:
+    default:
       assert(0);
       break;
   }
@@ -854,6 +855,28 @@
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Dim SubpassData requires format Unknown";
     }
+  } else if (info.dim == spv::Dim::TileImageDataEXT) {
+    if (_.IsVoidType(info.sampled_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Sampled Type to be not "
+                "OpTypeVoid";
+    }
+    if (info.sampled != 2) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Sampled to be 2";
+    }
+    if (info.format != spv::ImageFormat::Unknown) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires format Unknown";
+    }
+    if (info.depth != 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Depth to be 0";
+    }
+    if (info.arrayed != 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim TileImageDataEXT requires Arrayed to be 0";
+    }
   } else {
     if (info.multisampled && (info.sampled == 2) &&
         !_.HasCapability(spv::Capability::StorageImageMultisample)) {
@@ -919,6 +942,8 @@
   }
   // OpenCL requires Sampled=0, checked elsewhere.
   // Vulkan uses the Sampled=1 case.
+  // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated
+  // elsewhere.
   if ((info.sampled != 0) && (info.sampled != 1)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << _.VkErrorID(4657)
@@ -1118,6 +1143,12 @@
            << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
   }
 
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image Dim TileImageDataEXT cannot be used with "
+              "OpImageTexelPointer";
+  }
+
   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
   if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -1624,6 +1655,12 @@
                 spvOpcodeString(opcode));
   }
 
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image Dim TileImageDataEXT cannot be used with "
+           << spvOpcodeString(opcode);
+  }
+
   if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
     const uint32_t result_component_type =
         _.GetComponentType(actual_result_type);
@@ -1686,6 +1723,11 @@
            << "Image 'Dim' cannot be SubpassData";
   }
 
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image 'Dim' cannot be TileImageDataEXT";
+  }
+
   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
     return result;
 
@@ -1900,10 +1942,22 @@
            << "Expected Result Type to be int scalar type";
   }
 
-  if (_.GetIdOpcode(_.GetOperandTypeId(inst, 2)) != spv::Op::OpTypeImage) {
+  const uint32_t image_type = _.GetOperandTypeId(inst, 2);
+  if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected operand to be of type OpTypeImage";
   }
+
+  ImageTypeInfo info;
+  if (!GetImageTypeInfo(_, image_type, &info)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Corrupt image type definition";
+  }
+
+  if (info.dim == spv::Dim::TileImageDataEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Image 'Dim' cannot be TileImageDataEXT";
+  }
   return SPV_SUCCESS;
 }
 
@@ -1996,11 +2050,11 @@
            << " components, but given only " << actual_coord_size;
   }
 
-  // The operad is a sampled image.
+  // The operand is a sampled image.
   // The sampled image type is already checked to be parameterized by an image
   // type with Sampled=0 or Sampled=1.  Vulkan bans Sampled=0, and so we have
   // Sampled=1.  So the validator already enforces Vulkan VUID 4659:
-  //   OpImageQuerySizeLod must only consume an “Image” operand whose type has
+  //   OpImageQuerySizeLod must only consume an "Image" operand whose type has
   //   its "Sampled" operand set to 1
   return SPV_SUCCESS;
 }
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index 1b7847c..fde6e52 100644
--- a/source/val/validate_instruction.cpp
+++ b/source/val/validate_instruction.cpp
@@ -14,26 +14,20 @@
 
 // Performs validation on instructions that appear inside of a SPIR-V block.
 
-#include <algorithm>
 #include <cassert>
-#include <iomanip>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include "source/binary.h"
-#include "source/diagnostic.h"
 #include "source/enum_set.h"
 #include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/opcode.h"
 #include "source/operand.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_definition.h"
 #include "source/spirv_target_env.h"
 #include "source/spirv_validator_options.h"
 #include "source/util/string_utils.h"
-#include "source/val/function.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp
index 00a5999..3566698 100644
--- a/source/val/validate_interfaces.cpp
+++ b/source/val/validate_interfaces.cpp
@@ -15,7 +15,6 @@
 #include <algorithm>
 #include <vector>
 
-#include "source/diagnostic.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
 #include "source/val/function.h"
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index 238dd9b..dbc1f1e 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -14,12 +14,9 @@
 
 // Source code for logical layout validation as described in section 2.4
 
-#include <cassert>
-
 #include "DebugInfo.h"
 #include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/operand.h"
 #include "source/val/function.h"
diff --git a/source/val/validate_literals.cpp b/source/val/validate_literals.cpp
index 53aae07..15cc27a 100644
--- a/source/val/validate_literals.cpp
+++ b/source/val/validate_literals.cpp
@@ -14,13 +14,10 @@
 
 // Validates literal numbers.
 
-#include "source/val/validate.h"
-
 #include <cassert>
 
-#include "source/diagnostic.h"
-#include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp
index dd66ce9..4479e43 100644
--- a/source/val/validate_logicals.cpp
+++ b/source/val/validate_logicals.cpp
@@ -14,11 +14,9 @@
 
 // Validates correctness of logical SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp
index 748b238..c4f22a6 100644
--- a/source/val/validate_memory_semantics.cpp
+++ b/source/val/validate_memory_semantics.cpp
@@ -14,7 +14,6 @@
 
 #include "source/val/validate_memory_semantics.h"
 
-#include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/util/bitutils.h"
 #include "source/val/instruction.h"
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index dfa4646..d757d4f 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -502,6 +502,9 @@
     case spv::ExecutionMode::DepthGreater:
     case spv::ExecutionMode::DepthLess:
     case spv::ExecutionMode::DepthUnchanged:
+    case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
+    case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
+    case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
     case spv::ExecutionMode::PixelInterlockOrderedEXT:
     case spv::ExecutionMode::PixelInterlockUnorderedEXT:
     case spv::ExecutionMode::SampleInterlockOrderedEXT:
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp
index 5c5e9bd..af04e76 100644
--- a/source/val/validate_non_uniform.cpp
+++ b/source/val/validate_non_uniform.cpp
@@ -14,14 +14,11 @@
 
 // Validates correctness of barrier SPIR-V instructions.
 
-#include "source/val/validate.h"
-
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
-#include "source/util/bitutils.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validate_scopes.h"
 #include "source/val/validation_state.h"
 
diff --git a/source/val/validate_primitives.cpp b/source/val/validate_primitives.cpp
index 5e598c3..6769090 100644
--- a/source/val/validate_primitives.cpp
+++ b/source/val/validate_primitives.cpp
@@ -14,13 +14,11 @@
 
 // Validates correctness of primitive SPIR-V instructions.
 
-#include "source/val/validate.h"
-
 #include <string>
 
-#include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index fa1dad9..40c49d1 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -14,7 +14,6 @@
 
 #include "source/val/validate_scopes.h"
 
-#include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
 #include "source/val/validation_state.h"
@@ -240,7 +239,7 @@
                !_.HasCapability(spv::Capability::SubgroupBallotKHR) &&
                !_.HasCapability(spv::Capability::SubgroupVoteKHR)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(6997) << spvOpcodeString(opcode)
+             << _.VkErrorID(7951) << spvOpcodeString(opcode)
              << ": in Vulkan 1.0 environment Memory Scope is can not be "
                 "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
                 "declared";
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index c95eec3..dbf0ba6 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1572,6 +1572,7 @@
       case spv::StorageClass::ShaderRecordBufferKHR:
       case spv::StorageClass::TaskPayloadWorkgroupEXT:
       case spv::StorageClass::HitObjectAttributeNV:
+      case spv::StorageClass::TileImageEXT:
         return true;
       default:
         return false;
@@ -2163,8 +2164,6 @@
       return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808);
     case 6925:
       return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925);
-    case 6997:
-      return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-06997);
     case 7102:
       return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102);
     case 7320:
@@ -2179,6 +2178,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-Base-07652);
     case 7703:
       return VUID_WRAP(VUID-StandaloneSpirv-Component-07703);
+    case 7951:
+      return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951);
     default:
       return "";  // unknown id
   }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 4ca8ef8..37c5e1d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -31,7 +31,7 @@
 function(add_spvtools_unittest)
   if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main)
     set(one_value_args TARGET PCH_FILE)
-    set(multi_value_args SRCS LIBS ENVIRONMENT)
+    set(multi_value_args SRCS LIBS ENVIRONMENT DEFINES)
     cmake_parse_arguments(
       ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN})
     set(target test_${ARG_TARGET})
@@ -40,6 +40,7 @@
       spvtools_pch(SRC_COPY ${ARG_PCH_FILE})
     endif()
     add_executable(${target} ${SRC_COPY})
+    target_compile_definitions(${target} PUBLIC ${ARG_DEFINES})
     spvtools_default_compile_options(${target})
     if(${COMPILER_IS_LIKE_GNU})
       target_compile_options(${target} PRIVATE -Wno-undef)
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp
index 4c699c1..50710cd 100644
--- a/test/binary_parse_test.cpp
+++ b/test/binary_parse_test.cpp
@@ -214,6 +214,40 @@
   MockParseClient client_;
 };
 
+class CxxBinaryParseTest
+    : public spvtest::TextToBinaryTestBase<::testing::Test> {
+ protected:
+  CxxBinaryParseTest() {
+    header_parser_ = [this](const spv_endianness_t endianness,
+                            const spv_parsed_header_t& header) {
+      return this->client_.Header(endianness, header.magic, header.version,
+                                  header.generator, header.bound,
+                                  header.reserved);
+    };
+
+    instruction_parser_ = [this](const spv_parsed_instruction_t& instruction) {
+      return this->client_.Instruction(ParsedInstruction(instruction));
+    };
+  }
+
+  ~CxxBinaryParseTest() override { spvDiagnosticDestroy(diagnostic_); }
+
+  void Parse(const SpirvVector& words, bool expected_result,
+             bool flip_words = false,
+             spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
+    SpirvVector flipped_words(words);
+    MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end());
+    spvtools::SpirvTools tools(env);
+    EXPECT_EQ(expected_result, tools.Parse(flipped_words, header_parser_,
+                                           instruction_parser_, &diagnostic_));
+  }
+
+  spv_diagnostic diagnostic_ = nullptr;
+  MockParseClient client_;
+  HeaderParser header_parser_;
+  InstructionParser instruction_parser_;
+};
+
 // Adds an EXPECT_CALL to client_->Header() with appropriate parameters,
 // including bound.  Returns the EXPECT_CALL result.
 #define EXPECT_HEADER(bound)                                                 \
@@ -235,6 +269,16 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully("");
+    EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
   const auto words = CompileSuccessfully("");
   EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
@@ -245,6 +289,15 @@
                      words.size(), invoke_header, invoke_instruction, nullptr));
 }
 
+TEST_F(CxxBinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
+  const auto words = CompileSuccessfully("");
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_EQ(true,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+}
+
 TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) {
   auto words = CompileSuccessfully("");
   words.push_back(0xffffffff);  // Certainly invalid instruction header.
@@ -256,6 +309,16 @@
                      words.size(), invoke_header, invoke_instruction, nullptr));
 }
 
+TEST_F(CxxBinaryParseTest, NullDiagnosticsIsOkForBadParse) {
+  auto words = CompileSuccessfully("");
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_EQ(false,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+}
+
 // Make sure that we don't blow up when both the consumer and the diagnostic are
 // null.
 TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
@@ -272,6 +335,18 @@
                            invoke_header, invoke_instruction, nullptr));
 }
 
+TEST_F(CxxBinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  tools.SetMessageConsumer(nullptr);
+
+  auto words = CompileSuccessfully("");
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(false,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
   const auto words = CompileSuccessfully("");
 
@@ -289,6 +364,21 @@
   EXPECT_EQ(0, invocation);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
+  const auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
+                                         const spv_position_t&,
+                                         const char*) { ++invocation; });
+
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(true,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+  EXPECT_EQ(0, invocation);
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
   auto words = CompileSuccessfully("");
 
@@ -315,6 +405,30 @@
   EXPECT_EQ(1, invocation);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
+  auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer(
+      [&invocation](spv_message_level_t level, const char* source,
+                    const spv_position_t& position, const char* message) {
+        ++invocation;
+        EXPECT_EQ(SPV_MSG_ERROR, level);
+        EXPECT_STREQ("input", source);
+        EXPECT_EQ(0u, position.line);
+        EXPECT_EQ(0u, position.column);
+        EXPECT_EQ(1u, position.index);
+        EXPECT_STREQ("Invalid opcode: 65535", message);
+      });
+
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(false,
+            tools.Parse(words, header_parser_, instruction_parser_, nullptr));
+  EXPECT_EQ(1, invocation);
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
   const auto words = CompileSuccessfully("");
 
@@ -333,6 +447,22 @@
   EXPECT_EQ(nullptr, diagnostic_);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
+  const auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
+                                         const spv_position_t&,
+                                         const char*) { ++invocation; });
+
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(true, tools.Parse(words, header_parser_, instruction_parser_,
+                              &diagnostic_));
+  EXPECT_EQ(0, invocation);
+  EXPECT_EQ(nullptr, diagnostic_);
+}
+
 TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
   auto words = CompileSuccessfully("");
 
@@ -352,6 +482,23 @@
   EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
 }
 
+TEST_F(CxxBinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
+  auto words = CompileSuccessfully("");
+  spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+  int invocation = 0;
+  tools.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
+                                         const spv_position_t&,
+                                         const char*) { ++invocation; });
+
+  words.push_back(0xffffffff);  // Certainly invalid instruction header.
+  EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).Times(0);  // No instruction callback.
+  EXPECT_EQ(false, tools.Parse(words, header_parser_, instruction_parser_,
+                               &diagnostic_));
+  EXPECT_EQ(0, invocation);
+  EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
+}
+
 TEST_F(BinaryParseTest,
        ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
   for (bool endian_swap : kSwapEndians) {
@@ -365,6 +512,19 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest,
+       ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully("%1 = OpTypeVoid");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(2).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_SUCCESS));
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, NullHeaderCallbackIsIgnored) {
   const auto words = CompileSuccessfully("%1 = OpTypeVoid");
   EXPECT_CALL(client_, Header(_, _, _, _, _, _))
@@ -408,6 +568,22 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(3).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
+        .WillOnce(Return(SPV_SUCCESS));
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
   for (bool endian_swap : kSwapEndians) {
     const auto words = CompileSuccessfully(
@@ -423,6 +599,21 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(3).WillOnce(Return(SPV_ERROR_INVALID_BINARY));
+    // Early exit means no calls to Instruction().
+    EXPECT_CALL(client_, Instruction(_)).Times(0);
+    Parse(words, false, endian_swap);
+    // On error, the binary parser doesn't generate its own diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest,
        EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
   for (bool endian_swap : kSwapEndians) {
@@ -440,6 +631,23 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest,
+       EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(3).WillOnce(Return(SPV_REQUESTED_TERMINATION));
+    // Early exit means no calls to Instruction().
+    EXPECT_CALL(client_, Instruction(_)).Times(0);
+    Parse(words, false, endian_swap);
+    // On early termination, the binary parser doesn't generate its own
+    // diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) {
   for (bool endian_swap : kSwapEndians) {
     const auto words = CompileSuccessfully(
@@ -457,6 +665,23 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EarlyReturnWithOnePassingCallback) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1 "
+        "%3 = OpTypeFloat 32");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_REQUESTED_TERMINATION));
+    Parse(words, false, endian_swap);
+    // On early termination, the binary parser doesn't generate its own
+    // diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
   for (bool endian_swap : kSwapEndians) {
     const auto words = CompileSuccessfully(
@@ -476,6 +701,25 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
+  for (bool endian_swap : kSwapEndians) {
+    const auto words = CompileSuccessfully(
+        "%1 = OpTypeVoid "
+        "%2 = OpTypeInt 32 1 "
+        "%3 = OpTypeFloat 32");
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
+        .WillOnce(Return(SPV_SUCCESS));
+    EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
+        .WillOnce(Return(SPV_REQUESTED_TERMINATION));
+    Parse(words, false, endian_swap);
+    // On early termination, the binary parser doesn't generate its own
+    // diagnostics.
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 TEST_F(BinaryParseTest, InstructionWithStringOperand) {
   for (bool endian_swap : kSwapEndians) {
     const std::string str =
@@ -501,6 +745,31 @@
   }
 }
 
+TEST_F(CxxBinaryParseTest, InstructionWithStringOperand) {
+  for (bool endian_swap : kSwapEndians) {
+    const std::string str =
+        "the future is already here, it's just not evenly distributed";
+    const auto str_words = MakeVector(str);
+    const auto instruction = MakeInstruction(spv::Op::OpName, {99}, str_words);
+    const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
+    InSequence calls_expected_in_specific_order;
+    EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
+    const auto operands = std::vector<spv_parsed_operand_t>{
+        MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
+        MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
+    EXPECT_CALL(
+        client_,
+        Instruction(ParsedInstruction(spv_parsed_instruction_t{
+            instruction.data(), static_cast<uint16_t>(instruction.size()),
+            uint16_t(spv::Op::OpName), SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
+            0 /* No result id for OpName*/, operands.data(),
+            static_cast<uint16_t>(operands.size())})))
+        .WillOnce(Return(SPV_SUCCESS));
+    Parse(words, true, endian_swap);
+    EXPECT_EQ(nullptr, diagnostic_);
+  }
+}
+
 // Checks for non-zero values for the result_id and ext_inst_type members
 // spv_parsed_instruction_t.
 TEST_F(BinaryParseTest, ExtendedInstruction) {
@@ -534,6 +803,37 @@
   EXPECT_EQ(nullptr, diagnostic_);
 }
 
+TEST_F(CxxBinaryParseTest, ExtendedInstruction) {
+  const auto words = CompileSuccessfully(
+      "%extcl = OpExtInstImport \"OpenCL.std\" "
+      "%result = OpExtInst %float %extcl sqrt %x");
+  EXPECT_HEADER(5).WillOnce(Return(SPV_SUCCESS));
+  EXPECT_CALL(client_, Instruction(_)).WillOnce(Return(SPV_SUCCESS));
+  // We're only interested in the second call to Instruction():
+  const auto operands = std::vector<spv_parsed_operand_t>{
+      MakeSimpleOperand(1, SPV_OPERAND_TYPE_TYPE_ID),
+      MakeSimpleOperand(2, SPV_OPERAND_TYPE_RESULT_ID),
+      MakeSimpleOperand(3,
+                        SPV_OPERAND_TYPE_ID),  // Extended instruction set Id
+      MakeSimpleOperand(4, SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER),
+      MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID),  // Id of the argument
+  };
+  const auto instruction = MakeInstruction(
+      spv::Op::OpExtInst,
+      {2, 3, 1, static_cast<uint32_t>(OpenCLLIB::Entrypoints::Sqrt), 4});
+  EXPECT_CALL(client_,
+              Instruction(ParsedInstruction(spv_parsed_instruction_t{
+                  instruction.data(), static_cast<uint16_t>(instruction.size()),
+                  uint16_t(spv::Op::OpExtInst), SPV_EXT_INST_TYPE_OPENCL_STD,
+                  2 /*type id*/, 3 /*result id*/, operands.data(),
+                  static_cast<uint16_t>(operands.size())})))
+      .WillOnce(Return(SPV_SUCCESS));
+  // Since we are actually checking the output, don't test the
+  // endian-swapped version.
+  Parse(words, true, false);
+  EXPECT_EQ(nullptr, diagnostic_);
+}
+
 // A binary parser diagnostic test case where we provide the words array
 // pointer and word count explicitly.
 struct WordsAndCountDiagnosticCase {
diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp
index 3c9e6b4..534ad69 100644
--- a/test/fuzz/transformation_add_dead_block_test.cpp
+++ b/test/fuzz/transformation_add_dead_block_test.cpp
@@ -295,11 +295,16 @@
                OpBranch %8
           %8 = OpLabel
                OpLoopMerge %12 %11 None
+               OpBranch %13
+         %13 = OpLabel
+               OpSelectionMerge %14 None
                OpBranchConditional %5 %9 %10
           %9 = OpLabel
                OpBranch %11
          %10 = OpLabel
                OpBranch %12
+         %14 = OpLabel
+               OpUnreachable
          %11 = OpLabel
                OpBranch %8
          %12 = OpLabel
diff --git a/test/fuzz/transformation_add_dead_break_test.cpp b/test/fuzz/transformation_add_dead_break_test.cpp
index 5302d8a..fd46c96 100644
--- a/test/fuzz/transformation_add_dead_break_test.cpp
+++ b/test/fuzz/transformation_add_dead_break_test.cpp
@@ -2743,6 +2743,9 @@
                OpBranch %100
         %100 = OpLabel
                OpLoopMerge %101 %104 None
+               OpBranch %105
+        %105 = OpLabel
+               OpSelectionMerge %106 None
                OpBranchConditional %11 %102 %103
         %103 = OpLabel
         %200 = OpCopyObject %10 %11
@@ -2752,6 +2755,8 @@
                OpReturn
         %102 = OpLabel
                OpBranch %103
+        %106 = OpLabel
+               OpUnreachable
         %104 = OpLabel
                OpBranch %100
                OpFunctionEnd
@@ -2791,12 +2796,17 @@
                OpBranch %100
         %100 = OpLabel
                OpLoopMerge %101 %104 None
+               OpBranch %105
+        %105 = OpLabel
+               OpSelectionMerge %106 None
                OpBranchConditional %11 %102 %103
         %103 = OpLabel
         %200 = OpCopyObject %10 %11
                OpBranch %101
         %102 = OpLabel
                OpBranch %103
+        %106 = OpLabel
+               OpUnreachable
         %101 = OpLabel
         %201 = OpCopyObject %10 %200
                OpReturn
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 10ed82a..01b98a6 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -223,12 +223,12 @@
     Dim, EnumCapabilityTest,
     Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
             ValuesIn(std::vector<EnumCapabilityCase>{
-                CASE2(DIMENSIONALITY, Dim::Dim1D, Sampled1D, Image1D),
-                CASE3(DIMENSIONALITY, Dim::Dim2D, Kernel, Shader, ImageMSArray),
+                CASE1(DIMENSIONALITY, Dim::Dim1D, Sampled1D),
+                CASE0(DIMENSIONALITY, Dim::Dim2D),
                 CASE0(DIMENSIONALITY, Dim::Dim3D),
-                CASE2(DIMENSIONALITY, Dim::Cube, Shader, ImageCubeArray),
-                CASE2(DIMENSIONALITY, Dim::Rect, SampledRect, ImageRect),
-                CASE2(DIMENSIONALITY, Dim::Buffer, SampledBuffer, ImageBuffer),
+                CASE1(DIMENSIONALITY, Dim::Cube, Shader),
+                CASE1(DIMENSIONALITY, Dim::Rect, SampledRect),
+                CASE1(DIMENSIONALITY, Dim::Buffer, SampledBuffer),
                 CASE1(DIMENSIONALITY, Dim::SubpassData, InputAttachment),
             })));
 
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 0d94151..83aab3c 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -7857,6 +7857,33 @@
   SinglePassRunAndMatch<AggressiveDCEPass>(text, true, false, false);
 }
 
+TEST_F(AggressiveDCETest, RemoveWhenUsingPrintfExtension) {
+  // Remove dead n_out output variable from module
+  const std::string text = R"(
+; CHECK: OpExtInstImport "NonSemantic.DebugPrintf"
+; CHECK-NOT: OpVariable
+               OpCapability Shader
+          %1 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 8 8 1
+               OpSource HLSL 660
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %main = OpFunction %void None %5
+          %7 = OpLabel
+          %8 = OpVariable %_ptr_Function_uint Function
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/analyze_live_input_test.cpp b/test/opt/analyze_live_input_test.cpp
index c2a8f4c..7f1ff2e 100644
--- a/test/opt/analyze_live_input_test.cpp
+++ b/test/opt/analyze_live_input_test.cpp
@@ -15,7 +15,6 @@
 
 #include <unordered_set>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index f0f2436..a8e9557 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -14,7 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/ccp_pass.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/code_sink_test.cpp b/test/opt/code_sink_test.cpp
index bf5029b..98033fb 100644
--- a/test/opt/code_sink_test.cpp
+++ b/test/opt/code_sink_test.cpp
@@ -14,7 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp
index 5be3ba6..ef7addc 100644
--- a/test/opt/combine_access_chains_test.cpp
+++ b/test/opt/combine_access_chains_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/compact_ids_test.cpp b/test/opt/compact_ids_test.cpp
index 7c232fe..42f2351 100644
--- a/test/opt/compact_ids_test.cpp
+++ b/test/opt/compact_ids_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/constant_manager_test.cpp b/test/opt/constant_manager_test.cpp
index 14e14ec..54c8652 100644
--- a/test/opt/constant_manager_test.cpp
+++ b/test/opt/constant_manager_test.cpp
@@ -13,9 +13,7 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/constants.h"
diff --git a/test/opt/constants_test.cpp b/test/opt/constants_test.cpp
index 55c92a5..1d4c738 100644
--- a/test/opt/constants_test.cpp
+++ b/test/opt/constants_test.cpp
@@ -16,7 +16,6 @@
 
 #include <gtest/gtest-param-test.h>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/types.h"
 
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index 6a06de8..27330e1 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -1570,6 +1570,49 @@
   EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
 }
 
+TEST_F(ConvertToHalfTest, DoNotReplaceStructMember) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4814
+
+  // This test is a case with a non-relaxed phi with a relaxed operand.
+  // A convert must be inserted at the end of the block associated with
+  // the operand.
+  const std::string test =
+      R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET %MyConstants
+OpExecutionMode %PSMain OriginUpperLeft
+OpSource HLSL 600
+OpName %type_ConstantBuffer_myStruct "type.ConstantBuffer.myStruct"
+OpMemberName %type_ConstantBuffer_myStruct 0 "f"
+OpName %MyConstants "MyConstants"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %PSMain "PSMain"
+OpDecorate %out_var_SV_TARGET Location 0
+OpDecorate %MyConstants DescriptorSet 1
+OpDecorate %MyConstants Binding 2
+OpMemberDecorate %type_ConstantBuffer_myStruct 0 Offset 0
+OpDecorate %type_ConstantBuffer_myStruct Block
+%float = OpTypeFloat 32
+%type_ConstantBuffer_myStruct = OpTypeStruct %float
+%_ptr_Uniform_type_ConstantBuffer_myStruct = OpTypePointer Uniform %type_ConstantBuffer_myStruct
+%_ptr_Output_float = OpTypePointer Output %float
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%MyConstants = OpVariable %_ptr_Uniform_type_ConstantBuffer_myStruct Uniform
+%out_var_SV_TARGET = OpVariable %_ptr_Output_float Output
+%PSMain = OpFunction %void None %9
+%10 = OpLabel
+%11 = OpLoad %type_ConstantBuffer_myStruct %MyConstants
+%12 = OpCompositeExtract %float %11 0
+OpStore %out_var_SV_TARGET %12
+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 d6e376e..2d4b7a3 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
 #include <string>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dataflow.cpp b/test/opt/dataflow.cpp
index 51473d8..dcb6bc6 100644
--- a/test/opt/dataflow.cpp
+++ b/test/opt/dataflow.cpp
@@ -17,7 +17,6 @@
 #include <map>
 #include <set>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "opt/function_utils.h"
 #include "source/opt/build_module.h"
diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp
index 9e479c0..3df26a9 100644
--- a/test/opt/debug_info_manager_test.cpp
+++ b/test/opt/debug_info_manager_test.cpp
@@ -15,11 +15,8 @@
 #include "source/opt/debug_info_manager.h"
 
 #include <memory>
-#include <string>
 #include <vector>
 
-#include "effcee/effcee.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
diff --git a/test/opt/decoration_manager_test.cpp b/test/opt/decoration_manager_test.cpp
index cf3516a..b287d5e 100644
--- a/test/opt/decoration_manager_test.cpp
+++ b/test/opt/decoration_manager_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
 #include <memory>
 #include <string>
 #include <vector>
diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp
index 4315012..5f7731b 100644
--- a/test/opt/def_use_test.cpp
+++ b/test/opt/def_use_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp
index 91c950e..7a118f9 100644
--- a/test/opt/desc_sroa_test.cpp
+++ b/test/opt/desc_sroa_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/dominator_tree/common_dominators.cpp b/test/opt/dominator_tree/common_dominators.cpp
index dfa03e9..9573afa 100644
--- a/test/opt/dominator_tree/common_dominators.cpp
+++ b/test/opt/dominator_tree/common_dominators.cpp
@@ -13,9 +13,7 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp
index 4fccef0..2a5bc98 100644
--- a/test/opt/dominator_tree/generated.cpp
+++ b/test/opt/dominator_tree/generated.cpp
@@ -14,7 +14,6 @@
 
 #include <array>
 #include <memory>
-#include <set>
 #include <string>
 #include <vector>
 
diff --git a/test/opt/dominator_tree/nested_ifs.cpp b/test/opt/dominator_tree/nested_ifs.cpp
index 0552b75..848296a 100644
--- a/test/opt/dominator_tree/nested_ifs.cpp
+++ b/test/opt/dominator_tree/nested_ifs.cpp
@@ -12,9 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/nested_ifs_post.cpp b/test/opt/dominator_tree/nested_ifs_post.cpp
index ad759df..217bdec 100644
--- a/test/opt/dominator_tree/nested_ifs_post.cpp
+++ b/test/opt/dominator_tree/nested_ifs_post.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/nested_loops.cpp b/test/opt/dominator_tree/nested_loops.cpp
index 7d03937..a82f409 100644
--- a/test/opt/dominator_tree/nested_loops.cpp
+++ b/test/opt/dominator_tree/nested_loops.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/nested_loops_with_unreachables.cpp b/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
index e87e8dd..2c91bd1e 100644
--- a/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
+++ b/test/opt/dominator_tree/nested_loops_with_unreachables.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/post.cpp b/test/opt/dominator_tree/post.cpp
index bb10fde..acbf012 100644
--- a/test/opt/dominator_tree/post.cpp
+++ b/test/opt/dominator_tree/post.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/simple.cpp b/test/opt/dominator_tree/simple.cpp
index d11854d..eae2438 100644
--- a/test/opt/dominator_tree/simple.cpp
+++ b/test/opt/dominator_tree/simple.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/switch_case_fallthrough.cpp b/test/opt/dominator_tree/switch_case_fallthrough.cpp
index d9dd7d1..9eeb410 100644
--- a/test/opt/dominator_tree/switch_case_fallthrough.cpp
+++ b/test/opt/dominator_tree/switch_case_fallthrough.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/unreachable_for.cpp b/test/opt/dominator_tree/unreachable_for.cpp
index 469e5c1..bf95930 100644
--- a/test/opt/dominator_tree/unreachable_for.cpp
+++ b/test/opt/dominator_tree/unreachable_for.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/dominator_tree/unreachable_for_post.cpp b/test/opt/dominator_tree/unreachable_for_post.cpp
index 8d3e37b..57278f5 100644
--- a/test/opt/dominator_tree/unreachable_for_post.cpp
+++ b/test/opt/dominator_tree/unreachable_for_post.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/eliminate_dead_const_test.cpp b/test/opt/eliminate_dead_const_test.cpp
index 87aab54..ec4c284 100644
--- a/test/opt/eliminate_dead_const_test.cpp
+++ b/test/opt/eliminate_dead_const_test.cpp
@@ -13,9 +13,6 @@
 // limitations under the License.
 
 #include <algorithm>
-#include <cstdarg>
-#include <iostream>
-#include <sstream>
 #include <string>
 #include <unordered_set>
 #include <vector>
diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp
index 96deb2a..e9f79a1 100644
--- a/test/opt/eliminate_dead_functions_test.cpp
+++ b/test/opt/eliminate_dead_functions_test.cpp
@@ -517,6 +517,39 @@
   SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
 }
 
+TEST_F(EliminateDeadFunctionsBasicTest, DependentNonSemanticChain) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint GLCompute [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK-NOT: = OpFunction
+; CHECK: [[ext1:%\w+]] = OpExtInst %void {{%\w+}} 1 [[main]]
+; CHECK: [[ext2:%\w+]] = OpExtInst %void {{%\w+}} 2 [[ext1]]
+; CHECK: [[ext3:%\w+]] = OpExtInst %void {{%\w+}} 3 [[ext1]] [[ext2]]
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Test"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%dead = OpFunction %void None %void_fn
+%dead_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%2 = OpExtInst %void %1 1 %main
+%3 = OpExtInst %void %1 2 %2
+%4 = OpExtInst %void %1 3 %2 %3
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
+  SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/eliminate_dead_io_components_test.cpp b/test/opt/eliminate_dead_io_components_test.cpp
index da26cef..b7a2fb5 100644
--- a/test/opt/eliminate_dead_io_components_test.cpp
+++ b/test/opt/eliminate_dead_io_components_test.cpp
@@ -15,7 +15,6 @@
 
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp
index 4438f3d..bb0ec03 100644
--- a/test/opt/eliminate_dead_member_test.cpp
+++ b/test/opt/eliminate_dead_member_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "assembly_builder.h"
-#include "gmock/gmock.h"
 #include "pass_fixture.h"
 #include "pass_utils.h"
 
diff --git a/test/opt/eliminate_dead_output_stores_test.cpp b/test/opt/eliminate_dead_output_stores_test.cpp
index 470e709..4c2e44c 100644
--- a/test/opt/eliminate_dead_output_stores_test.cpp
+++ b/test/opt/eliminate_dead_output_stores_test.cpp
@@ -15,7 +15,6 @@
 
 #include <unordered_set>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/feature_manager_test.cpp b/test/opt/feature_manager_test.cpp
index 94c7734..a5105a9 100644
--- a/test/opt/feature_manager_test.cpp
+++ b/test/opt/feature_manager_test.cpp
@@ -14,9 +14,7 @@
 
 #include <algorithm>
 #include <memory>
-#include <string>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
diff --git a/test/opt/fix_func_call_arguments_test.cpp b/test/opt/fix_func_call_arguments_test.cpp
index ecd13a8..606ed26 100644
--- a/test/opt/fix_func_call_arguments_test.cpp
+++ b/test/opt/fix_func_call_arguments_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp
index 1c0101a..93ce873 100644
--- a/test/opt/fix_storage_class_test.cpp
+++ b/test/opt/fix_storage_class_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp
index 63207fd..d7ac2ab 100644
--- a/test/opt/flatten_decoration_test.cpp
+++ b/test/opt/flatten_decoration_test.cpp
@@ -16,7 +16,6 @@
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index cc9bcd6..eff8edf 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -27,7 +27,6 @@
 #include "source/opt/ir_context.h"
 #include "source/opt/module.h"
 #include "spirv-tools/libspirv.hpp"
-#include "test/opt/pass_utils.h"
 
 namespace spvtools {
 namespace opt {
@@ -293,6 +292,7 @@
 %v4float_null = OpConstantNull %v4float
 %mat4v4float_null = OpConstantComposite %mat4v4float %v4float_null %v4float_null %v4float_null %v4float_null
 %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
 %v4double_0_0_0_0 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
 %v4double_0_0_0_1 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_1
@@ -303,6 +303,7 @@
 %v4double_null = OpConstantNull %v4double
 %mat4v4double_null = OpConstantComposite %mat4v4double %v4double_null %v4double_null %v4double_null %v4double_null
 %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
 %uint_0x3f800000 = OpConstant %uint 0x3f800000
 %uint_0xbf800000 = OpConstant %uint 0xbf800000
@@ -1050,7 +1051,16 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {30.0,30.0,30.0,30.0}),
-   // Test case 4: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+   // Test case 4: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {1.0, 2.0, 3.0, 4.0} {30.0, 0.0, 30.0, 0.0}
+   InstructionFoldingCase<std::vector<double>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_1_2_3_4_null\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {30.0,0.0,30.0,0.0}),
+   // Test case 5: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<double>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1059,7 +1069,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0,0.0,0.0,0.0}),
-   // Test case 5: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+   // Test case 6: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<double>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1068,7 +1078,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0,0.0,0.0,0.0}),
-   // Test case 6: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+   // Test case 7: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
    InstructionFoldingCase<std::vector<double>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1076,7 +1086,16 @@
        "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_1_2_3_4\n" +
        "OpReturn\n" +
        "OpFunctionEnd",
-       2, {10.0,20.0,30.0,40.0})
+       2, {10.0,20.0,30.0,40.0}),
+   // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {10.0, 20.0, 30.0, 40.0}
+   InstructionFoldingCase<std::vector<double>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4_null %v4double_1_2_3_4\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {4.0,8.0,12.0,16.0})
 ));
 
 using FloatVectorInstructionFoldingTest =
@@ -1155,7 +1174,16 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 4: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+   // Test case 4: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {1.0, 2.0, 3.0, 4.0} {30.0, 0.0, 30.0, 0.0}
+   InstructionFoldingCase<std::vector<float>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_1_2_3_4_null\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {30.0,0.0,30.0,0.0}),
+   // Test case 5: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1164,7 +1192,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 5: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
+   // Test case 6: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1173,7 +1201,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {30.0f,30.0f,30.0f,30.0f}),
-   // Test case 6: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+   // Test case 7: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1182,7 +1210,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 7: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+   // Test case 8: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1191,7 +1219,7 @@
        "OpReturn\n" +
        "OpFunctionEnd",
        2, {0.0f,0.0f,0.0f,0.0f}),
-   // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+   // Test case 9: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
    InstructionFoldingCase<std::vector<float>>(
        Header() +
        "%main = OpFunction %void None %void_func\n" +
@@ -1199,7 +1227,16 @@
        "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_1_2_3_4\n" +
        "OpReturn\n" +
        "OpFunctionEnd",
-       2, {10.0f,20.0f,30.0f,40.0f})
+       2, {10.0f,20.0f,30.0f,40.0f}),
+   // Test case 10: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, Null, {1.0, 2.0, 3.0, 4.0}, Null} {10.0, 20.0, 30.0, 40.0}
+   InstructionFoldingCase<std::vector<float>>(
+       Header() +
+       "%main = OpFunction %void None %void_func\n" +
+       "%main_lab = OpLabel\n" +
+       "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4_null %v4float_1_2_3_4\n" +
+       "OpReturn\n" +
+       "OpFunctionEnd",
+       2, {4.0,8.0,12.0,16.0})
 ));
 // clang-format on
 using BooleanInstructionFoldingTest =
@@ -7372,7 +7409,16 @@
             "%4 = OpCompositeInsert %struct_v2int_int_int %int_1 %struct_v2int_int_int_null 2\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        4, false)
+        4, false),
+    // Test case 17: Don't fold when index into composite is out of bounds.
+    InstructionFoldingCase<bool>(
+	Header() +
+            "%main = OpFunction %void None %void_func\n" +
+	    "%main_lab = OpLabel\n" +
+	    "%4 = OpCompositeExtract %int %struct_v2int_int_int 3\n" +
+	    "OpReturn\n" +
+	    "OpFunctionEnd",
+	4, false)
 ));
 
 INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest,
diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp
index ad0fc32..1ccaa3e 100644
--- a/test/opt/freeze_spec_const_test.cpp
+++ b/test/opt/freeze_spec_const_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
 #include <string>
 #include <tuple>
 #include <utility>
diff --git a/test/opt/function_test.cpp b/test/opt/function_test.cpp
index 09cca33..6a40e93 100644
--- a/test/opt/function_test.cpp
+++ b/test/opt/function_test.cpp
@@ -13,8 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <sstream>
-#include <string>
 #include <vector>
 
 #include "function_utils.h"
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 057b909..a1a3b7d 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -12,12 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <array>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "pass_fixture.h"
 #include "pass_utils.h"
 #include "source/opt/graphics_robust_access_pass.h"
diff --git a/test/opt/if_conversion_test.cpp b/test/opt/if_conversion_test.cpp
index dc7f831..c1425e8 100644
--- a/test/opt/if_conversion_test.cpp
+++ b/test/opt/if_conversion_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index d450511..dd4b6f6 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -18,7 +18,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
@@ -42,16 +41,18 @@
 ; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
 )";
 
-static const std::string kStreamWrite4Begin = R"(
-; CHECK: %inst_bindless_stream_write_4 = OpFunction %void None {{%\w+}}
+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_10
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
+; 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
@@ -59,7 +60,7 @@
 ; 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: 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
@@ -68,7 +69,7 @@
 ; CHECK: OpStore {{%\w+}} [[param_1]]
 )";
 
-static const std::string kStreamWrite4End = R"(
+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]]
@@ -78,6 +79,12 @@
 ; 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
@@ -85,7 +92,7 @@
 )";
 
 // clang-format off
-static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"(
+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
@@ -99,9 +106,9 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
+)" + kStreamWrite6End;
 
-static const std::string kStreamWrite4Tese = kStreamWrite4Begin + R"(
+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
@@ -119,9 +126,9 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
+)" + kStreamWrite6End;
 
-static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"(
+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
@@ -133,9 +140,9 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
+)" + kStreamWrite6End;
 
-static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"(
+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
@@ -152,9 +159,9 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
+)" + kStreamWrite6End;
 
-static const std::string kStreamWrite4Ray = kStreamWrite4Begin + R"(
+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+}}
@@ -171,20 +178,22 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
+)" + kStreamWrite6End;
 // clang-format on
 
-static const std::string kStreamWrite5Begin = R"(
-; CHECK: %inst_bindless_stream_write_5 = OpFunction %void None {{%\w+}}
+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_11
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11
+; 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
@@ -192,7 +201,7 @@
 ; 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_11
+; 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
@@ -201,7 +210,7 @@
 ; CHECK: OpStore {{%\w+}} [[param_1]]
 )";
 
-static const std::string kStreamWrite5End = R"(
+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]]
@@ -214,6 +223,12 @@
 ; 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
@@ -221,7 +236,7 @@
 )";
 
 // clang-format off
-static const std::string kStreamWrite5Frag = kStreamWrite5Begin + R"(
+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
@@ -235,9 +250,9 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite4End;
+)" + kStreamWrite7End;
 
-static const std::string kStreamWrite5Vert = kStreamWrite5Begin + R"(
+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
@@ -249,10 +264,13 @@
 ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
 ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
 ; CHECK: OpStore {{%\w+}} {{%\w+}}
-)" + kStreamWrite5End;
+)" + 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
@@ -260,61 +278,90 @@
 )";
 
 static const std::string kInputGlobals = R"(
-; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_uint
+; 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 kDirectRead2 = R"(
-; CHECK: %inst_bindless_direct_read_2 = OpFunction %uint None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+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+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; 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 kDirectRead3 = R"(
- ;CHECK: %inst_bindless_direct_read_3 = OpFunction %uint None {{%\w+}}
-; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
-; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
- ;CHECK: {{%\w+}} = OpLabel
- ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
- ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
- ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
- ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
- ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
- ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]]
- ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
- ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
- ;CHECK: OpReturnValue {{%\w+}}
- ;CHECK: OpFunctionEnd
-)";
-
-static const std::string kDirectRead4 = R"(
-; CHECK: %inst_bindless_direct_read_4 = OpFunction %uint 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
+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+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
-; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_4]]
-; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
-; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; 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
 )";
@@ -355,8 +402,8 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_tColor Binding 5
+OpDecorate %g_sAniso DescriptorSet 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
 %void = OpTypeVoid
@@ -438,10 +485,10 @@
 OpName %g_sAniso "g_sAniso"
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
+OpDecorate %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
@@ -590,25 +637,25 @@
 OpStore %_entryPointOutput_vColor %37
 ; CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30
 ; CHECK-NOT: OpStore %_entryPointOutput_vColor %37
-; CHECK: %40 = OpULessThan %bool %32 %uint_128
-; CHECK: OpSelectionMerge %41 None
-; CHECK: OpBranchConditional %40 %42 %43
-; CHECK: %42 = OpLabel
-; CHECK: %44 = OpLoad %16 %33
-; CHECK: %45 = OpSampledImage %26 %44 %35
-; CHECK: %46 = OpImageSampleImplicitLod %v4float %45 %30
-; CHECK: OpBranch %41
-; CHECK: %43 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_56 %uint_0 %32 %uint_128
-; CHECK: OpBranch %41
-; CHECK: %41 = OpLabel
-; CHECK: %104 = OpPhi %v4float %46 %42 [[null_v4float]] %43
-; CHECK: OpStore %_entryPointOutput_vColor %104
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Frag;
+  const std::string output_func = kStreamWrite6Frag;
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>(
@@ -666,11 +713,11 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor Binding 4
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
 OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
+OpDecorate %g_sAniso DescriptorSet 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
@@ -736,10 +783,10 @@
 ; CHECK: %54 = OpImageSampleImplicitLod %v4float %53 %31
 ; CHECK: OpBranch %49
 ; CHECK: %51 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_58 %uint_0 %33 %uint_128
+; 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: %112 = OpPhi %v4float %54 %50 [[null_v4float]] %51
+; CHECK: {{%\w+}} = OpPhi %v4float %54 %50 [[null_v4float]] %51
 %39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
 %40 = OpLoad %uint %39
 %41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40
@@ -749,26 +796,26 @@
 %45 = OpFAdd %v4float %38 %44
 ; CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31
 ; CHECK-NOT: %45 = OpFAdd %v4float %38 %44
-; CHECK: %113 = OpULessThan %bool %40 %uint_128
-; CHECK: OpSelectionMerge %114 None
-; CHECK: OpBranchConditional %113 %115 %116
-; CHECK: %115 = OpLabel
-; CHECK: %117 = OpLoad %17 %41
-; CHECK: %118 = OpSampledImage %27 %117 %36
-; CHECK: %119 = OpImageSampleImplicitLod %v4float %118 %31
-; CHECK: OpBranch %114
-; CHECK: %116 = OpLabel
-; CHECK: %121 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_64 %uint_0 %40 %uint_128
-; CHECK: OpBranch %114
-; CHECK: %114 = OpLabel
-; CHECK: %122 = OpPhi %v4float %119 %115 [[null_v4float]] %116
-; CHECK: %45 = OpFAdd %v4float %112 %122
+; 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+}}
 OpStore %_entryPointOutput_vColor %45
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Frag;
+  const std::string output_func = kStreamWrite6Frag;
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
@@ -800,7 +847,7 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor Binding 9
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %i_vTextureCoords Location 0
@@ -865,16 +912,16 @@
 ; CHECK: %84 = OpImageRead %v4float %83 %53
 ; CHECK: OpBranch %79
 ; CHECK: %81 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %64 %uint_128
+; 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: %142 = OpPhi %v4float %84 %80 [[null_v4float]] %81
-; CHECK: OpStore %_entryPointOutput_vColor %142
+; CHECK: {{%\w+}} = OpPhi %v4float %84 %80 [[null_v4float]] %81
+; CHECK: OpStore %_entryPointOutput_vColor {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Frag;
+  const std::string output_func = kStreamWrite6Frag;
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
@@ -904,8 +951,8 @@
 OpName %_ ""
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor DescriptorSet 4
+OpDecorate %g_tColor Binding 11
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %i_vTextureCoords Location 0
@@ -967,16 +1014,16 @@
 ; CHECK: %79 = OpImageSampleImplicitLod %v4float %78 %53
 ; CHECK: OpBranch %75
 ; CHECK: %77 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_49 %uint_0 %64 %uint_128
+; 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: %137 = OpPhi %v4float %79 %76 [[null_v4float]] %77
-; CHECK: OpStore %_entryPointOutput_vColor %137
+; CHECK: {{%\w+}} = OpPhi %v4float %79 %76 [[null_v4float]] %77
+; CHECK: OpStore %_entryPointOutput_vColor {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Frag;
+  const std::string output_func = kStreamWrite6Frag;
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
@@ -1007,8 +1054,8 @@
 OpName %_ ""
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor DescriptorSet 30
+OpDecorate %g_tColor Binding 2
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %i_vTextureCoords Location 0
@@ -1068,7 +1115,7 @@
 ; CHECK: OpImageWrite %39 %28 %19
 ; CHECK: OpBranch %36
 ; CHECK: %38 = OpLabel
-; CHECK: %95 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %30 %uint_128
+; 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
@@ -1076,7 +1123,7 @@
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Frag;
+  const std::string output_func = kStreamWrite6Frag;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
@@ -1121,11 +1168,11 @@
 OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
 OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
 OpDecorate %gl_PerVertex Block
-OpDecorate %texSampler1D DescriptorSet 0
-OpDecorate %texSampler1D Binding 3
+OpDecorate %texSampler1D DescriptorSet 2
+OpDecorate %texSampler1D Binding 13
 OpMemberDecorate %foo 0 Offset 0
 OpDecorate %foo Block
-OpDecorate %__0 DescriptorSet 0
+OpDecorate %__0 DescriptorSet 7
 OpDecorate %__0 Binding 5
 OpDecorate %coords2D Location 0
 %void = OpTypeVoid
@@ -1196,18 +1243,18 @@
 ; CHECK: %51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41
 ; CHECK: OpBranch %47
 ; CHECK: %49 = OpLabel
-; CHECK: [[bitcast_result:%\w+]] = OpBitcast %uint %37
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_74 %uint_0 [[bitcast_result]] %uint_128
+; 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: %107 = OpPhi %v4float %51 %48 [[null_v4float]] %49
+; CHECK: {{%\w+}} = OpPhi %v4float %51 %48 [[null_v4float]] %49
 ; CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-; CHECK: OpStore %43 %107
+; CHECK: OpStore %43 {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Vert;
+  const std::string output_func = kStreamWrite6Vert;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
@@ -1224,9 +1271,9 @@
   // #version 450
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
-  // layout(std140, set = 0, binding = 0) uniform ufoo { uint index; } uniform_index_buffer;
+  // layout(std140, set = 9, binding = 1) uniform ufoo { uint index; } uniform_index_buffer;
   //
-  // layout(set = 0, binding = 1) buffer bfoo { vec4 val; } adds[11];
+  // layout(set = 9, binding = 2) buffer bfoo { vec4 val; } adds[11];
   //
   // layout(triangles, equal_spacing, cw) in;
   //
@@ -1266,12 +1313,12 @@
 OpDecorate %gl_PerVertex Block
 OpMemberDecorate %bfoo 0 Offset 0
 OpDecorate %bfoo Block
-OpDecorate %adds DescriptorSet 0
+OpDecorate %adds DescriptorSet 9
 OpDecorate %adds Binding 1
 OpMemberDecorate %ufoo 0 Offset 0
 OpDecorate %ufoo Block
-OpDecorate %uniform_index_buffer DescriptorSet 0
-OpDecorate %uniform_index_buffer Binding 0
+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
@@ -1328,19 +1375,19 @@
 ; CHECK: %38 = OpLoad %v4float %29
 ; CHECK: OpBranch %35
 ; CHECK: %37 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_63 %uint_0 %28 %uint_11
+; 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: %102 = OpPhi %v4float %38 %36 [[null_v4float]] %37
+; CHECK: {{%\w+}} = OpPhi %v4float %38 %36 [[null_v4float]] %37
 %31 = OpAccessChain %_ptr_Output_v4float %_ %int_0
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: OpStore %31 %102
+; CHECK: OpStore %31 {{%\w+}}
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Tese;
+  const std::string output_func = kStreamWrite6Tese;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
@@ -1380,12 +1427,12 @@
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
 OpName %param "param"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
+OpDecorate %g_tColor DescriptorSet 1
+OpDecorate %g_tColor Binding 2
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 1
+OpDecorate %g_sAniso DescriptorSet 1
+OpDecorate %g_sAniso Binding 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
@@ -1481,7 +1528,7 @@
 ; CHECK: OpNoLine
 ; CHECK: OpBranch %63
 ; CHECK: %65 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_109 %uint_0 %50 %uint_128
+; 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
@@ -1497,7 +1544,7 @@
 OpFunctionEnd
 )";
 
-  const std::string output_func = kStreamWrite4Frag;
+  const std::string output_func = kStreamWrite6Frag;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(
@@ -1535,7 +1582,7 @@
 OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
 OpDecorate %PerViewConstantBuffer_t Block
 OpDecorate %g_sAniso DescriptorSet 1
-OpDecorate %g_sAniso Binding 0
+OpDecorate %g_sAniso Binding 3
 OpDecorate %i_vTextureCoords Location 0
 OpDecorate %_entryPointOutput_vColor Location 0
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
@@ -1567,12 +1614,7 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %41 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
+)" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ; CHECK: %v4uint = OpTypeVector %uint 4
@@ -1594,39 +1636,40 @@
 OpStore %_entryPointOutput_vColor %71
 ; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53
 ; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
-; CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_2 %uint_2
-; CHECK: %57 = OpULessThan %bool %32 %55
-; CHECK: OpSelectionMerge %58 None
-; CHECK: OpBranchConditional %57 %59 %60
-; CHECK: %59 = OpLabel
-; CHECK: %61 = OpLoad %16 %33
-; CHECK: %62 = OpSampledImage %26 %61 %35
-; CHECK: %136 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_1 %uint_2 %32
-; CHECK: %137 = OpULessThan %bool %uint_0 %136
-; CHECK: OpSelectionMerge %138 None
-; CHECK: OpBranchConditional %137 %139 %140
-; CHECK: %139 = OpLabel
-; CHECK: %141 = OpLoad %16 %33
-; CHECK: %142 = OpSampledImage %26 %141 %35
-; CHECK: %143 = OpImageSampleImplicitLod %v4float %142 %30
-; CHECK: OpBranch %138
-; CHECK: %140 = OpLabel
-; CHECK: %144 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_1 %32 %uint_0
-; CHECK: OpBranch %138
-; CHECK: %138 = OpLabel
-; CHECK: [[phi_result_1:%\w+]] = OpPhi %v4float %143 %139 [[null_v4float]] %140
-; CHECK: OpBranch %58
-; CHECK: %60 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_0 %32 %55
-; CHECK: OpBranch %58
-; CHECK: %58 = OpLabel
-; CHECK: [[phi_result_2:%\w+]] = OpPhi %v4float [[phi_result_1]] %138 [[null_v4float]] %60
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Frag + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -1657,10 +1700,10 @@
 OpName %g_sAniso "g_sAniso"
 OpName %i_vTextureCoords "i.vTextureCoords"
 OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
+OpDecorate %g_tColor DescriptorSet 1
+OpDecorate %g_tColor Binding 2
+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
@@ -1682,13 +1725,7 @@
 %i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
 %_ptr_Output_v4float = OpTypePointer Output %v4float
 %_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %28 = OpTypeFunction %uint %uint %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
+)" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ; CHECK: %v4uint = OpTypeVector %uint 4
@@ -1707,26 +1744,26 @@
 OpStore %_entryPointOutput_vColor %24
 ; CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20
 ; CHECK-NOT: OpStore %_entryPointOutput_vColor %24
-; CHECK: %50 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %52 = OpULessThan %bool %uint_0 %50
-; CHECK: OpSelectionMerge %54 None
-; CHECK: OpBranchConditional %52 %55 %56
-; CHECK: %55 = OpLabel
-; CHECK: %57 = OpLoad %12 %g_tColor
-; CHECK: %58 = OpSampledImage %16 %57 %22
-; CHECK: %59 = OpImageSampleImplicitLod %v4float %58 %20
-; CHECK: OpBranch %54
-; CHECK: %56 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_39 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %54
-; CHECK: %54 = OpLabel
-; CHECK: [[phi_result:%\w+]] = OpPhi %v4float %59 %55 [[null_v4float]] %56
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag;
+  const std::string new_funcs = kReadDescInit + kStreamWrite6Frag;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -1746,10 +1783,10 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
 OpExecutionMode %foo OriginUpperLeft
-OpDecorate %image_var DescriptorSet 0
-OpDecorate %image_var Binding 0
-OpDecorate %sampler_var DescriptorSet 0
-OpDecorate %sampler_var Binding 1
+OpDecorate %image_var DescriptorSet 4
+OpDecorate %image_var Binding 1
+OpDecorate %sampler_var DescriptorSet 4
+OpDecorate %sampler_var Binding 2
 OpDecorate %gid DescriptorSet 0
 OpDecorate %gid Binding 2
 OpDecorate %struct Block
@@ -1805,12 +1842,12 @@
 OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var
 OpEntryPoint Fragment %foo "bar" %gid %image_var %sampler_var
 OpExecutionMode %foo OriginUpperLeft
-OpDecorate %image_var DescriptorSet 0
-OpDecorate %image_var Binding 0
-OpDecorate %sampler_var DescriptorSet 0
-OpDecorate %sampler_var Binding 1
-OpDecorate %gid DescriptorSet 0
-OpDecorate %gid Binding 2
+OpDecorate %image_var DescriptorSet 3
+OpDecorate %image_var Binding 2
+OpDecorate %sampler_var DescriptorSet 3
+OpDecorate %sampler_var Binding 3
+OpDecorate %gid DescriptorSet 3
+OpDecorate %gid Binding 4
 OpDecorate %struct Block
 OpMemberDecorate %struct 0 Offset 0
 %void = OpTypeVoid
@@ -1857,7 +1894,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=0) out float b;
   //
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer[];
+  // layout(set = 6, binding=3)  uniform uname { float a; }  uniformBuffer[];
   //
   // void main()
   // {
@@ -1888,7 +1925,7 @@
 OpDecorate %b Location 0
 OpMemberDecorate %uname 0 Offset 0
 OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
+OpDecorate %uniformBuffer DescriptorSet 6
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
@@ -1897,10 +1934,10 @@
 OpDecorate %20 NonUniform
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + R"(
-; CHECK: OpDecorate %130 NonUniform
+; CHECK: OpDecorate {{%\w+}} NonUniform
 )" + kOutputDecorations + R"(
 ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-; CHECK: OpDecorate %127 NonUniform
+; CHECK: OpDecorate {{%\w+}} NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -1915,13 +1952,7 @@
 %nu_ii = OpVariable %_ptr_Input_int Input
 %int_0 = OpConstant %int 0
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
+)" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %v4float = OpTypeVector %float 4
 ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
@@ -1939,38 +1970,39 @@
 OpStore %b %20
 ; CHECK-NOT: %20 = OpLoad %float %19
 ; CHECK-NOT: OpStore %b %20
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %103 = OpBitcast %uint %7
-; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
-; CHECK: %123 = OpULessThan %bool %uint_0 %122
-; CHECK: OpSelectionMerge %124 None
-; CHECK: OpBranchConditional %123 %125 %126
-; CHECK: %125 = OpLabel
-; CHECK: %127 = OpLoad %float %20
-; CHECK: OpBranch %124
-; CHECK: %126 = OpLabel
-; CHECK: %128 = OpBitcast %uint %7
-; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
-; CHECK: OpBranch %124
-; CHECK: %124 = OpLabel
-; CHECK: [[phi_result_1:%\w+]] = OpPhi %float %127 %125 [[null_float]] %126
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
+; 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_4 %uint_45 %uint_0 [[bitcast_result]] %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
-; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] %124 [[null_float]] %45
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Frag + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -1985,7 +2017,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=0) out float b;
   //
-  // layout(binding=3)  buffer bname { float b; }  storageBuffer[];
+  // layout(set = 7, binding=3)  buffer bname { float b; }  storageBuffer[];
   //
   // void main()
   // {
@@ -2016,7 +2048,7 @@
 OpDecorate %b Location 0
 OpMemberDecorate %bname 0 Offset 0
 OpDecorate %bname Block
-OpDecorate %storageBuffer DescriptorSet 0
+OpDecorate %storageBuffer DescriptorSet 7
 OpDecorate %storageBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
@@ -2024,11 +2056,8 @@
 OpDecorate %16 NonUniform
 OpDecorate %20 NonUniform
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-)" + kInputDecorations + R"(
-; CHECK: OpDecorate %130 NonUniform
-)" + kOutputDecorations + R"(
+)" + kInputDecorations + kOutputDecorations + R"(
 ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-; CHECK: OpDecorate %127 NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2044,12 +2073,8 @@
 %int_0 = OpConstant %int 0
 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
 ; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
 ; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
+)" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %v4float = OpTypeVector %float 4
 ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
@@ -2067,38 +2092,39 @@
 OpStore %b %20
 ; CHECK-NOT: %20 = OpLoad %float %19
 ; CHECK-NOT: OpStore %b %20
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
+; 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: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 [[bitcast_result_1]]
-; CHECK: %123 = OpULessThan %bool %uint_0 %122
-; CHECK: OpSelectionMerge %124 None
-; CHECK: OpBranchConditional %123 %125 %126
-; CHECK: %125 = OpLabel
-; CHECK: %127 = OpLoad %float %20
-; CHECK: OpBranch %124
-; CHECK: %126 = OpLabel
-; CHECK: %128 = OpBitcast %uint %7
-; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
-; CHECK: OpBranch %124
-; CHECK: %124 = OpLabel
-; CHECK: [[phi_result_1:%\w+]] = OpPhi %float %127 %125 [[null_float]] %126
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
+; 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_4 %uint_45 %uint_0 [[bitcast_result_2]] %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
-; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] %124 [[null_float]] %45
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Frag + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2142,10 +2168,10 @@
 OpDecorate %20 NonUniform
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + R"(
-; CHECK: OpDecorate %130 NonUniform
+; CHECK: OpDecorate {{%\w+}} NonUniform
 )" + kOutputDecorations + R"(
 ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-; CHECK: OpDecorate %127 NonUniform
+; CHECK: OpDecorate {{%\w+}} NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2160,13 +2186,7 @@
 %nu_ii = OpVariable %_ptr_Input_int Input
 %int_0 = OpConstant %int 0
 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %26 = OpTypeFunction %uint %uint %uint
-; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
+)" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %v4float = OpTypeVector %float 4
 ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
@@ -2184,38 +2204,39 @@
 OpStore %b %20
 ; CHECK-NOT: %20 = OpLoad %float %19
 ; CHECK-NOT: OpStore %b %20
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %103 = OpBitcast %uint %7
-; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
-; CHECK: %123 = OpULessThan %bool %uint_0 %122
-; CHECK: OpSelectionMerge %124 None
-; CHECK: OpBranchConditional %123 %125 %126
-; CHECK: %125 = OpLabel
-; CHECK: %127 = OpLoad %float %20
-; CHECK: OpBranch %124
-; CHECK: %126 = OpLabel
-; CHECK: %128 = OpBitcast %uint %7
-; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
-; CHECK: OpBranch %124
-; CHECK: %124 = OpLabel
-; CHECK: [[phi_result_1:%\w+]] = OpPhi %float %127 %125 [[null_float]] %126
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
+; 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_4 %uint_45 %uint_0 [[bitcast_result]] %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
-; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] %124 [[null_float]] %45
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Frag + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2228,7 +2249,7 @@
   // #extension GL_EXT_nonuniform_qualifier : enable
   //
   // layout(location=0) out float b;
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer;
+  // layout(set=7, binding=3)  uniform uname { float a; }  uniformBuffer;
   //
   // void main()
   // {
@@ -2255,7 +2276,7 @@
 OpDecorate %b Location 0
 OpMemberDecorate %uname 0 Offset 0
 OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
+OpDecorate %uniformBuffer DescriptorSet 7
 OpDecorate %uniformBuffer Binding 3
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -2274,12 +2295,8 @@
 ; CHECK: %int = OpTypeInt 32 1
 ; CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float
 ; CHECK: %uint = OpTypeInt 32 0
-; CHECK: %21 = OpTypeFunction %uint %uint %uint %uint %uint
 ; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-)" + kInputGlobals + R"(
-; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-; CHECK: %bool = OpTypeBool
-)" + kOutputGlobals + R"(
+)" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %v4float = OpTypeVector %float 4
 ; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
@@ -2296,24 +2313,24 @@
 OpStore %b %16
 ; CHECK-NOT: %16 = OpLoad %float %15
 ; CHECK-NOT: OpStore %b %16
-; CHECK: %43 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %uint_0
-; CHECK: %45 = OpULessThan %bool %uint_0 %43
-; CHECK: OpSelectionMerge %47 None
-; CHECK: OpBranchConditional %45 %48 %49
-; CHECK: %48 = OpLabel
-; CHECK: %50 = OpLoad %float %15
-; CHECK: OpBranch %47
-; CHECK: %49 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_32 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %47
-; CHECK: %47 = OpLabel
-; CHECK: [[phi_result:%\w+]] = OpPhi %float %50 %48 [[null_float]] %49
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag;
+  const std::string new_funcs = kReadDescInit + kStreamWrite6Frag;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2328,7 +2345,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=1) in float b;
   //
-  // layout(binding=4)  buffer bname { float b; }  storageBuffer[];
+  // layout(set=5, binding=4)  buffer bname { float b; }  storageBuffer[];
   //
   // void main()
   // {
@@ -2357,7 +2374,7 @@
 OpName %b "b"
 OpMemberDecorate %bname 0 Offset 0
 OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
+OpDecorate %storageBuffer DescriptorSet 5
 OpDecorate %storageBuffer Binding 4
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
@@ -2384,7 +2401,6 @@
 )" + kInputGlobals + kOutputGlobals + R"(
 ; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
 ; CHECK: %v4uint = OpTypeVector %uint 4
-; CHECK: %102 = OpTypeFunction %uint %uint %uint %uint %uint
 )";
   // clang-format on
 
@@ -2396,35 +2412,36 @@
 %20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0
 OpStore %20 %18
 ; CHECK-NOT: OpStore %20 %18
-; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_4
-; CHECK: %42 = OpULessThan %bool %7 %40
-; CHECK: OpSelectionMerge %43 None
-; CHECK: OpBranchConditional %42 %44 %45
-; CHECK: %44 = OpLabel
-; CHECK: %100 = OpBitcast %uint %7
-; CHECK: %119 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_4 %100
-; CHECK: %120 = OpULessThan %bool %uint_0 %119
-; CHECK: OpSelectionMerge %121 None
-; CHECK: OpBranchConditional %120 %122 %123
-; CHECK: %122 = OpLabel
+; 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 %121
-; CHECK: %123 = OpLabel
-; CHECK: %124 = OpBitcast %uint %7
-; CHECK: %125 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %124 %uint_0
-; CHECK: OpBranch %121
-; CHECK: %121 = OpLabel
-; CHECK: OpBranch %43
-; CHECK: %45 = OpLabel
-; CHECK: %46 = OpBitcast %uint %7
-; CHECK: %99 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %46 %40
-; CHECK: OpBranch %43
-; CHECK: %43 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Frag + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2439,7 +2456,7 @@
   // layout(location=0) in nonuniformEXT flat int nu_ii;
   // layout(location=0) out float b;
   //
-  // layout(binding=3)  uniform uname { float a; }  uniformBuffer[128];
+  // layout(set=1, binding=3)  uniform uname { float a; }  uniformBuffer[128];
   //
   // void main()
   // {
@@ -2469,7 +2486,7 @@
 OpDecorate %b Location 0
 OpMemberDecorate %uname 0 Offset 0
 OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
+OpDecorate %uniformBuffer DescriptorSet 1
 OpDecorate %uniformBuffer Binding 3
 OpDecorate %nu_ii Flat
 OpDecorate %nu_ii Location 0
@@ -2480,7 +2497,7 @@
 )" + kOutputDecorations + R"(
 ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 )" + kInputDecorations + R"(
-; CHECK: OpDecorate %117 NonUniform
+; CHECK: OpDecorate [[load_result:%\w+]] NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %float = OpTypeFloat 32
@@ -2515,37 +2532,37 @@
 OpStore %b %22
 ; CHECK-NOT: %22 = OpLoad %float %21
 ; CHECK-NOT: OpStore %b %22
-; CHECK: %25 = OpULessThan %bool %7 %uint_128
-; CHECK: OpSelectionMerge %26 None
-; CHECK: OpBranchConditional %25 %27 %28
-; CHECK: %27 = OpLabel
-; CHECK: %90 = OpBitcast %uint %7
-; CHECK: %112 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %90
-; CHECK: %113 = OpULessThan %bool %uint_0 %112
-; CHECK: OpSelectionMerge %114 None
-; CHECK: OpBranchConditional %113 %115 %116
-; CHECK: %115 = OpLabel
-; CHECK: %117 = OpLoad %float %22
-; CHECK: OpBranch %114
-; CHECK: %116 = OpLabel
-; CHECK: %118 = OpBitcast %uint %7
-; CHECK: %119 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_1 %118 %uint_0
-; CHECK: OpBranch %114
-; CHECK: %114 = OpLabel
-; CHECK: [[phi_result_1:%\w+]] = OpPhi %float %117 %115 [[null_float]] %116
-; CHECK: OpBranch %26
-; CHECK: %28 = OpLabel
-; CHECK: [[bitcast_result:%\w+]] = OpBitcast %uint %7
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_0 [[bitcast_result]] %uint_128
-; CHECK: OpBranch %26
-; CHECK: %26 = OpLabel
-; CHECK: [[phi_result_2:%\w+]] = OpPhi %float [[phi_result_1]] %114 [[null_float]] %28
+; 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]]
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs = kStreamWrite6Frag + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2560,12 +2577,12 @@
   //
   // layout (local_size_x = 1, local_size_y = 1) in;
   //
-  // layout(set = 0, binding = 0, std140) buffer Input {
+  // layout(set = 2, binding = 0, std140) buffer Input {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 2, binding = 1, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -2594,9 +2611,9 @@
 OpMemberDecorate %Input 0 Offset 0
 OpMemberDecorate %Input 1 Offset 4
 OpDecorate %Input BufferBlock
-OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo DescriptorSet 2
 OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
+OpDecorate %images DescriptorSet 2
 OpDecorate %images Binding 1
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
@@ -2644,64 +2661,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: %132 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %133 = OpULessThan %bool %uint_0 %132
-; CHECK: OpSelectionMerge %134 None
-; CHECK: OpBranchConditional %133 %135 %136
-; CHECK: %135 = OpLabel
-; CHECK: %137 = OpLoad %uint %25
-; CHECK: OpBranch %134
-; CHECK: %136 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_47 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %134
-; CHECK: %134 = OpLabel
-; CHECK: [[phi_result_1:%\w+]] = OpPhi %uint %137 %135 [[null_uint]] %136
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %141
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %141 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: {{%\w+}} = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 [[phi_result_1]]
-; CHECK: %143 = OpULessThan %bool %uint_0 %142
-; CHECK: OpSelectionMerge %144 None
-; CHECK: OpBranchConditional %143 %145 %146
-; CHECK: %145 = OpLabel
-; CHECK: %147 = OpLoad %13 %27
-; CHECK: %148 = OpImageRead %v4float %147 %20
-; CHECK: OpBranch %144
-; CHECK: %146 = OpLabel
-; CHECK: %149 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_1 %141 %uint_0
-; CHECK: OpBranch %144
-; CHECK: %144 = OpLabel
-; CHECK: [[phi_result_2:%\w+]] = OpPhi %v4float %148 %145 [[null_v4float]] %146
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_0 %141 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %113 = OpPhi %v4float [[phi_result_2]] %144 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %113 0
-; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-; CHECK: %151 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %152 = OpULessThan %bool %uint_0 %151
-; CHECK: OpSelectionMerge %153 None
-; CHECK: OpBranchConditional %152 %154 %155
-; CHECK: %154 = OpLabel
-; CHECK: OpStore %31 %30
-; CHECK: OpBranch %153
-; CHECK: %155 = OpLabel
-; CHECK: %157 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_53 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %153
-; CHECK: %153 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
   const std::string new_funcs =
-      kDirectRead2 + kStreamWrite4Compute + kDirectRead4;
+      kReadBindingLength + kStreamWrite6Compute + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2715,12 +2732,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 3, binding = 1, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 3, binding = 5, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -2750,10 +2767,10 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 3
+OpDecorate %sbo Binding 1
+OpDecorate %images DescriptorSet 3
+OpDecorate %images Binding 5
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -2797,63 +2814,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 [[null_uint]] %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 [[null_v4float]] %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
+; 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: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
+; 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 %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Ray + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -2867,12 +2885,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 5, binding = 1, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 5, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -2902,10 +2920,10 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 5
+OpDecorate %sbo Binding 1
+OpDecorate %images DescriptorSet 5
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -2950,63 +2968,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 [[null_uint]] %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 [[null_v4float]] %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
+; 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: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
+; 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 %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Ray + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -3020,12 +3039,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 2, binding = 1, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 2, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3055,10 +3074,10 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 2
+OpDecorate %sbo Binding 1
+OpDecorate %images DescriptorSet 2
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -3103,63 +3122,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 [[null_uint]] %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 [[null_v4float]] %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
+; 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: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
+; 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 %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Ray + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -3173,12 +3193,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 1, binding = 2, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 1, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3208,10 +3228,10 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 1
+OpDecorate %sbo Binding 2
+OpDecorate %images DescriptorSet 1
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -3256,63 +3276,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 [[null_uint]] %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 [[null_v4float]] %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
+; 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: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
+; 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 %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Ray + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -3326,12 +3347,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 1, binding = 2, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 1, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3361,10 +3382,10 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 1
+OpDecorate %sbo Binding 2
+OpDecorate %images DescriptorSet 1
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -3409,63 +3430,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 [[null_uint]] %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 [[null_v4float]] %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
+; 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: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
+; 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 %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Ray + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -3479,12 +3501,12 @@
   // #extension GL_EXT_nonuniform_qualifier : require
   // #extension GL_NV_ray_tracing : require
   //
-  // layout(set = 0, binding = 0, std140) buffer StorageBuffer {
+  // layout(set = 1, binding = 2, std140) buffer StorageBuffer {
   //   uint index;
   //   float red;
   // } sbo;
   //
-  // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
+  // layout(set = 1, binding = 3, rgba32f) readonly uniform image2D images[];
   //
   // void main()
   // {
@@ -3514,10 +3536,10 @@
 OpMemberDecorate %StorageBuffer 0 Offset 0
 OpMemberDecorate %StorageBuffer 1 Offset 4
 OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
+OpDecorate %sbo DescriptorSet 1
+OpDecorate %sbo Binding 2
+OpDecorate %images DescriptorSet 1
+OpDecorate %images Binding 3
 OpDecorate %images NonWritable
 ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + kOutputDecorations + R"(
@@ -3561,63 +3583,64 @@
 %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
 OpStore %31 %29
 ; CHECK-NOT: OpStore %31 %29
-; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %134 = OpULessThan %bool %uint_0 %133
-; CHECK: OpSelectionMerge %135 None
-; CHECK: OpBranchConditional %134 %136 %137
-; CHECK: %136 = OpLabel
-; CHECK: %138 = OpLoad %uint %25
-; CHECK: OpBranch %135
-; CHECK: %137 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %135
-; CHECK: %135 = OpLabel
-; CHECK: %142 = OpPhi %uint %138 %136 [[null_uint]] %137
-; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; 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: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
-; CHECK: %50 = OpULessThan %bool %142 %48
-; CHECK: OpSelectionMerge %51 None
-; CHECK: OpBranchConditional %50 %52 %53
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpLoad %13 %27
-; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
-; CHECK: %144 = OpULessThan %bool %uint_0 %143
-; CHECK: OpSelectionMerge %145 None
-; CHECK: OpBranchConditional %144 %146 %147
-; CHECK: %146 = OpLabel
-; CHECK: %148 = OpLoad %13 %27
-; CHECK: %149 = OpImageRead %v4float %148 %20
-; CHECK: OpBranch %145
-; CHECK: %147 = OpLabel
-; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
-; CHECK: OpBranch %145
-; CHECK: %145 = OpLabel
-; CHECK: %151 = OpPhi %v4float %149 %146 [[null_v4float]] %147
-; CHECK: OpBranch %51
-; CHECK: %53 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %114 = OpPhi %v4float %151 %145 [[null_v4float]] %53
-; CHECK: %30 = OpCompositeExtract %float %114 0
+; 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: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %153 = OpULessThan %bool %uint_0 %152
-; CHECK: OpSelectionMerge %154 None
-; CHECK: OpBranchConditional %153 %155 %156
-; CHECK: %155 = OpLabel
+; 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 %154
-; CHECK: %156 = OpLabel
-; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %154
-; CHECK: %154 = OpLabel
+; 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
 OpReturn
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
+  const std::string new_funcs =
+      kReadBindingLength + kStreamWrite6Ray + kReadDescInit;
 
   // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -3637,13 +3660,13 @@
   // layout(location = 0) in vec2 inTexcoord;
   // layout(location = 0) out vec4 outColor;
   //
-  // layout(set = 0, binding = 0) uniform Uniforms {
+  // layout(set = 1, binding = 0) uniform Uniforms {
   //   vec2 var0;
   // } uniforms;
   //
-  // layout(set = 0, binding = 1) uniform sampler uniformSampler;
-  // layout(set = 0, binding = 2) uniform texture2D uniformTex;
-  // layout(set = 0, binding = 3) uniform texture2D uniformTexArr[8];
+  // layout(set = 1, binding = 1) uniform sampler uniformSampler;
+  // layout(set = 1, binding = 2) uniform texture2D uniformTex;
+  // layout(set = 1, binding = 3) uniform texture2D uniformTexArr[8];
   //
   // void main() {
   //   int index = 0;
@@ -3678,18 +3701,18 @@
 OpMemberName %Uniforms 0 "var0"
 OpName %uniforms "uniforms"
 OpName %outColor "outColor"
-OpDecorate %uniformTexArr DescriptorSet 0
+OpDecorate %uniformTexArr DescriptorSet 1
 OpDecorate %uniformTexArr Binding 3
 OpDecorate %19 NonUniformEXT
 OpDecorate %22 NonUniformEXT
-OpDecorate %uniformSampler DescriptorSet 0
+OpDecorate %uniformSampler DescriptorSet 1
 OpDecorate %uniformSampler Binding 1
 OpDecorate %inTexcoord Location 0
-OpDecorate %uniformTex DescriptorSet 0
+OpDecorate %uniformTex DescriptorSet 1
 OpDecorate %uniformTex Binding 2
 OpMemberDecorate %Uniforms 0 Offset 0
 OpDecorate %Uniforms Block
-OpDecorate %uniforms DescriptorSet 0
+OpDecorate %uniforms DescriptorSet 1
 OpDecorate %uniforms Binding 0
 OpDecorate %outColor Location 0
 ; CHECK: OpDecorate %63 NonUniform
@@ -3697,7 +3720,7 @@
 )" + kOutputDecorations + R"(
 ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
 )" + kInputDecorations + R"(
-; CHECK: OpDecorate %151 NonUniform
+; CHECK: OpDecorate [[desc_state_result:%\w+]] NonUniform
 %void = OpTypeVoid
 %3 = OpTypeFunction %void
 %int = OpTypeInt 32 1
@@ -3762,35 +3785,35 @@
 %50 = OpImageSampleImplicitLod %v4float %41 %49
 %51 = OpCompositeExtract %float %50 0
 ; CHECK-NOT: %51 = OpCompositeExtract %float %50 0
-; CHECK: %157 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-; CHECK: %158 = OpULessThan %bool %uint_0 %157
-; CHECK: OpSelectionMerge %159 None
-; CHECK: OpBranchConditional %158 %160 %161
-; CHECK: %160 = OpLabel
-; CHECK: %162 = OpLoad %v2float %47
-; CHECK: OpBranch %159
-; CHECK: %161 = OpLabel
-; CHECK: {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_87 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %159
-; CHECK: %159 = OpLabel
-; CHECK: %166 = OpPhi %v2float %162 %160 [[null_v2float]] %161
-; CHECK: %49 = OpFMul %v2float %42 %166
-; CHECK: %167 = OpSampledImage %27 %39 %40
-; CHECK: %168 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0
-; CHECK: %169 = OpULessThan %bool %uint_0 %168
-; CHECK: OpSelectionMerge %170 None
-; CHECK: OpBranchConditional %169 %171 %172
-; CHECK: %171 = OpLabel
-; CHECK: %173 = OpLoad %13 %uniformTex
-; CHECK: %174 = OpSampledImage %27 %173 %40
-; CHECK: %175 = OpImageSampleImplicitLod %v4float %174 %49
-; CHECK: OpBranch %170
-; CHECK: %172 = OpLabel
-; CHECK: %177 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_89 %uint_1 %uint_0 %uint_0
-; CHECK: OpBranch %170
-; CHECK: %170 = OpLabel
-; CHECK: %178 = OpPhi %v4float %175 %171 [[null_v4float]] %172
-; CHECK: %51 = OpCompositeExtract %float %178 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
 %54 = OpLoad %float %x
 %55 = OpLoad %float %y
@@ -3800,7 +3823,7 @@
 OpFunctionEnd
 )";
 
-  const std::string new_funcs = kStreamWrite4Frag + kDirectRead4;
+  const std::string new_funcs = kStreamWrite6Frag + kReadDescInit;
 
   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
@@ -3920,7 +3943,7 @@
  ;CHECK:        [[null_v2float:%\w+]] = OpConstantNull %v2float
      %MainPs = OpFunction %void None %3
           %5 = OpLabel
- ;CHECK: %140 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_1 %uint_0
+ ;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
@@ -3935,40 +3958,40 @@
          %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:        %141 = OpULessThan %bool %119 %140
- ;CHECK:               OpSelectionMerge %143 None
- ;CHECK:               OpBranchConditional %141 %144 %145
- ;CHECK:        %144 = OpLabel
- ;CHECK:        %146 = OpLoad %v2float %86
- ;CHECK:               OpBranch %143
- ;CHECK:        %145 = OpLabel
- ;CHECK:        {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_71 %uint_4 %uint_0 %119 %140
- ;CHECK:               OpBranch %143
- ;CHECK:        %143 = OpLabel
- ;CHECK:        %203 = OpPhi %v2float %146 %144 [[null_v2float]] %145
+ ;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: {{%\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:        %204 = OpIAdd %uint %uint_8 %uint_7
- ;CHECK:        %205 = OpULessThan %bool %204 %140
- ;CHECK:               OpSelectionMerge %206 None
- ;CHECK:               OpBranchConditional %205 %207 %208
- ;CHECK:        %207 = OpLabel
- ;CHECK:        %209 = OpLoad %v2float %89
- ;CHECK:               OpBranch %206
- ;CHECK:        %208 = OpLabel
- ;CHECK:        %211 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_75 %uint_4 %uint_0 %204 %140
- ;CHECK:               OpBranch %206
- ;CHECK:        %206 = OpLabel
- ;CHECK:        %212 = OpPhi %v2float %209 %207 [[null_v2float]] %208
+ ;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 %203 %143 %212 %206
+ ;CHECK: %115 = OpPhi %v2float {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
          %95 = OpFAdd %v2float %69 %115
          %96 = OpLoad %49 %g_tColor
          %97 = OpLoad %53 %g_sAniso
@@ -3977,7 +4000,7 @@
                OpStore %_entryPointOutput_vColor %100
                OpReturn
                OpFunctionEnd
-)" + kDirectRead3 + kStreamWrite5Frag;
+)" + kReadDescInit + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4107,35 +4130,35 @@
 ;CHECK:        [[null_v2float:%\w+]] = OpConstantNull %v2float
      %MainPs = OpFunction %void None %3
           %5 = OpLabel
-;CHECK:        %123 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_2 %uint_0
-;CHECK:               OpBranch %93
-;CHECK:         %93 = OpLabel
-;CHECK:               OpBranch %92
-;CHECK:         %92 = 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:         %96 = OpIMul %uint %uint_80 %80
-;CHECK:         %97 = OpIAdd %uint %uint_0 %96
-;CHECK:         %99 = OpIAdd %uint %97 %uint_64
-;CHECK:        %101 = OpIAdd %uint %99 %uint_7
-;CHECK:        %125 = OpULessThan %bool %101 %123
-;CHECK:               OpSelectionMerge %127 None
-;CHECK:               OpBranchConditional %125 %128 %129
-;CHECK:        %128 = OpLabel
-;CHECK:        %130 = OpLoad %v2float %81
-;CHECK:               OpBranch %127
-;CHECK:        %129 = OpLabel
-;CHECK:        {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %123
-;CHECK:               OpBranch %127
-;CHECK:        %127 = OpLabel
-;CHECK:        %186 = OpPhi %v2float %130 %128 [[null_v2float]] %129
+;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: 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 %186
+;CHECK:             %86 = OpFAdd %v2float %66 {{%\w+}}
          %87 = OpLoad %46 %g_tColor
          %88 = OpLoad %50 %g_sAniso
          %89 = OpSampledImage %54 %87 %88
@@ -4143,7 +4166,7 @@
                OpStore %_entryPointOutput_vColor %91
                OpReturn
                OpFunctionEnd
-)" + kDirectRead3 + kStreamWrite5Frag;
+)" + kReadDescInit + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4238,19 +4261,19 @@
 %_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
-;CHECK:        [[null_v4float:%\w+]] = OpConstantNull %v4float
+;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:        %126 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0
-;CHECK:        %191 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
-;CHECK:               OpBranch %93
-;CHECK:         %93 = OpLabel
-;CHECK:               OpBranch %92
-;CHECK:         %92 = 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
@@ -4259,22 +4282,22 @@
          %86 = OpFAdd %v2float %66 %82
 ;CHECK-NOT: %82 = OpLoad %v2float %81
 ;CHECK-NOT: %86 = OpFAdd %v2float %66 %82
-;CHECK:         %96 = OpIMul %uint %uint_80 %80
-;CHECK:         %97 = OpIAdd %uint %uint_0 %96
-;CHECK:         %99 = OpIAdd %uint %97 %uint_64
-;CHECK:        %101 = OpIAdd %uint %99 %uint_7
-;CHECK:        %128 = OpULessThan %bool %101 %126
-;CHECK:               OpSelectionMerge %130 None
-;CHECK:               OpBranchConditional %128 %131 %132
-;CHECK:        %131 = OpLabel
-;CHECK:        %133 = OpLoad %v2float %81
-;CHECK:               OpBranch %130
-;CHECK:        %132 = OpLabel
-;CHECK:        {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %126
-;CHECK:               OpBranch %130
-;CHECK:        %130 = OpLabel
-;CHECK:        %190 = OpPhi %v2float %133 %131 [[null_v2float]] %132
-;CHECK:         %86 = OpFAdd %v2float %66 %190
+;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: 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
@@ -4282,23 +4305,23 @@
                OpStore %_entryPointOutput_vColor %91
 ;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86
 ;CHECK-NOT:       OpStore %_entryPointOutput_vColor %91
-;CHECK:        %192 = OpULessThan %bool %uint_0 %191
-;CHECK:               OpSelectionMerge %193 None
-;CHECK:               OpBranchConditional %192 %194 %195
-;CHECK:        %194 = OpLabel
-;CHECK:        %196 = OpLoad %46 %g_tColor
-;CHECK:        %197 = OpSampledImage %54 %196 %88
-;CHECK:        %198 = OpImageSampleImplicitLod %v4float %197 %86
-;CHECK:               OpBranch %193
-;CHECK:        %195 = OpLabel
-;CHECK:        {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0
-;CHECK:               OpBranch %193
-;CHECK:        %193 = OpLabel
-;CHECK:        %202 = OpPhi %v4float %198 %194 [[null_v4float]] %195
-;CHECK:               OpStore %_entryPointOutput_vColor %202
+;CHECK: {{%\w+}} = OpULessThan %bool %uint_0 {{%\w+}}
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+;CHECK: {{%\w+}} = OpLabel
+;CHECK: {{%\w+}} = OpLoad %46 %g_tColor
+;CHECK: {{%\w+}} = OpSampledImage %54 {{%\w+}} %88
+;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
-)" + kDirectRead4 + kStreamWrite5Frag;
+)" + kReadDescInit + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4333,12 +4356,12 @@
                OpName %g_sAniso "g_sAniso"
                OpName %i_vTextureCoords "i.vTextureCoords"
                OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-               OpDecorate %g_tColor DescriptorSet 0
-               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_tColor DescriptorSet 1
+               OpDecorate %g_tColor Binding 2
                OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
                OpDecorate %PerViewConstantBuffer_t Block
-               OpDecorate %g_sAniso DescriptorSet 0
-               OpDecorate %g_sAniso Binding 0
+               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
@@ -4379,8 +4402,8 @@
 )" + kInputGlobals + R"(
      %MainPs = OpFunction %void None %10
          %30 = OpLabel
-;CHECK:               OpBranch %108
-;CHECK:        %108 = OpLabel
+;CHECK:               OpBranch {{%\w+}}
+;CHECK:        {{%\w+}} = OpLabel
 ;CHECK:               OpBranch %39
 ;CHECK:         %39 = OpLabel
          %31 = OpLoad %v2float %i_vTextureCoords
@@ -4395,38 +4418,38 @@
 ;CHECK-NOT:         %38 = OpImageSampleImplicitLod %v4float %37 %31
 ;CHECK-NOT:               OpStore %_entryPointOutput_vColor %38
 ;CHECK:         %41 = OpUConvert %uint %33
-;CHECK:         %43 = OpULessThan %bool %41 %uint_128
-;CHECK:               OpSelectionMerge %44 None
-;CHECK:               OpBranchConditional %43 %45 %46
-;CHECK:         %45 = OpLabel
-;CHECK:         %47 = OpLoad %16 %34
-;CHECK:         %48 = OpSampledImage %27 %47 %36
-;CHECK:        %109 = OpUConvert %uint %33
-;CHECK:        %131 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %109
-;CHECK:        %132 = OpULessThan %bool %uint_0 %131
-;CHECK:               OpSelectionMerge %133 None
-;CHECK:               OpBranchConditional %132 %134 %135
-;CHECK:        %134 = OpLabel
-;CHECK:        %136 = OpLoad %16 %34
-;CHECK:        %137 = OpSampledImage %27 %136 %36
-;CHECK:        %138 = OpImageSampleImplicitLod %v4float %137 %31
-;CHECK:               OpBranch %133
-;CHECK:        %135 = OpLabel
-;CHECK:        %139 = OpUConvert %uint %33
-;CHECK:        %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_1 %139 %uint_0
-;CHECK:               OpBranch %133
-;CHECK:        %133 = OpLabel
-;CHECK:        [[phi_result_1:%\w+]] = OpPhi %v4float %138 %134 [[null_v4float]] %135
+;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_4 %uint_60 %uint_0 %41 %uint_128
+;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]] %133 [[null_v4float]] %46
+;CHECK:        [[phi_result_2:%\w+]] = OpPhi %v4float [[phi_result_1]] {{%\w+}} [[null_v4float]] %46
 ;CHECK:               OpStore %_entryPointOutput_vColor [[phi_result_2]]
                OpReturn
                OpFunctionEnd
-)" + kStreamWrite4Frag + kDirectRead4;
+)" + kStreamWrite6Frag + kReadDescInit;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4559,11 +4582,11 @@
 ;CHECK:        [[null_v2float:%\w+]] = OpConstantNull %v2float
      %MainPs = OpFunction %void None %14
          %37 = OpLabel
-;CHECK:         %79 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %49
-;CHECK:         %49 = OpLabel
-;CHECK:               OpBranch %48
-;CHECK:         %48 = 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
@@ -4572,23 +4595,23 @@
          %43 = OpFAdd %v2float %38 %42
 ;CHECK-NOT:     %42 = OpLoad %v2float %41
 ;CHECK-NOT:     %43 = OpFAdd %v2float %38 %42
-;CHECK:         %52 = OpUConvert %uint %40
-;CHECK:         %53 = OpIMul %uint %uint_80 %52
-;CHECK:         %54 = OpIAdd %uint %uint_0 %53
-;CHECK:         %56 = OpIAdd %uint %54 %uint_64
-;CHECK:         %58 = OpIAdd %uint %56 %uint_7
-;CHECK:         %81 = OpULessThan %bool %58 %79
-;CHECK:               OpSelectionMerge %83 None
-;CHECK:               OpBranchConditional %81 %84 %85
-;CHECK:         %84 = OpLabel
-;CHECK:         %86 = OpLoad %v2float %41
-;CHECK:               OpBranch %83
-;CHECK:         %85 = OpLabel
-;CHECK:        {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_81 %uint_4 %uint_0 %58 %79
-;CHECK:               OpBranch %83
-;CHECK:         %83 = OpLabel
-;CHECK:        %143 = OpPhi %v2float %86 %84 [[null_v2float]] %85
-;CHECK:         %43 = OpFAdd %v2float %38 %143
+;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: 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
@@ -4596,7 +4619,7 @@
                OpStore %_entryPointOutput_vColor %47
                OpReturn
                OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Frag;
+               )" + kReadDescInit + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4651,14 +4674,15 @@
                OpDecorate %_ Binding 0
                OpDecorate %21 RelaxedPrecision
 ;CHECK-NOT:           OpDecorate %21 RelaxedPrecision
-;CHECK:               OpDecorate %116 RelaxedPrecision
+;CHECK: OpDecorate %v_vtxResult RelaxedPrecision
+;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision
                OpDecorate %a_position Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
+;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + R"(
-;CHECK:               OpDecorate %61 RelaxedPrecision
+;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision
 )" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-;CHECK:               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -4685,36 +4709,36 @@
 ;CHECK: [[null_float:%\w+]] = OpConstantNull %float
        %main = OpFunction %void None %3
           %5 = OpLabel
-;CHECK:         %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %26
-;CHECK:         %26 = OpLabel
-;CHECK:               OpBranch %25
-;CHECK:         %25 = 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
          %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1
          %21 = OpLoad %float %20
 ;CHECK-NOT:     %21 = OpLoad %float %20
-;CHECK:         %30 = OpIMul %uint %uint_4 %int_2
-;CHECK:         %31 = OpIAdd %uint %uint_0 %30
-;CHECK:         %32 = OpIMul %uint %uint_16 %uint_1
-;CHECK:         %33 = OpIAdd %uint %31 %32
-;CHECK:         %35 = OpIAdd %uint %33 %uint_3
-;CHECK:         %57 = OpULessThan %bool %35 %55
-;CHECK:               OpSelectionMerge %58 None
-;CHECK:               OpBranchConditional %57 %59 %60
-;CHECK:         %59 = OpLabel
-;CHECK:         %61 = OpLoad %float %20
-;CHECK:               OpBranch %58
-;CHECK:         %60 = OpLabel
-;CHECK:        {{\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55
-;CHECK:               OpBranch %58
-;CHECK:         %58 = OpLabel
-;CHECK:        %116 = OpPhi %float %61 %59 [[null_float]] %60
+;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: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\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 %116
+;CHECK-NOT:    OpStore %v_vtxResult %21
+;CHECK:  OpStore %v_vtxResult [[phi_result]]
                OpReturn
                OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Vert;
+               )" + kReadDescInit + kStreamWrite7Vert;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4768,15 +4792,16 @@
                OpDecorate %_ DescriptorSet 0
                OpDecorate %_ Binding 0
                OpDecorate %21 RelaxedPrecision
-;CHECK-NOT:           OpDecorate %21 RelaxedPrecision
-;CHECK:               OpDecorate %115 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
+;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + R"(
-;CHECK:               OpDecorate %61 RelaxedPrecision
+;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision
 )" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-;CHECK:               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -4803,36 +4828,36 @@
 ;CHECK: [[null_float:%\w+]] = OpConstantNull %float
 %main = OpFunction %void None %3
           %5 = OpLabel
-;CHECK:         %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %26
-;CHECK:         %26 = OpLabel
-;CHECK:               OpBranch %25
-;CHECK:         %25 = 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:         %57 = OpULessThan %bool %35 %55
-;CHECK:               OpSelectionMerge %58 None
-;CHECK:               OpBranchConditional %57 %59 %60
-;CHECK:         %59 = OpLabel
-;CHECK:         %61 = OpLoad %float %20
-;CHECK:               OpBranch %58
-;CHECK:         %60 = OpLabel
-;CHECK:    {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55
-;CHECK:               OpBranch %58
-;CHECK:         %58 = OpLabel
-;CHECK:        %115 = OpPhi %float %61 %59 [[null_float]] %60
+;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]]
+;CHECK: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\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
-;CHECK:               OpStore %v_vtxResult %115
+;CHECK: OpStore %v_vtxResult [[phi_result]]
                OpReturn
                OpFunctionEnd
-               )" + kDirectRead3 + kStreamWrite5Vert;
+               )" + kReadDescInit + kStreamWrite7Vert;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -4851,7 +4876,7 @@
   // layout(location = 0) in highp vec4 a_position;
   // layout(location = 0) out highp vec2 v_vtxResult;
   //
-  // layout(set = 0, binding = 0, std430, row_major) uniform Block
+  // layout(set = 3, binding = 7, std430, row_major) uniform Block
   // {
   //    lowp mat2 var[3][4];
   // };
@@ -4885,18 +4910,18 @@
                OpMemberDecorate %Block 0 Offset 0
                OpMemberDecorate %Block 0 MatrixStride 16
                OpDecorate %Block Block
-               OpDecorate %_ DescriptorSet 0
-               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 3
+               OpDecorate %_ Binding 7
                OpDecorate %26 RelaxedPrecision
 ;CHECK-NOT:               OpDecorate %26 RelaxedPrecision
-;CHECK:               OpDecorate %125 RelaxedPrecision
+;CHECK: OpDecorate [[phi_result:%\w+]] RelaxedPrecision
                OpDecorate %a_position Location 0
-;CHECK:               OpDecorate %_runtimearr_uint ArrayStride 4
+;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
 )" + kInputDecorations + R"(
-;CHECK:               OpDecorate %70 RelaxedPrecision
+;CHECK: OpDecorate [[load_result:%\w+]] RelaxedPrecision
 )" + kOutputDecorations + R"(
-;CHECK:               OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-;CHECK:               OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -4928,38 +4953,38 @@
 ;CHECK: [[null_v2float:%\w+]] = OpConstantNull %v2float
        %main = OpFunction %void None %3
           %5 = OpLabel
-;CHECK:         %64 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK:               OpBranch %31
-;CHECK:         %31 = OpLabel
-;CHECK:               OpBranch %30
-;CHECK:         %30 = 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
-;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:         %66 = OpULessThan %bool %43 %64
-;CHECK:               OpSelectionMerge %67 None
-;CHECK:               OpBranchConditional %66 %68 %69
-;CHECK:         %68 = OpLabel
-;CHECK:         %70 = OpLoad %v2float %25
-;CHECK:               OpBranch %67
-;CHECK:         %69 = OpLabel
-;CHECK:        {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_51 %uint_4 %uint_0 %43 %64
-;CHECK:               OpBranch %67
-;CHECK:         %67 = OpLabel
-;CHECK:        %125 = OpPhi %v2float %70 %68 [[null_v2float]] %69
-;CHECK:               OpStore %v_vtxResult %125
+;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: OpSelectionMerge {{%\w+}} None
+;CHECK: OpBranchConditional {{%\w+}} {{%\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
-               )" + kDirectRead3 + kStreamWrite5Vert;
+               )" + kReadDescInit + kStreamWrite7Vert;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -5048,14 +5073,14 @@
 ;CHECK:             %33 = OpImageRead %v4float %32 %17
 ;CHECK:                   OpBranch %29
 ;CHECK:             %31 = OpLabel
-;CHECK:             {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_33 %uint_7 %uint_0 %23 %25
+;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:             %94 = OpPhi %v4float %33 %30 [[null_v4float]] %31
-;CHECK:                   OpStore %x %94
+;CHECK:             [[phi_result:%\w+]] = OpPhi %v4float %33 %30 [[null_v4float]] %31
+;CHECK:                   OpStore %x [[phi_result]]
                           OpReturn
                           OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+                          )" + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -5145,12 +5170,12 @@
 ;CHECK:                   OpImageWrite %32 %14 %18
 ;CHECK:                   OpBranch %29
 ;CHECK:             %31 = OpLabel
-;CHECK:             %91 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_7 %uint_0 %23 %25
+;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
-                          )" + kStreamWrite5Frag;
+                          )" + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -5239,14 +5264,14 @@
 ;CHECK:             %33 = OpImageFetch %v4float %32 %17
 ;CHECK:                   OpBranch %29
 ;CHECK:             %31 = OpLabel
-;CHECK:             {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_32 %uint_6 %uint_0 %23 %25
+;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:             %95 = OpPhi %v4float %33 %30 [[null_v4float]] %31
-;CHECK:                   OpStore %x %95
+;CHECK:             [[phi_result:%\w+]] = OpPhi %v4float %33 %30 [[null_v4float]] %31
+;CHECK:                   OpStore %x [[phi_result]]
                           OpReturn
                           OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+                          )" + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -5338,14 +5363,14 @@
 ;CHECK:             %36 = OpImageFetch %v4float %35 %18
 ;CHECK:                   OpBranch %31
 ;CHECK:             %33 = OpLabel
-;CHECK:             {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_6 %uint_0 %25 %27
+;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:             %98 = OpPhi %v4float %36 %32 [[null_v4float]] %33
-;CHECK:                   OpStore %x %98
+;CHECK:             [[phi_result:%\w+]] = OpPhi %v4float %36 %32 [[null_v4float]] %33
+;CHECK:                   OpStore %x [[phi_result]]
                           OpReturn
                           OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+                          )" + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -5447,14 +5472,14 @@
 ;CHECK:             %42 = OpImageFetch %v4float %41 %23
 ;CHECK:                   OpBranch %36
 ;CHECK:             %38 = OpLabel
-;CHECK:            {{%\w+}} = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_42 %uint_6 %uint_0 %30 %32
+;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:            %104 = OpPhi %v4float %42 %37 [[null_v4float]] %38
-;CHECK:                   OpStore %x %104
+;CHECK:            [[phi_result:%\w+]] = OpPhi %v4float %42 %37 [[null_v4float]] %38
+;CHECK:                   OpStore %x [[phi_result]]
                           OpReturn
                           OpFunctionEnd
-                          )" + kStreamWrite5Frag;
+                          )" + kStreamWrite7Frag;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
@@ -5532,13 +5557,13 @@
 R"(%main = OpFunction %void None %3
 %5 = OpLabel
 %i = OpVariable %_ptr_Function_int Function
-;CHECK: OpBranch %137
-;CHECK: %137 = OpLabel
-;CHECK: 65 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
-;CHECK: OpBranch %40
-;CHECK: 40 = OpLabel
-;CHECK: OpBranch %39
-;CHECK: 39 = OpLabel
+;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
@@ -5551,41 +5576,41 @@
 %29 = OpSLessThan %bool %15 %27
 ;CHECK-NOT: %27 = OpLoad %int %26
 ;CHECK-NOT: %29 = OpSLessThan %bool %15 %27
-;CHECK: 43 = OpIAdd %uint %uint_8 %uint_3
-;CHECK: 66 = OpULessThan %bool %43 %65
-;CHECK: OpSelectionMerge %67 None
-;CHECK: OpBranchConditional %66 %68 %69
-;CHECK: 68 = OpLabel
-;CHECK: 70 = OpLoad %int %26
-;CHECK: OpBranch %67
-;CHECK: 69 = OpLabel
-;CHECK: 122 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_54 %uint_4 %uint_0 %43 %65
-;CHECK: OpBranch %67
-;CHECK: 67 = OpLabel
-;CHECK: 124 = OpPhi %int %70 %68 %123 %69
-;CHECK: 29 = OpSLessThan %bool %15 %124
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_8 %uint_3
+;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\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]]
 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: 125 = OpIAdd %uint %uint_0 %uint_7
-;CHECK: 126 = OpULessThan %bool %125 %65
-;CHECK: OpSelectionMerge %127 None
-;CHECK: OpBranchConditional %126 %128 %129
-;CHECK: 128 = OpLabel
-;CHECK: 130 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
-;CHECK: OpBranch %127
-;CHECK: 129 = OpLabel
-;CHECK: 132 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_59 %uint_4 %uint_0 %125 %65
-;CHECK: 135 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_bufStruct %134
-;CHECK: OpBranch %127
-;CHECK: 127 = OpLabel
-;CHECK: 136 = OpPhi %_ptr_PhysicalStorageBuffer_bufStruct %130 %128 %135 %129
+;CHECK: {{%\w+}} = OpIAdd %uint %uint_0 %uint_7
+;CHECK: {{%\w+}} = OpULessThan %bool {{%\w+}} {{%\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
+;CHECK: [[phi_result_2:%\w+]] = OpPhi %_ptr_PhysicalStorageBuffer_bufStruct [[load_result_2]] {{%\w+}} {{%\w+}} {{%\w+}}
 %33 = OpLoad %int %i
 %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
 ;CHECK-NOT: %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
-;CHECK: %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %136 %int_0 %33
+;CHECK: %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int [[phi_result_2]] %int_0 %33
 OpStore %36 %int_n559035791 Aligned 16
 OpBranch %13
 %13 = OpLabel
@@ -5596,7 +5621,7 @@
 %12 = OpLabel
 OpReturn
 OpFunctionEnd)"
-    + kDirectRead3 + kStreamWrite5Vert;
+    + kReadDescInit + kStreamWrite7Vert;
   // clang-format on
 
   SetTargetEnv(SPV_ENV_VULKAN_1_2);
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index 4a56f60..80e6b7a 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -19,7 +19,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp
index 4031480..7dd10d8 100644
--- a/test/opt/inst_debug_printf_test.cpp
+++ b/test/opt/inst_debug_printf_test.cpp
@@ -18,7 +18,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp
index 6ea7fcc..67961eb 100644
--- a/test/opt/instruction_test.cpp
+++ b/test/opt/instruction_test.cpp
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/instruction.h"
+
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/interface_var_sroa_test.cpp b/test/opt/interface_var_sroa_test.cpp
index 7762458..6f51b08 100644
--- a/test/opt/interface_var_sroa_test.cpp
+++ b/test/opt/interface_var_sroa_test.cpp
@@ -14,8 +14,6 @@
 
 #include <iostream>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/ir_builder.cpp b/test/opt/ir_builder.cpp
index e04e781..f0cfc18 100644
--- a/test/opt/ir_builder.cpp
+++ b/test/opt/ir_builder.cpp
@@ -12,18 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
+#include "source/opt/ir_builder.h"
+
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/basic_block.h"
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/type_manager.h"
 #include "spirv-tools/libspirv.hpp"
 
diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp
index 86a3f45..1acbefe 100644
--- a/test/opt/ir_context_test.cpp
+++ b/test/opt/ir_context_test.cpp
@@ -16,7 +16,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <string>
 #include <utility>
 
 #include "OpenCLDebugInfo100.h"
@@ -1149,6 +1148,91 @@
             20);
 }
 
+struct TargetEnvCompareTestData {
+  spv_target_env later_env, earlier_env;
+};
+
+using TargetEnvCompareTest = ::testing::TestWithParam<TargetEnvCompareTestData>;
+
+TEST_P(TargetEnvCompareTest, Case) {
+  // If new environments are added, then we must update the list of tests.
+  ASSERT_EQ(SPV_ENV_VULKAN_1_3 + 1, SPV_ENV_MAX);
+
+  const auto& tc = GetParam();
+
+  std::unique_ptr<Module> module(new Module());
+  IRContext localContext(tc.later_env, std::move(module),
+                         spvtools::MessageConsumer());
+  EXPECT_TRUE(localContext.IsTargetEnvAtLeast(tc.earlier_env));
+
+  if (tc.earlier_env != tc.later_env) {
+    std::unique_ptr<Module> module(new Module());
+    IRContext localContext(tc.earlier_env, std::move(module),
+                           spvtools::MessageConsumer());
+    EXPECT_FALSE(localContext.IsTargetEnvAtLeast(tc.later_env));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TestCase, TargetEnvCompareTest,
+    ::testing::Values(
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_6},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_0},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_1},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_2},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_3},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_4},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_5},
+        TargetEnvCompareTestData{SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_6}));
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index 45104f4..769a25d 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/test/opt/ir_loader_test.cpp
@@ -14,7 +14,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <string>
 #include <unordered_set>
 #include <utility>
 #include <vector>
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index 07fb537..b35f3a3 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -1348,6 +1348,56 @@
                                                      true);
 }
 
+TEST_F(LocalAccessChainConvertTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+               OpSourceExtension "GL_GOOGLE_include_directive"
+               OpName %main "main"
+               OpName %a "a"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_float = OpTypePointer Function %float
+    %float_1 = OpConstant %float 1
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; Make sure the access chains were removed.
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[a]]
+; CHECK: [[ex:%\w+]] = OpCompositeExtract {{%\w+}} [[ld]] 0
+; CHECK: [[ld2:%\w+]] = OpLoad {{%\w+}} [[a]]
+; CHECK: [[v:%\w+]] = OpCompositeInsert {{%\w+}} [[ex]] [[ld2]] 0
+; CHECK: OpStore [[a]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_v4float Function
+         %13 = OpAccessChain %_ptr_Function_float %a %uint_0
+         %14 = OpLoad %float %13
+         %17 = OpAccessChain %_ptr_Function_float %a %uint_0
+               OpStore %17 %14
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalAccessChainConvertPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Assorted vector and matrix types
diff --git a/test/opt/local_redundancy_elimination_test.cpp b/test/opt/local_redundancy_elimination_test.cpp
index 291e1bc..01f7666 100644
--- a/test/opt/local_redundancy_elimination_test.cpp
+++ b/test/opt/local_redundancy_elimination_test.cpp
@@ -15,9 +15,7 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "source/opt/build_module.h"
 #include "source/opt/value_number_table.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp
index 28b8a07..7d19c22 100644
--- a/test/opt/local_single_block_elim.cpp
+++ b/test/opt/local_single_block_elim.cpp
@@ -1502,6 +1502,49 @@
   SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleBlockLoadStoreElimTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; CHECK-NEXT: [[b:%\w+]] = OpVariable
+; CHECK: OpStore [[a]] [[v:%\w+]]
+; CHECK-NOT: OpLoad %int [[a]]
+; CHECK: OpStore [[b]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function
+          %b = OpVariable %_ptr_Function_int Function
+               OpStore %a %int_0
+         %16 = OpLoad %int %a
+               OpStore %b %16
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other target variable types
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index 8f43a11..ffe352e 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -1750,6 +1750,58 @@
   SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleStoreElimTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; CHECK-NEXT: [[b:%\w+]] = OpVariable
+; CHECK: OpStore [[a]] [[v:%\w+]]
+; CHECK: OpStore [[b]]
+; Make sure the load was removed.
+; CHECK: OpLabel
+; CHECK-NOT: OpLoad %int [[a]]
+; CHECK: OpStore [[b]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function
+          %b = OpVariable %_ptr_Function_int Function
+               OpStore %a %int_0
+               OpStore %b %int_1
+               OpSelectionMerge %15 None
+               OpBranchConditional %false %14 %15
+         %14 = OpLabel
+         %16 = OpLoad %int %a
+               OpStore %b %16
+               OpBranch %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other types
diff --git a/test/opt/loop_optimizations/CMakeLists.txt b/test/opt/loop_optimizations/CMakeLists.txt
index e362078..6e20f72 100644
--- a/test/opt/loop_optimizations/CMakeLists.txt
+++ b/test/opt/loop_optimizations/CMakeLists.txt
@@ -21,6 +21,7 @@
        fusion_illegal.cpp
        fusion_legal.cpp
        fusion_pass.cpp
+       hoist_access_chains.cpp
        hoist_all_loop_types.cpp
        hoist_double_nested_loops.cpp
        hoist_from_independent_loops.cpp
diff --git a/test/opt/loop_optimizations/dependence_analysis.cpp b/test/opt/loop_optimizations/dependence_analysis.cpp
index 42d9acb..40520f5 100644
--- a/test/opt/loop_optimizations/dependence_analysis.cpp
+++ b/test/opt/loop_optimizations/dependence_analysis.cpp
@@ -14,17 +14,11 @@
 
 #include <memory>
 #include <set>
-#include <string>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
-#include "gmock/gmock.h"
-#include "source/opt/iterator.h"
 #include "source/opt/loop_dependence.h"
 #include "source/opt/loop_descriptor.h"
-#include "source/opt/pass.h"
-#include "source/opt/tree_iterator.h"
 #include "test/opt//assembly_builder.h"
 #include "test/opt//function_utils.h"
 #include "test/opt//pass_fixture.h"
diff --git a/test/opt/loop_optimizations/dependence_analysis_helpers.cpp b/test/opt/loop_optimizations/dependence_analysis_helpers.cpp
index aabf478..6206199 100644
--- a/test/opt/loop_optimizations/dependence_analysis_helpers.cpp
+++ b/test/opt/loop_optimizations/dependence_analysis_helpers.cpp
@@ -13,17 +13,11 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
-#include <unordered_set>
 #include <vector>
 
-#include "gmock/gmock.h"
-#include "source/opt/iterator.h"
 #include "source/opt/loop_dependence.h"
 #include "source/opt/loop_descriptor.h"
-#include "source/opt/pass.h"
 #include "source/opt/scalar_analysis.h"
-#include "source/opt/tree_iterator.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/function_utils.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/loop_optimizations/fusion_compatibility.cpp b/test/opt/loop_optimizations/fusion_compatibility.cpp
index cda8576..9acfe8f 100644
--- a/test/opt/loop_optimizations/fusion_compatibility.cpp
+++ b/test/opt/loop_optimizations/fusion_compatibility.cpp
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <iterator>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/fusion_illegal.cpp b/test/opt/loop_optimizations/fusion_illegal.cpp
index 26d5445..bff416b 100644
--- a/test/opt/loop_optimizations/fusion_illegal.cpp
+++ b/test/opt/loop_optimizations/fusion_illegal.cpp
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <iterator>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/fusion_legal.cpp b/test/opt/loop_optimizations/fusion_legal.cpp
index 56b0b76..ef7daee 100644
--- a/test/opt/loop_optimizations/fusion_legal.cpp
+++ b/test/opt/loop_optimizations/fusion_legal.cpp
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <iterator>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
diff --git a/test/opt/loop_optimizations/hoist_access_chains.cpp b/test/opt/loop_optimizations/hoist_access_chains.cpp
new file mode 100644
index 0000000..0c688b3
--- /dev/null
+++ b/test/opt/loop_optimizations/hoist_access_chains.cpp
@@ -0,0 +1,157 @@
+// Copyright (c) 2023 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/opt/licm_pass.h"
+#include "test/opt/pass_fixture.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using PassClassTest = PassTest<::testing::Test>;
+
+/*
+  Tests for the LICM pass to check it handles access chains correctly
+
+  Generated from the following GLSL fragment shader
+--eliminate-local-multi-store has also been run on the spv binary
+#version 460
+void main() {
+  for (uint i = 0; i < 123u; ++i) {
+    vec2 do_not_hoist_store = vec2(0.0f);
+    float do_not_hoist_access_chain_load = do_not_hoist_store.x;
+  }
+}
+*/
+
+TEST_F(PassClassTest, HoistAccessChains) {
+  const std::string before_hoist = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 460
+OpName %main "main"
+OpName %i "i"
+OpName %do_not_hoist_store "do_not_hoist_store"
+OpName %do_not_hoist_access_chain_load "do_not_hoist_access_chain_load"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_0 = OpConstant %uint 0
+%uint_123 = OpConstant %uint 123
+%bool = OpTypeBool
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%float_0 = OpConstant %float 0
+%17 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_float = OpTypePointer Function %float
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%main = OpFunction %void None %7
+%21 = OpLabel
+%i = OpVariable %_ptr_Function_uint Function
+%do_not_hoist_store = OpVariable %_ptr_Function_v2float Function
+%do_not_hoist_access_chain_load = OpVariable %_ptr_Function_float Function
+OpStore %i %uint_0
+OpBranch %22
+%22 = OpLabel
+OpLoopMerge %23 %24 None
+OpBranch %25
+%25 = OpLabel
+%26 = OpLoad %uint %i
+%27 = OpULessThan %bool %26 %uint_123
+OpBranchConditional %27 %28 %23
+%28 = OpLabel
+OpStore %do_not_hoist_store %17
+%29 = OpAccessChain %_ptr_Function_float %do_not_hoist_store %uint_0
+%30 = OpLoad %float %29
+OpStore %do_not_hoist_access_chain_load %30
+OpBranch %24
+%24 = OpLabel
+%31 = OpLoad %uint %i
+%32 = OpIAdd %uint %31 %int_1
+OpStore %i %32
+OpBranch %22
+%23 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after_hoist = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 460
+OpName %main "main"
+OpName %i "i"
+OpName %do_not_hoist_store "do_not_hoist_store"
+OpName %do_not_hoist_access_chain_load "do_not_hoist_access_chain_load"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%uint_0 = OpConstant %uint 0
+%uint_123 = OpConstant %uint 123
+%bool = OpTypeBool
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%float_0 = OpConstant %float 0
+%17 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_float = OpTypePointer Function %float
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%main = OpFunction %void None %7
+%21 = OpLabel
+%i = OpVariable %_ptr_Function_uint Function
+%do_not_hoist_store = OpVariable %_ptr_Function_v2float Function
+%do_not_hoist_access_chain_load = OpVariable %_ptr_Function_float Function
+OpStore %i %uint_0
+%29 = OpAccessChain %_ptr_Function_float %do_not_hoist_store %uint_0
+OpBranch %22
+%22 = OpLabel
+OpLoopMerge %23 %24 None
+OpBranch %25
+%25 = OpLabel
+%26 = OpLoad %uint %i
+%27 = OpULessThan %bool %26 %uint_123
+OpBranchConditional %27 %28 %23
+%28 = OpLabel
+OpStore %do_not_hoist_store %17
+%30 = OpLoad %float %29
+OpStore %do_not_hoist_access_chain_load %30
+OpBranch %24
+%24 = OpLabel
+%31 = OpLoad %uint %i
+%32 = OpIAdd %uint %31 %int_1
+OpStore %i %32
+OpBranch %22
+%23 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/loop_optimizations/lcssa.cpp b/test/opt/loop_optimizations/lcssa.cpp
index ace6ce1..32c2f72 100644
--- a/test/opt/loop_optimizations/lcssa.cpp
+++ b/test/opt/loop_optimizations/lcssa.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
@@ -21,7 +20,6 @@
 #include "source/opt/build_module.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/loop_utils.h"
-#include "source/opt/pass.h"
 #include "test/opt//assembly_builder.h"
 #include "test/opt/function_utils.h"
 
diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp
index b3f4f44..3dd0b93 100644
--- a/test/opt/loop_optimizations/loop_descriptions.cpp
+++ b/test/opt/loop_optimizations/loop_descriptions.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/loop_fission.cpp b/test/opt/loop_optimizations/loop_fission.cpp
index bc3ec39..41e40c3 100644
--- a/test/opt/loop_optimizations/loop_fission.cpp
+++ b/test/opt/loop_optimizations/loop_fission.cpp
@@ -12,15 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/loop_fission.h"
+
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/loop_fission.h"
-#include "source/opt/loop_unroller.h"
 #include "source/opt/loop_utils.h"
-#include "source/opt/pass.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/function_utils.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/loop_optimizations/peeling.cpp b/test/opt/loop_optimizations/peeling.cpp
index 4ff7a5a..34c3307 100644
--- a/test/opt/loop_optimizations/peeling.cpp
+++ b/test/opt/loop_optimizations/peeling.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "effcee/effcee.h"
diff --git a/test/opt/loop_optimizations/peeling_pass.cpp b/test/opt/loop_optimizations/peeling_pass.cpp
index 1b5a12d..ad7fcdc 100644
--- a/test/opt/loop_optimizations/peeling_pass.cpp
+++ b/test/opt/loop_optimizations/peeling_pass.cpp
@@ -17,7 +17,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/ir_builder.h"
 #include "source/opt/loop_descriptor.h"
 #include "source/opt/loop_peeling.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/loop_optimizations/unroll_assumptions.cpp b/test/opt/loop_optimizations/unroll_assumptions.cpp
index 159e4a1..81657a5 100644
--- a/test/opt/loop_optimizations/unroll_assumptions.cpp
+++ b/test/opt/loop_optimizations/unroll_assumptions.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index 299fb2d..6468adf 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp
index 33dc05f..a93a50b 100644
--- a/test/opt/module_test.cpp
+++ b/test/opt/module_test.cpp
@@ -12,16 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/module.h"
+
 #include <memory>
-#include <sstream>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
-#include "source/opt/module.h"
-#include "source/opt/pass.h"
 #include "spirv-tools/libspirv.hpp"
 #include "test/opt/module_utils.h"
 
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index 04bd5d9..494f2e9 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -14,9 +14,7 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "spirv-tools/libspirv.hpp"
-#include "spirv-tools/optimizer.hpp"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/pass_remove_duplicates_test.cpp b/test/opt/pass_remove_duplicates_test.cpp
index ac87db1..131a6b4 100644
--- a/test/opt/pass_remove_duplicates_test.cpp
+++ b/test/opt/pass_remove_duplicates_test.cpp
@@ -12,12 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/pass_manager.h"
diff --git a/test/opt/private_to_local_test.cpp b/test/opt/private_to_local_test.cpp
index 8b5ec59..f7c37c9 100644
--- a/test/opt/private_to_local_test.cpp
+++ b/test/opt/private_to_local_test.cpp
@@ -15,7 +15,6 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "source/opt/build_module.h"
 #include "source/opt/value_number_table.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/propagator_test.cpp b/test/opt/propagator_test.cpp
index 76211a5..307a2a1 100644
--- a/test/opt/propagator_test.cpp
+++ b/test/opt/propagator_test.cpp
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/propagator.h"
+
 #include <map>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -22,8 +23,6 @@
 #include "source/opt/build_module.h"
 #include "source/opt/cfg.h"
 #include "source/opt/ir_context.h"
-#include "source/opt/pass.h"
-#include "source/opt/propagator.h"
 
 namespace spvtools {
 namespace opt {
diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp
index 28eda73..eb78497 100644
--- a/test/opt/redundancy_elimination_test.cpp
+++ b/test/opt/redundancy_elimination_test.cpp
@@ -15,8 +15,6 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "source/opt/build_module.h"
-#include "source/opt/value_number_table.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/opt/register_liveness.cpp b/test/opt/register_liveness.cpp
index 7cb210f..3870e2f 100644
--- a/test/opt/register_liveness.cpp
+++ b/test/opt/register_liveness.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include <memory>
-#include <string>
 #include <unordered_set>
 #include <vector>
 
diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp
index b9cb0de..e486df3 100644
--- a/test/opt/relax_float_ops_test.cpp
+++ b/test/opt/relax_float_ops_test.cpp
@@ -18,7 +18,6 @@
 #include <string>
 #include <vector>
 
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp
index ddf027f..8bb40f7 100644
--- a/test/opt/remove_unused_interface_variables_test.cpp
+++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp
index 9ab9eb1..6018be2 100644
--- a/test/opt/replace_desc_array_access_using_var_index_test.cpp
+++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp
@@ -14,8 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/replace_invalid_opc_test.cpp b/test/opt/replace_invalid_opc_test.cpp
index 1be904b..aee0d6e 100644
--- a/test/opt/replace_invalid_opc_test.cpp
+++ b/test/opt/replace_invalid_opc_test.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <cstdarg>
 #include <string>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "pass_utils.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
@@ -404,6 +402,7 @@
             OpReturn
             OpFunctionEnd)";
 
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_2);
   auto result = SinglePassRunAndDisassemble<ReplaceInvalidOpcodePass>(
       text, /* skip_nop = */ true, /* do_validation = */ false);
   EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
@@ -432,9 +431,40 @@
             OpReturn
             OpFunctionEnd)";
 
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_2);
   SinglePassRunAndMatch<ReplaceInvalidOpcodePass>(text, false);
 }
 
+// Since version 1.3 OpControlBarriers are allowed is more shaders.
+// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpControlBarrier
+TEST_F(ReplaceInvalidOpcodeTest, BarrierDontReplaceV13) {
+  const std::string text = R"(
+            OpCapability Shader
+       %1 = OpExtInstImport "GLSL.std.450"
+            OpMemoryModel Logical GLSL450
+            OpEntryPoint Vertex %main "main"
+            OpExecutionMode %main LocalSize 1 1 1
+            OpSource GLSL 450
+            OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+            OpSourceExtension "GL_GOOGLE_include_directive"
+            OpName %main "main"
+    %void = OpTypeVoid
+       %3 = OpTypeFunction %void
+    %uint = OpTypeInt 32 0
+  %uint_2 = OpConstant %uint 2
+%uint_264 = OpConstant %uint 264
+    %main = OpFunction %void None %3
+       %5 = OpLabel
+            OpControlBarrier %uint_2 %uint_2 %uint_264
+            OpReturn
+            OpFunctionEnd)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_3);
+  auto result = SinglePassRunAndDisassemble<ReplaceInvalidOpcodePass>(
+      text, /* skip_nop = */ true, /* do_validation = */ false);
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
 TEST_F(ReplaceInvalidOpcodeTest, MessageTest) {
   const std::string text = R"(
                OpCapability Shader
diff --git a/test/opt/scalar_analysis.cpp b/test/opt/scalar_analysis.cpp
index 14f82af..4779658 100644
--- a/test/opt/scalar_analysis.cpp
+++ b/test/opt/scalar_analysis.cpp
@@ -12,17 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/scalar_analysis.h"
+
 #include <memory>
-#include <string>
-#include <unordered_set>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/opt/iterator.h"
-#include "source/opt/loop_descriptor.h"
 #include "source/opt/pass.h"
-#include "source/opt/scalar_analysis.h"
-#include "source/opt/tree_iterator.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/function_utils.h"
 #include "test/opt/pass_fixture.h"
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 0c97c80..0ba285b 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/test/opt/scalar_replacement_test.cpp
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/opt/scalar_replacement_pass.h"
-
 #include <string>
 
-#include "gmock/gmock.h"
+#include "source/opt/scalar_replacement_pass.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
@@ -2310,6 +2308,54 @@
   SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
 }
 
+TEST_F(ScalarReplacementTest, RestrictPointer) {
+  // This test makes sure that a variable with the restrict pointer decoration
+  // is replaced, and that the pointer is applied to the new variable.
+  const std::string text = R"(
+; CHECK: OpDecorate [[new_var:%\w+]] RestrictPointer
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct %int
+; CHECK: [[ptr_type:%\w+]] = OpTypePointer PhysicalStorageBuffer [[struct_type]]
+; CHECK: [[dup_struct_type:%\w+]] = OpTypeStruct %int
+; CHECK: {{%\w+}} = OpTypePointer PhysicalStorageBuffer [[dup_struct_type]]
+; CHECK: [[var_type:%\w+]] = OpTypePointer Function [[ptr_type]]
+; CHECK: [[new_var]] = OpVariable [[var_type]] Function
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpMemberDecorate %3 0 Offset 0
+               OpDecorate %3 Block
+               OpMemberDecorate %4 0 Offset 0
+               OpDecorate %4 Block
+               OpDecorate %5 RestrictPointer
+          %6 = OpTypeVoid
+          %7 = OpTypeFunction %6
+          %8 = OpTypeInt 32 1
+          %9 = OpConstant %8 0
+          %3 = OpTypeStruct %8
+         %10 = OpTypePointer PhysicalStorageBuffer %3
+         %11 = OpTypeStruct %10
+          %4 = OpTypeStruct %8
+         %12 = OpTypePointer PhysicalStorageBuffer %4
+         %13 = OpTypePointer Function %11
+         %14 = OpTypePointer Function %10
+         %15 = OpTypePointer Function %12
+         %16 = OpUndef %11
+          %2 = OpFunction %6 None %7
+         %17 = OpLabel
+          %5 = OpVariable %13 Function
+               OpStore %5 %16
+         %18 = OpAccessChain %14 %5 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp
index 7727f56..7fce289 100644
--- a/test/opt/simplification_test.cpp
+++ b/test/opt/simplification_test.cpp
@@ -16,7 +16,6 @@
 
 #include "gmock/gmock.h"
 #include "source/opt/simplification_pass.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 
 namespace spvtools {
diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp
index dbb889c..4328c39 100644
--- a/test/opt/spread_volatile_semantics_test.cpp
+++ b/test/opt/spread_volatile_semantics_test.cpp
@@ -14,7 +14,6 @@
 
 #include <string>
 
-#include "gmock/gmock.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/strength_reduction_test.cpp b/test/opt/strength_reduction_test.cpp
index 31d0503..a37c6c2 100644
--- a/test/opt/strength_reduction_test.cpp
+++ b/test/opt/strength_reduction_test.cpp
@@ -12,16 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <cstdarg>
-#include <iostream>
-#include <sstream>
 #include <string>
 #include <unordered_set>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/struct_cfg_analysis_test.cpp b/test/opt/struct_cfg_analysis_test.cpp
index e7031cb..9c72cee 100644
--- a/test/opt/struct_cfg_analysis_test.cpp
+++ b/test/opt/struct_cfg_analysis_test.cpp
@@ -17,7 +17,6 @@
 #include <string>
 
 #include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp
index bc80050..563eb74 100644
--- a/test/opt/type_manager_test.cpp
+++ b/test/opt/type_manager_test.cpp
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/opt/type_manager.h"
+
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -22,7 +23,6 @@
 #include "gtest/gtest.h"
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
-#include "source/opt/type_manager.h"
 #include "spirv-tools/libspirv.hpp"
 
 namespace spvtools {
diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp
index 4352b7c..4ceeb14 100644
--- a/test/opt/types_test.cpp
+++ b/test/opt/types_test.cpp
@@ -391,18 +391,13 @@
       case Type::kArray:
       case Type::kRuntimeArray:
       case Type::kStruct:
+      case Type::kPointer:
         expectation = false;
         break;
       default:
         break;
     }
-    EXPECT_EQ(t->IsUniqueType(false), expectation)
-        << "expected '" << t->str() << "' to be a "
-        << (expectation ? "" : "non-") << "unique type";
-
-    // Allowing variables pointers.
-    if (t->AsPointer()) expectation = false;
-    EXPECT_EQ(t->IsUniqueType(true), expectation)
+    EXPECT_EQ(t->IsUniqueType(), expectation)
         << "expected '" << t->str() << "' to be a "
         << (expectation ? "" : "non-") << "unique type";
   }
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index 2cd3c7d..d213b8b 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "assembly_builder.h"
-#include "gmock/gmock.h"
 #include "pass_fixture.h"
 #include "pass_utils.h"
 
diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp
index c760f98..3d7aaad 100644
--- a/test/opt/value_table_test.cpp
+++ b/test/opt/value_table_test.cpp
@@ -17,7 +17,6 @@
 #include "gmock/gmock.h"
 #include "source/opt/build_module.h"
 #include "source/opt/value_number_table.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 
 namespace spvtools {
diff --git a/test/opt/workaround1209_test.cpp b/test/opt/workaround1209_test.cpp
index 50d3c09..5b0146b 100644
--- a/test/opt/workaround1209_test.cpp
+++ b/test/opt/workaround1209_test.cpp
@@ -12,15 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <algorithm>
-#include <cstdarg>
-#include <iostream>
-#include <sstream>
 #include <string>
 #include <unordered_set>
 
-#include "gmock/gmock.h"
-#include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
diff --git a/test/opt/wrap_opkill_test.cpp b/test/opt/wrap_opkill_test.cpp
index e40d701..efc834c 100644
--- a/test/opt/wrap_opkill_test.cpp
+++ b/test/opt/wrap_opkill_test.cpp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 6780c7e..55f8466 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -1198,5 +1198,54 @@
                                  {1, 2, 3, 4, 5, 6})},
             })));
 
+// SPV_EXT_shader_tile_image
+
+INSTANTIATE_TEST_SUITE_P(
+    SPV_EXT_shader_tile_image, 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_EXT_shader_tile_image\"\n",
+             MakeInstruction(spv::Op::OpExtension,
+                             MakeVector("SPV_EXT_shader_tile_image"))},
+            {"OpCapability TileImageColorReadAccessEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::TileImageColorReadAccessEXT})},
+            {"OpCapability TileImageDepthReadAccessEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::TileImageDepthReadAccessEXT})},
+            {"OpCapability TileImageStencilReadAccessEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::TileImageStencilReadAccessEXT})},
+            {"OpExecutionMode %1 NonCoherentColorAttachmentReadEXT\n",
+             MakeInstruction(spv::Op::OpExecutionMode,
+                             {1, (uint32_t)spv::ExecutionMode::
+                                     NonCoherentColorAttachmentReadEXT})},
+            {"OpExecutionMode %1 NonCoherentDepthAttachmentReadEXT\n",
+             MakeInstruction(spv::Op::OpExecutionMode,
+                             {1, (uint32_t)spv::ExecutionMode::
+                                     NonCoherentDepthAttachmentReadEXT})},
+            {"OpExecutionMode %1 NonCoherentStencilAttachmentReadEXT\n",
+             MakeInstruction(spv::Op::OpExecutionMode,
+                             {1, (uint32_t)spv::ExecutionMode::
+                                     NonCoherentStencilAttachmentReadEXT})},
+            {"%2 = OpColorAttachmentReadEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpColorAttachmentReadEXT, {1, 2, 3})},
+            {"%2 = OpColorAttachmentReadEXT %1 %3 %4\n",
+             MakeInstruction(spv::Op::OpColorAttachmentReadEXT, {1, 2, 3, 4})},
+            {"%2 = OpDepthAttachmentReadEXT %1\n",
+             MakeInstruction(spv::Op::OpDepthAttachmentReadEXT, {1, 2})},
+            {"%2 = OpDepthAttachmentReadEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpDepthAttachmentReadEXT, {1, 2, 3})},
+            {"%2 = OpStencilAttachmentReadEXT %1\n",
+             MakeInstruction(spv::Op::OpStencilAttachmentReadEXT, {1, 2})},
+            {"%2 = OpStencilAttachmentReadEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpStencilAttachmentReadEXT, {1, 2, 3})},
+        })));
+
 }  // namespace
 }  // namespace spvtools
diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp
index 241600e..770f298 100644
--- a/test/text_to_binary.type_declaration_test.cpp
+++ b/test/text_to_binary.type_declaration_test.cpp
@@ -59,6 +59,7 @@
         CASE(Rect),
         CASE(Buffer),
         CASE(SubpassData),
+        CASE(TileImageDataEXT),
     }));
 #undef CASE
 // clang-format on
@@ -221,6 +222,7 @@
   CASE(AtomicCounter);
   CASE(Image);
   CASE(StorageBuffer);
+  CASE(TileImageEXT);
 }
 
 #undef CASE
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index 99f9780..4c8989f 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -18,4 +18,14 @@
 add_test(NAME spirv-tools_spirv_test_framework_unittests
          COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py
          WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_spvtools_unittest(
+  TARGET spirv_unit_test_tools_util
+  SRCS flags_test.cpp ${spirv-tools_SOURCE_DIR}/tools/util/flags.cpp
+  LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}
+  DEFINES TESTING=1)
+
 add_subdirectory(opt)
+if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Android"))
+  add_subdirectory(objdump)
+endif ()
diff --git a/test/tools/flags_test.cpp b/test/tools/flags_test.cpp
new file mode 100644
index 0000000..43db996
--- /dev/null
+++ b/test/tools/flags_test.cpp
@@ -0,0 +1,415 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tools/util/flags.h"
+
+#include "gmock/gmock.h"
+
+#ifdef UTIL_FLAGS_FLAG
+#undef UTIL_FLAGS_FLAG
+#define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort)     \
+  flags::Flag<Type> Name(Default);                                          \
+  flags::FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
+                                              IsShort)
+#else
+#error \
+    "UTIL_FLAGS_FLAG is not defined. Either flags.h is not included of the flag name changed."
+#endif
+
+class FlagTest : public ::testing::Test {
+ protected:
+  void SetUp() override { flags::FlagList::reset(); }
+};
+
+TEST_F(FlagTest, NoFlags) {
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, DashIsPositional) {
+  const char* argv[] = {"binary", "-", nullptr};
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "-");
+}
+
+TEST_F(FlagTest, Positional) {
+  const char* argv[] = {"binary", "A", "BCD", nullptr};
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(flags::positional_arguments.size(), 2);
+  EXPECT_EQ(flags::positional_arguments[0], "A");
+  EXPECT_EQ(flags::positional_arguments[1], "BCD");
+}
+
+TEST_F(FlagTest, MissingRequired) {
+  FLAG_SHORT_bool(g, false, true);
+
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_FALSE(flags::Parse(argv));
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanShortValue) {
+  FLAG_SHORT_bool(g, false, false);
+  const char* argv[] = {"binary", "-g", nullptr};
+  EXPECT_FALSE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(g.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanShortDefaultValue) {
+  FLAG_SHORT_bool(g, true, false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_TRUE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(g.value());
+}
+
+TEST_F(FlagTest, BooleanLongValueNotParsed) {
+  FLAG_SHORT_bool(g, false, false);
+  const char* argv[] = {"binary", "-g", "false", nullptr};
+  EXPECT_FALSE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(g.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "false");
+}
+
+TEST_F(FlagTest, BooleanLongSplitNotParsed) {
+  FLAG_LONG_bool(foo, false, false);
+  const char* argv[] = {"binary", "--foo", "true", nullptr};
+  EXPECT_FALSE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "true");
+}
+
+TEST_F(FlagTest, BooleanLongExplicitTrue) {
+  FLAG_LONG_bool(foo, false, false);
+  const char* argv[] = {"binary", "--foo=true", nullptr};
+  EXPECT_FALSE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanLongExplicitFalse) {
+  FLAG_LONG_bool(foo, false, false);
+  const char* argv[] = {"binary", "--foo=false", nullptr};
+  EXPECT_FALSE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_FALSE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanLongDefaultValue) {
+  FLAG_LONG_bool(foo, true, false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_TRUE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_TRUE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, BooleanLongDefaultValueCancelled) {
+  FLAG_LONG_bool(foo, true, false);
+  const char* argv[] = {"binary", "--foo=false", nullptr};
+  EXPECT_TRUE(foo.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_FALSE(foo.value());
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringFlagDefaultValue) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(f.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringFlagShortMissingString) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", "-f", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, StringFlagDefault) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(f.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringFlagSet) {
+  FLAG_SHORT_string(f, "default", false);
+  const char* argv[] = {"binary", "-f", "toto", nullptr};
+  EXPECT_EQ(f.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(f.value(), "toto");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringLongFlagSetSplit) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo", "toto", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "toto");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringLongFlagSetUnified) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo=toto", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "toto");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, StringLongFlagSetEmpty) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo=", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, AllPositionalAfterDoubleDash) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--", "--foo=toto", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 1);
+  EXPECT_EQ(flags::positional_arguments[0], "--foo=toto");
+}
+
+TEST_F(FlagTest, NothingAfterDoubleDash) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), "default");
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, FlagDoubleSetNotAllowed) {
+  FLAG_LONG_string(foo, "default", false);
+  const char* argv[] = {"binary", "--foo=abc", "--foo=def", nullptr};
+  EXPECT_EQ(foo.value(), "default");
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, MultipleFlags) {
+  FLAG_LONG_string(foo, "default foo", false);
+  FLAG_LONG_string(bar, "default_bar", false);
+  const char* argv[] = {"binary", "--foo", "abc", "--bar=def", nullptr};
+  EXPECT_EQ(foo.value(), "default foo");
+  EXPECT_EQ(bar.value(), "default_bar");
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), "abc");
+  EXPECT_EQ(bar.value(), "def");
+}
+
+TEST_F(FlagTest, MixedStringAndBool) {
+  FLAG_LONG_string(foo, "default foo", false);
+  FLAG_LONG_string(bar, "default_bar", false);
+  FLAG_SHORT_bool(g, false, false);
+  const char* argv[] = {"binary", "--foo", "abc", "-g", "--bar=def", nullptr};
+  EXPECT_EQ(foo.value(), "default foo");
+  EXPECT_EQ(bar.value(), "default_bar");
+  EXPECT_FALSE(g.value());
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), "abc");
+  EXPECT_EQ(bar.value(), "def");
+  EXPECT_TRUE(g.value());
+}
+
+TEST_F(FlagTest, UintFlagDefaultValue) {
+  FLAG_SHORT_uint(f, 18, false);
+  const char* argv[] = {"binary", nullptr};
+  EXPECT_EQ(f.value(), 18);
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(f.value(), 18);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintFlagShortMissingValue) {
+  FLAG_SHORT_uint(f, 19, false);
+  const char* argv[] = {"binary", "-f", nullptr};
+  EXPECT_EQ(f.value(), 19);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintFlagSet) {
+  FLAG_SHORT_uint(f, 20, false);
+  const char* argv[] = {"binary", "-f", "21", nullptr};
+  EXPECT_EQ(f.value(), 20);
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(f.value(), 21);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSetSplit) {
+  FLAG_LONG_uint(foo, 22, false);
+  const char* argv[] = {"binary", "--foo", "23", nullptr};
+  EXPECT_EQ(foo.value(), 22);
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), 23);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSetUnified) {
+  FLAG_LONG_uint(foo, 24, false);
+  const char* argv[] = {"binary", "--foo=25", nullptr};
+  EXPECT_EQ(foo.value(), 24);
+
+  EXPECT_TRUE(flags::Parse(argv));
+
+  EXPECT_EQ(foo.value(), 25);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSetEmptyIsWrong) {
+  FLAG_LONG_uint(foo, 26, false);
+  const char* argv[] = {"binary", "--foo=", nullptr};
+  EXPECT_EQ(foo.value(), 26);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetNegativeFails) {
+  FLAG_LONG_uint(foo, 26, false);
+  const char* argv[] = {"binary", "--foo=-2", nullptr};
+  EXPECT_EQ(foo.value(), 26);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetOverflowFails) {
+  FLAG_LONG_uint(foo, 27, false);
+  const char* argv[] = {
+      "binary", "--foo=99999999999999999999999999999999999999999999999999999",
+      nullptr};
+  EXPECT_EQ(foo.value(), 27);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetInvalidCharTrailing) {
+  FLAG_LONG_uint(foo, 28, false);
+  const char* argv[] = {"binary", "--foo=12A", nullptr};
+  EXPECT_EQ(foo.value(), 28);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSetSpaces) {
+  FLAG_LONG_uint(foo, 29, false);
+  const char* argv[] = {"binary", "--foo= 12", nullptr};
+  EXPECT_EQ(foo.value(), 29);
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), 12);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
+
+TEST_F(FlagTest, UintLongFlagSpacesOnly) {
+  FLAG_LONG_uint(foo, 30, false);
+  const char* argv[] = {"binary", "--foo=  ", nullptr};
+  EXPECT_EQ(foo.value(), 30);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagSplitNumber) {
+  FLAG_LONG_uint(foo, 31, false);
+  const char* argv[] = {"binary", "--foo= 2 2", nullptr};
+  EXPECT_EQ(foo.value(), 31);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagHex) {
+  FLAG_LONG_uint(foo, 32, false);
+  const char* argv[] = {"binary", "--foo=0xA", nullptr};
+  EXPECT_EQ(foo.value(), 32);
+
+  EXPECT_FALSE(flags::Parse(argv));
+}
+
+TEST_F(FlagTest, UintLongFlagZeros) {
+  FLAG_LONG_uint(foo, 33, false);
+  const char* argv[] = {"binary", "--foo=0000", nullptr};
+  EXPECT_EQ(foo.value(), 33);
+
+  EXPECT_TRUE(flags::Parse(argv));
+  EXPECT_EQ(foo.value(), 0);
+  EXPECT_EQ(flags::positional_arguments.size(), 0);
+}
diff --git a/test/tools/objdump/CMakeLists.txt b/test/tools/objdump/CMakeLists.txt
new file mode 100644
index 0000000..46fae21
--- /dev/null
+++ b/test/tools/objdump/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (c) 2023 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_spvtools_unittest(
+  TARGET spirv_unit_test_tools_objdump
+  SRCS
+    extract_source_test.cpp
+    ${spirv-tools_SOURCE_DIR}/tools/util/flags.cpp
+    ${spirv-tools_SOURCE_DIR}/tools/util/cli_consumer.cpp
+    ${spirv-tools_SOURCE_DIR}/tools/objdump/extract_source.cpp
+    LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} SPIRV-Tools-opt
+  DEFINES TESTING=1)
diff --git a/test/tools/objdump/extract_source_test.cpp b/test/tools/objdump/extract_source_test.cpp
new file mode 100644
index 0000000..0b81caa
--- /dev/null
+++ b/test/tools/objdump/extract_source_test.cpp
@@ -0,0 +1,265 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tools/objdump/extract_source.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "spirv-tools/libspirv.hpp"
+#include "tools/util/cli_consumer.h"
+
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+std::pair<bool, std::unordered_map<std::string, std::string>> ExtractSource(
+    const std::string& spv_source) {
+  std::unique_ptr<spvtools::opt::IRContext> ctx = spvtools::BuildModule(
+      kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, spv_source,
+      spvtools::SpirvTools::kDefaultAssembleOption |
+          SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  std::vector<uint32_t> binary;
+  ctx->module()->ToBinary(&binary, /* skip_nop = */ false);
+  std::unordered_map<std::string, std::string> output;
+  bool result = ExtractSourceFromModule(binary, &output);
+  return std::make_pair(result, std::move(output));
+}
+
+}  // namespace
+
+TEST(ExtractSourceTest, no_debug) {
+  std::string source = R"(
+           OpCapability Shader
+           OpCapability Linkage
+           OpMemoryModel Logical GLSL450
+   %void = OpTypeVoid
+      %2 = OpTypeFunction %void
+   %bool = OpTypeBool
+      %4 = OpUndef %bool
+      %5 = OpFunction %void None %2
+      %6 = OpLabel
+           OpReturn
+           OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 0);
+}
+
+TEST(ExtractSourceTest, SimpleSource) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute.hlsl"
+      OpSource HLSL 660 %2 "[numthreads(1, 1, 1)] void compute_1(){ }"
+      OpName %1 "compute_1"
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %1 = OpFunction %3 None %4
+ %5 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] ==
+              "[numthreads(1, 1, 1)] void compute_1(){ }");
+}
+
+TEST(ExtractSourceTest, SourceContinued) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute.hlsl"
+      OpSource HLSL 660 %2 "[numthreads(1, 1, 1)] "
+      OpSourceContinued "void compute_1(){ }"
+      OpName %1 "compute_1"
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %1 = OpFunction %3 None %4
+ %5 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] ==
+              "[numthreads(1, 1, 1)] void compute_1(){ }");
+}
+
+TEST(ExtractSourceTest, OnlyFilename) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute.hlsl"
+      OpSource HLSL 660 %2
+      OpName %1 "compute_1"
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %1 = OpFunction %3 None %4
+ %5 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] == "");
+}
+
+TEST(ExtractSourceTest, MultipleFiles) {
+  std::string source = R"(
+      OpCapability Shader
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %1 "compute_1"
+      OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpString "compute1.hlsl"
+ %3 = OpString "compute2.hlsl"
+      OpSource HLSL 660 %2 "some instruction"
+      OpSource HLSL 660 %3 "some other instruction"
+      OpName %1 "compute_1"
+ %4 = OpTypeVoid
+ %5 = OpTypeFunction %4
+ %1 = OpFunction %4 None %5
+ %6 = OpLabel
+      OpLine %2 1 41
+      OpReturn
+      OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 2);
+  ASSERT_TRUE(result["compute1.hlsl"] == "some instruction");
+  ASSERT_TRUE(result["compute2.hlsl"] == "some other instruction");
+}
+
+TEST(ExtractSourceTest, MultilineCode) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute_1"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString "compute.hlsl"
+               OpSource HLSL 660 %2 "[numthreads(1, 1, 1)]
+void compute_1() {
+}
+"
+               OpName %1 "compute_1"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 3 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] ==
+              "[numthreads(1, 1, 1)]\nvoid compute_1() {\n}\n");
+}
+
+TEST(ExtractSourceTest, EmptyFilename) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute_1"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString ""
+               OpSource HLSL 660 %2 "void compute(){}"
+               OpName %1 "compute_1"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 3 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["unnamed-0.hlsl"] == "void compute(){}");
+}
+
+TEST(ExtractSourceTest, EscapeEscaped) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString "compute.hlsl"
+               OpSource HLSL 660 %2 "// check \" escape removed"
+               OpName %1 "compute"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 6 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] == "// check \" escape removed");
+}
+
+TEST(ExtractSourceTest, OpSourceWithNoSource) {
+  std::string source = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "compute"
+               OpExecutionMode %1 LocalSize 1 1 1
+          %2 = OpString "compute.hlsl"
+               OpSource HLSL 660 %2
+               OpName %1 "compute"
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %1 = OpFunction %3 None %4
+          %5 = OpLabel
+               OpLine %2 6 1
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto[success, result] = ExtractSource(source);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(result.size() == 1);
+  ASSERT_TRUE(result["compute.hlsl"] == "");
+}
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp
index bb30de0..9f85a30 100644
--- a/test/val/val_annotation_test.cpp
+++ b/test/val/val_annotation_test.cpp
@@ -18,7 +18,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_code_generator.h"
 #include "test/val/val_fixtures.h"
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index c86cdc1..f160904 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -361,7 +361,7 @@
   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-SubgroupVoteKHR-06997"));
+              AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-07951"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -775,7 +775,7 @@
   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-SubgroupVoteKHR-06997"));
+              AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-07951"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index d876c48..3953057 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -16,14 +16,12 @@
 
 #include <array>
 #include <functional>
-#include <iterator>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/diagnostic.h"
 #include "source/spirv_target_env.h"
 #include "source/val/validate.h"
 #include "test/test_fixture.h"
@@ -3546,6 +3544,37 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
 }
 
+TEST_F(ValidateCFG, LoopConditionalBranchWithoutExitBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranchConditional %undef %then %else
+%then = OpLabel
+OpBranch %continue
+%else = OpLabel
+OpBranch %exit
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
 TEST_F(ValidateCFG, MissingMergeSwitchBad) {
   const std::string text = R"(
 OpCapability Shader
diff --git a/test/val/val_constants_test.cpp b/test/val/val_constants_test.cpp
index 301539d..4727877 100644
--- a/test/val/val_constants_test.cpp
+++ b/test/val/val_constants_test.cpp
@@ -478,6 +478,22 @@
                         "a null value"));
 }
 
+TEST_F(ValidateConstant, VectorMismatchedConstituents) {
+  std::string spirv = kShaderPreamble kBasicTypes R"(
+%int = OpTypeInt 32 1
+%int_0 = OpConstantNull %int
+%const_vector = OpConstantComposite %uint2 %uint_0 %int_0
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpConstantComposite Constituent <id> '13[%13]'s type "
+          "does not match Result Type <id> '3[%v2uint]'s vector element type"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 6a7f243..349e5e9 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -14,7 +14,6 @@
 
 // Validation tests for Data Rules.
 
-#include <sstream>
 #include <string>
 #include <utility>
 
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 4f90e6b..be16aba 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -19,7 +19,6 @@
 
 #include "gmock/gmock.h"
 #include "source/val/decoration.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_code_generator.h"
 #include "test/val/val_fixtures.h"
diff --git a/test/val/val_extension_spv_khr_bit_instructions_test.cpp b/test/val/val_extension_spv_khr_bit_instructions_test.cpp
index 0e92671..d23b9b6 100644
--- a/test/val/val_extension_spv_khr_bit_instructions_test.cpp
+++ b/test/val/val_extension_spv_khr_bit_instructions_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_expect_assume_test.cpp b/test/val/val_extension_spv_khr_expect_assume_test.cpp
index 6ece15d..85a484a 100644
--- a/test/val/val_extension_spv_khr_expect_assume_test.cpp
+++ b/test/val/val_extension_spv_khr_expect_assume_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
index e0e6896..5b3a309 100644
--- a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
+++ b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
@@ -14,15 +14,12 @@
 // limitations under the License.
 
 #include <ostream>
-#include <sstream>
 #include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
@@ -131,7 +128,7 @@
          %char_0 = OpConstant %char 0
          %char_1 = OpConstant %char 1
 
-         %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uint_0 %uchar_0 %uchar_0
+         %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uchar_0 %uchar_0 %uchar_0
          %v4uchar_1 = OpConstantComposite %v4uchar %uchar_1 %uchar_1 %uchar_1 %uchar_1
          %v4char_0 = OpConstantComposite %v4char %char_0 %char_0 %char_0 %char_0
          %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1
diff --git a/test/val/val_extension_spv_khr_linkonce_odr_test.cpp b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
index ac15558..ed3fb8a 100644
--- a/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
+++ b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
index f528cb9..80d5753 100644
--- a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
+++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extension_spv_khr_terminate_invocation_test.cpp b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
index 8d92414..4d3e4d6 100644
--- a/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
+++ b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
@@ -18,10 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_extensions_test.cpp b/test/val/val_extensions_test.cpp
index 491a808..0ab8c6e 100644
--- a/test/val/val_extensions_test.cpp
+++ b/test/val/val_extensions_test.cpp
@@ -18,10 +18,8 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_function_test.cpp b/test/val/val_function_test.cpp
index e7d5cd7..24b5263 100644
--- a/test/val/val_function_test.cpp
+++ b/test/val/val_function_test.cpp
@@ -12,12 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <sstream>
 #include <string>
 #include <tuple>
 
 #include "gmock/gmock.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index a97ef7c..aa335c8 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -356,7 +356,8 @@
 
 std::string GenerateKernelCode(
     const std::string& body,
-    const std::string& capabilities_and_extensions = "") {
+    const std::string& capabilities_and_extensions = "",
+    const std::string& declarations = "") {
   std::ostringstream ss;
   ss << R"(
 OpCapability Addresses
@@ -436,7 +437,11 @@
 %type_sampler = OpTypeSampler
 %ptr_sampler = OpTypePointer UniformConstant %type_sampler
 %uniform_sampler = OpVariable %ptr_sampler UniformConstant
+)";
 
+  ss << declarations;
+
+  ss << R"(
 %main = OpFunction %void None %func
 %main_entry = OpLabel
 )";
@@ -480,10 +485,10 @@
 OpCapability Float64
 )";
 
-  ss << capabilities_and_extensions;
   if (!include_entry_point) {
-    ss << "OpCapability Linkage";
+    ss << "OpCapability Linkage\n";
   }
+  ss << capabilities_and_extensions;
 
   ss << R"(
 OpMemoryModel Logical GLSL450
@@ -781,6 +786,279 @@
               HasSubstr("Dim SubpassData requires Arrayed to be 0"));
 }
 
+TEST_F(ValidateImage, TypeImageWrongSampledTypeForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %void TileImageDataEXT 0 0 0 2 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Dim TileImageDataEXT requires Sampled Type to be not OpTypeVoid"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongSampledForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 0 0 1 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires Sampled to be 2"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongFormatForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Rgba32f
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires format Unknown"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongDepthForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 1 0 0 2 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires Depth to be 0"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongArrayedForTileImageDataEXT) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 1 0 2 Unknown
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim TileImageDataEXT requires Arrayed to be 0"));
+}
+
+TEST_F(ValidateImage, TypeSampledImage_TileImageDataEXT_Error) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability TileImageColorReadAccessEXT\n"
+                               "OpExtension \"SPV_EXT_shader_tile_image\"\n",
+                               false) +
+                           R"(
+%img_type = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%simg_type = OpTypeSampledImage %img_type
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sampled image type requires an image type with "
+                        "\"Sampled\" operand set to 0 or 1"));
+}
+
+TEST_F(ValidateImage, ImageTexelPointerImageDimTileImageDataEXTBad) {
+  const std::string body = R"(
+%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %tile_image_u32_tid_0002 %u32_0 %u32_0
+%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1
+)";
+  const std::string decl = R"(
+%type_image_u32_tid_0002 = OpTypeImage %u32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_u32_tid_0002 = OpTypePointer TileImageEXT %type_image_u32_tid_0002
+%tile_image_u32_tid_0002 = OpVariable %ptr_image_u32_tid_0002 TileImageEXT
+)";
+
+  const std::string extra = R"(
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Dim TileImageDataEXT cannot be used with "
+                        "OpImageTexelPointer"));
+}
+
+TEST_F(ValidateImage, ReadTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageRead %f32vec4 %img %u32vec2_01
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Image Dim TileImageDataEXT cannot be used with ImageRead"));
+}
+
+TEST_F(ValidateImage, WriteTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+OpImageWrite %img %u32vec2_01 %f32vec4_0000
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image 'Dim' cannot be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, QueryFormatTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageQueryFormat %u32 %img
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, extra, decl).c_str());
+
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image 'Dim' cannot be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, QueryOrderTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageQueryOrder %u32 %img
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, extra, decl).c_str());
+
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image 'Dim' cannot be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, SparseFetchTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageSparseFetch %struct_u32_f32vec4 %img %u32vec2_01
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Image 'Sampled' parameter to be 1"));
+}
+
+TEST_F(ValidateImage, SparseReadTileImageDataEXT) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Image Dim TileImageDataEXT cannot be used with ImageSparseRead"));
+}
+
 TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) {
   const std::string code = GetKernelHeader() + R"(
 %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index 8cca96f..e809abf 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -14,9 +14,7 @@
 
 // Validation tests for Logical Layout
 
-#include <algorithm>
 #include <functional>
-#include <sstream>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -57,13 +55,6 @@
   bool inverse_;
 };
 
-template <typename... T>
-spv_result_t InvalidSet(int order) {
-  for (spv_result_t val : {T(true)(order)...})
-    if (val != SPV_SUCCESS) return val;
-  return SPV_SUCCESS;
-}
-
 // SPIRV source used to test the logical layout
 const std::vector<std::string>& getInstructions() {
   // clang-format off
diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp
index 364d514..66a6ff7 100644
--- a/test/val/val_limits_test.cpp
+++ b/test/val/val_limits_test.cpp
@@ -16,7 +16,6 @@
 
 #include <sstream>
 #include <string>
-#include <utility>
 
 #include "gmock/gmock.h"
 #include "test/unit_spirv.h"
diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp
index ce6999d..a7b96a4 100644
--- a/test/val/val_mesh_shading_test.cpp
+++ b/test/val/val_mesh_shading_test.cpp
@@ -14,7 +14,6 @@
 
 // Tests instructions from SPV_EXT_mesh_shader
 
-#include <sstream>
 #include <string>
 
 #include "gmock/gmock.h"
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index 689f0ba..8dc0fbc 100644
--- a/test/val/val_modes_test.cpp
+++ b/test/val/val_modes_test.cpp
@@ -18,7 +18,6 @@
 
 #include "gmock/gmock.h"
 #include "source/spirv_target_env.h"
-#include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
@@ -579,6 +578,11 @@
     sstr << "OpCapability Kernel\n";
     if (env == SPV_ENV_UNIVERSAL_1_3) {
       sstr << "OpCapability SubgroupDispatch\n";
+    } else if (env == SPV_ENV_UNIVERSAL_1_5) {
+      sstr << "OpCapability TileImageColorReadAccessEXT\n";
+      sstr << "OpCapability TileImageDepthReadAccessEXT\n";
+      sstr << "OpCapability TileImageStencilReadAccessEXT\n";
+      sstr << "OpExtension \"SPV_EXT_shader_tile_image\"\n";
     }
   }
   sstr << "OpMemoryModel Logical GLSL450\n";
@@ -702,6 +706,27 @@
                    "DepthLess", "DepthUnchanged"),
             Values(SPV_ENV_UNIVERSAL_1_0)));
 
+INSTANTIATE_TEST_SUITE_P(ValidateModeFragmentOnlyGoodSpv15,
+                         ValidateModeExecution,
+                         Combine(Values(SPV_SUCCESS), Values(""),
+                                 Values("Fragment"),
+                                 Values("NonCoherentColorAttachmentReadEXT",
+                                        "NonCoherentDepthAttachmentReadEXT",
+                                        "NonCoherentStencilAttachmentReadEXT"),
+                                 Values(SPV_ENV_UNIVERSAL_1_5)));
+
+INSTANTIATE_TEST_SUITE_P(
+    ValidateModeFragmentOnlyBadSpv15, ValidateModeExecution,
+    Combine(Values(SPV_ERROR_INVALID_DATA),
+            Values("Execution mode can only be used with the Fragment "
+                   "execution model."),
+            Values("Geometry", "TessellationControl", "TessellationEvaluation",
+                   "GLCompute", "Vertex", "Kernel"),
+            Values("NonCoherentColorAttachmentReadEXT",
+                   "NonCoherentDepthAttachmentReadEXT",
+                   "NonCoherentStencilAttachmentReadEXT"),
+            Values(SPV_ENV_UNIVERSAL_1_5)));
+
 INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution,
                          Combine(Values(SPV_SUCCESS), Values(""),
                                  Values("Kernel"),
diff --git a/test/val/val_state_test.cpp b/test/val/val_state_test.cpp
index 4097a1f..51064ab 100644
--- a/test/val/val_state_test.cpp
+++ b/test/val/val_state_test.cpp
@@ -18,14 +18,10 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-#include "source/latest_version_spirv_header.h"
-
 #include "source/enum_set.h"
 #include "source/extensions.h"
+#include "source/latest_version_spirv_header.h"
 #include "source/spirv_validator_options.h"
-#include "source/val/construct.h"
-#include "source/val/function.h"
-#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index 8693e80..6a3e4bd 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -28,8 +28,6 @@
 using ::testing::HasSubstr;
 using ::testing::Values;
 using ValidateStorage = spvtest::ValidateBase<std::string>;
-using ValidateStorageClass =
-    spvtest::ValidateBase<std::tuple<std::string, bool, bool, std::string>>;
 using ValidateStorageExecutionModel = spvtest::ValidateBase<std::string>;
 
 TEST_F(ValidateStorage, FunctionStorageInsideFunction) {
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index d272b08..a93f640 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -39,26 +39,47 @@
   set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools executables")
 endfunction()
 
+set(COMMON_TOOLS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/util/flags.cpp")
+
 if (NOT ${SPIRV_SKIP_EXECUTABLES})
-  add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-diff SRCS diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-diff SRCS ${COMMON_TOOLS_SRCS} diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-dis  SRCS ${COMMON_TOOLS_SRCS} dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-val  SRCS ${COMMON_TOOLS_SRCS} val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-opt  SRCS ${COMMON_TOOLS_SRCS} opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
   if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) # iOS does not allow std::system calls which spirv-reduce requires
-    add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY})
+    add_spvtools_tool(TARGET spirv-reduce SRCS ${COMMON_TOOLS_SRCS} reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY})
   endif()
-  add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY})
-  add_spvtools_tool(TARGET spirv-lint SRCS lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-link SRCS ${COMMON_TOOLS_SRCS} link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-lint SRCS ${COMMON_TOOLS_SRCS} lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+  add_spvtools_tool(TARGET spirv-as
+                    SRCS as/as.cpp
+                         ${COMMON_TOOLS_SRCS}
+                    LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+  target_include_directories(spirv-as PRIVATE ${spirv-tools_SOURCE_DIR}
+                                              ${SPIRV_HEADER_INCLUDE_DIR})
   add_spvtools_tool(TARGET spirv-cfg
                     SRCS cfg/cfg.cpp
                          cfg/bin_to_dot.h
                          cfg/bin_to_dot.cpp
+                         ${COMMON_TOOLS_SRCS}
                     LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
   target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR}
                                                ${SPIRV_HEADER_INCLUDE_DIR})
   set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt
                             spirv-cfg spirv-link spirv-lint)
+
+  if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Android"))
+    add_spvtools_tool(TARGET spirv-objdump
+                      SRCS objdump/objdump.cpp
+                           objdump/extract_source.cpp
+                           util/cli_consumer.cpp
+                           ${COMMON_TOOLS_SRCS}
+                      LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+    target_include_directories(spirv-objdump PRIVATE ${spirv-tools_SOURCE_DIR}
+                                                     ${SPIRV_HEADER_INCLUDE_DIR})
+    set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-objdump)
+  endif()
+
   if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS"))
     set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce)
   endif()
diff --git a/tools/as/as.cpp b/tools/as/as.cpp
index 506b058..2a000cf 100644
--- a/tools/as/as.cpp
+++ b/tools/as/as.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <cassert>
 #include <cstdio>
 #include <cstring>
 #include <vector>
@@ -19,11 +20,11 @@
 #include "source/spirv_target_env.h"
 #include "spirv-tools/libspirv.h"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
-void print_usage(char* argv0) {
-  std::string target_env_list = spvTargetEnvList(19, 80);
-  printf(
-      R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
+static const auto kDefaultEnvironment = "spv1.6";
+static const std::string kHelpText =
+    R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
 
 Usage: %s [options] [<filename>]
 
@@ -42,94 +43,70 @@
                   Numeric IDs in the binary will have the same values as in the
                   source. Non-numeric IDs are allocated by filling in the gaps,
                   starting with 1 and going up.
-  --target-env    {%s}
+  --target-env    %s
                   Use specified environment.
-)",
-      argv0, argv0, target_env_list.c_str());
-}
+)";
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+// clang-format off
+FLAG_SHORT_bool(  h,                    /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   help,                 /* default_value= */ false,               /* required= */false);
+FLAG_LONG_bool(   version,              /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   preserve_numeric_ids, /* default_value= */ false,               /* required= */ false);
+FLAG_SHORT_string(o,                    /* default_value= */ "",                  /* required= */ false);
+FLAG_LONG_string( target_env,           /* default_value= */ kDefaultEnvironment, /* required= */ false);
+// clang-format on
 
-int main(int argc, char** argv) {
-  const char* inFile = nullptr;
-  const char* outFile = nullptr;
-  uint32_t options = 0;
-  spv_target_env target_env = kDefaultEnvironment;
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h': {
-          print_usage(argv[0]);
-          return 0;
-        }
-        case 'o': {
-          if (!outFile && argi + 1 < argc) {
-            outFile = argv[++argi];
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case 0: {
-          // Setting a filename of "-" to indicate stdin.
-          if (!inFile) {
-            inFile = argv[argi];
-          } else {
-            fprintf(stderr, "error: More than one input file specified\n");
-            return 1;
-          }
-        } break;
-        case '-': {
-          // Long options
-          if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          } else if (0 == strcmp(argv[argi], "--help")) {
-            print_usage(argv[0]);
-            return 0;
-          } else if (0 == strcmp(argv[argi], "--preserve-numeric-ids")) {
-            options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
-          } else if (0 == strcmp(argv[argi], "--target-env")) {
-            if (argi + 1 < argc) {
-              const auto env_str = argv[++argi];
-              if (!spvParseTargetEnv(env_str, &target_env)) {
-                fprintf(stderr, "error: Unrecognized target env: %s\n",
-                        env_str);
-                return 1;
-              }
-            } else {
-              fprintf(stderr, "error: Missing argument to --target-env\n");
-              return 1;
-            }
-          } else {
-            fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        default:
-          fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (!inFile) {
-        inFile = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than one input file specified\n");
-        return 1;
-      }
-    }
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
   }
 
-  if (!outFile) {
+  if (flags::h.value() || flags::help.value()) {
+    const std::string target_env_list = spvTargetEnvList(19, 80);
+    printf(kHelpText.c_str(), argv[0], argv[0], target_env_list.c_str());
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    spv_target_env target_env;
+    bool success = spvParseTargetEnv(kDefaultEnvironment, &target_env);
+    assert(success && "Default environment should always parse.");
+    if (!success) {
+      fprintf(stderr,
+              "error: invalid default target environment. Please report this "
+              "issue.");
+      return 1;
+    }
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(target_env));
+    return 0;
+  }
+
+  std::string outFile = flags::o.value();
+  if (outFile.empty()) {
     outFile = "out.spv";
   }
 
+  uint32_t options = 0;
+  if (flags::preserve_numeric_ids.value()) {
+    options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
+  }
+
+  spv_target_env target_env;
+  if (!spvParseTargetEnv(flags::target_env.value().c_str(), &target_env)) {
+    fprintf(stderr, "error: Unrecognized target env: %s\n",
+            flags::target_env.value().c_str());
+    return 1;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    fprintf(stderr, "error: exactly one input file must be specified.\n");
+    return 1;
+  }
+  std::string inFile = flags::positional_arguments[0];
+
   std::vector<char> contents;
-  if (!ReadTextFile<char>(inFile, &contents)) return 1;
+  if (!ReadTextFile<char>(inFile.c_str(), &contents)) return 1;
 
   spv_binary binary;
   spv_diagnostic diagnostic = nullptr;
@@ -143,7 +120,8 @@
     return error;
   }
 
-  if (!WriteFile<uint32_t>(outFile, "wb", binary->code, binary->wordCount)) {
+  if (!WriteFile<uint32_t>(outFile.c_str(), "wb", binary->code,
+                           binary->wordCount)) {
     spvBinaryDestroy(binary);
     return 1;
   }
diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp
index 5380c21..2d11e6f 100644
--- a/tools/cfg/cfg.cpp
+++ b/tools/cfg/cfg.cpp
@@ -21,11 +21,11 @@
 #include "spirv-tools/libspirv.h"
 #include "tools/cfg/bin_to_dot.h"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
-// Prints a program usage message to stdout.
-static void print_usage(const char* argv0) {
-  printf(
-      R"(%s - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+static const std::string kHelpText =
+    R"(%s - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL
 
 Usage: %s [options] [<filename>]
 
@@ -40,71 +40,42 @@
   -o <filename>   Set the output filename.
                   Output goes to standard output if this option is
                   not specified, or if the filename is "-".
-)",
-      argv0, argv0);
-}
+)";
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+// clang-format off
+FLAG_SHORT_bool(  h,       /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   help,    /* default_value= */ false, /* required= */false);
+FLAG_LONG_bool(   version, /* default_value= */ false, /* required= */ false);
+FLAG_SHORT_string(o,       /* default_value= */ "",    /* required= */ false);
+// clang-format on
 
-int main(int argc, char** argv) {
-  const char* inFile = nullptr;
-  const char* outFile = nullptr;  // Stays nullptr if printing to stdout.
-
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h':
-          print_usage(argv[0]);
-          return 0;
-        case 'o': {
-          if (!outFile && argi + 1 < argc) {
-            outFile = argv[++argi];
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case '-': {
-          // Long options
-          if (0 == strcmp(argv[argi], "--help")) {
-            print_usage(argv[0]);
-            return 0;
-          }
-          if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          }
-          print_usage(argv[0]);
-          return 1;
-        }
-        case 0: {
-          // Setting a filename of "-" to indicate stdin.
-          if (!inFile) {
-            inFile = argv[argi];
-          } else {
-            fprintf(stderr, "error: More than one input file specified\n");
-            return 1;
-          }
-        } break;
-        default:
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (!inFile) {
-        inFile = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than one input file specified\n");
-        return 1;
-      }
-    }
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
   }
 
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpText.c_str(), argv[0], argv[0]);
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    fprintf(stderr, "error: exactly one input file must be specified.\n");
+    return 1;
+  }
+
+  std::string inFile = flags::positional_arguments[0];
+  std::string outFile = flags::o.value();
+
   // Read the input binary.
   std::vector<uint32_t> contents;
-  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile.c_str(), &contents)) return 1;
   spv_context context = spvContextCreate(kDefaultEnvironment);
   spv_diagnostic diagnostic = nullptr;
 
@@ -118,7 +89,8 @@
     return error;
   }
   std::string str = ss.str();
-  WriteFile(outFile, "w", str.data(), str.size());
+  WriteFile(outFile.empty() ? nullptr : outFile.c_str(), "w", str.data(),
+            str.size());
 
   spvDiagnosticDestroy(diagnostic);
   spvContextDestroy(context);
diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp
index d3cad04..2217896 100644
--- a/tools/diff/diff.cpp
+++ b/tools/diff/diff.cpp
@@ -17,14 +17,25 @@
 #endif
 
 #include "source/diff/diff.h"
-
 #include "source/opt/build_module.h"
 #include "source/opt/ir_context.h"
 #include "spirv-tools/libspirv.hpp"
 #include "tools/io.h"
 #include "tools/util/cli_consumer.h"
+#include "tools/util/flags.h"
 
-static void print_usage(char* argv0) {
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+constexpr bool kColorIsPossible =
+#if SPIRV_COLOR_TERMINAL
+    true;
+#else
+    false;
+#endif
+
+void print_usage(const char* argv0) {
   printf(R"(%s - Compare two SPIR-V files
 
 Usage: %s <src_filename> <dst_filename>
@@ -38,11 +49,12 @@
   -h, --help      Print this help.
   --version       Display diff version information.
 
-  --color         Force color output.  The default when printing to a terminal.
-                  Overrides a previous --no-color option.
-  --no-color      Don't print in color.  Overrides a previous --color option.
-                  The default when output goes to something other than a
-                  terminal (e.g. a pipe, or a shell redirection).
+  --color         Force color output. The default when printing to a terminal.
+                  If both --color and --no-color is present, --no-color prevails.
+  --no-color      Don't print in color. The default when output goes to
+                  something other than a terminal (e.g. a pipe, or a shell
+                  redirection).
+                  If both --color and --no-color is present, --no-color prevails.
 
   --no-indent     Don't indent instructions.
 
@@ -58,9 +70,7 @@
          argv0, argv0);
 }
 
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
-
-static bool is_assembly(const char* path) {
+bool is_assembly(const char* path) {
   const char* suffix = strrchr(path, '.');
   if (suffix == nullptr) {
     return false;
@@ -69,7 +79,7 @@
   return strcmp(suffix, ".spvasm") == 0;
 }
 
-static std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) {
+std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) {
   if (is_assembly(path)) {
     std::vector<char> contents;
     if (!ReadTextFile<char>(path, &contents)) return {};
@@ -89,101 +99,62 @@
                                contents.data(), contents.size());
 }
 
-int main(int argc, char** argv) {
-  const char* src_file = nullptr;
-  const char* dst_file = nullptr;
-  bool color_is_possible =
-#if SPIRV_COLOR_TERMINAL
-      true;
-#else
-      false;
-#endif
-  bool force_color = false;
-  bool force_no_color = false;
-  bool allow_indent = true;
-  bool no_header = false;
-  bool dump_id_map = false;
-  bool ignore_set_binding = false;
-  bool ignore_location = false;
+}  // namespace
 
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h':
-          print_usage(argv[0]);
-          return 0;
-        case '-': {
-          // Long options
-          if (strcmp(argv[argi], "--no-color") == 0) {
-            force_no_color = true;
-            force_color = false;
-          } else if (strcmp(argv[argi], "--color") == 0) {
-            force_no_color = false;
-            force_color = true;
-          } else if (strcmp(argv[argi], "--no-indent") == 0) {
-            allow_indent = false;
-          } else if (strcmp(argv[argi], "--no-header") == 0) {
-            no_header = true;
-          } else if (strcmp(argv[argi], "--with-id-map") == 0) {
-            dump_id_map = true;
-          } else if (strcmp(argv[argi], "--ignore-set-binding") == 0) {
-            ignore_set_binding = true;
-          } else if (strcmp(argv[argi], "--ignore-location") == 0) {
-            ignore_location = true;
-          } else if (strcmp(argv[argi], "--help") == 0) {
-            print_usage(argv[0]);
-            return 0;
-          } else if (strcmp(argv[argi], "--version") == 0) {
-            printf("%s\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        default:
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (src_file == nullptr) {
-        src_file = argv[argi];
-      } else if (dst_file == nullptr) {
-        dst_file = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than two input files specified\n");
-        return 1;
-      }
-    }
-  }
+// clang-format off
+FLAG_SHORT_bool(h,                  /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( help,               /* default_value= */ false, /* required= */false);
+FLAG_LONG_bool( version,            /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( color,              /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( no_color,           /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( no_indent,          /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( no_header,          /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( with_id_map,        /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( ignore_set_binding, /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool( ignore_location,    /* default_value= */ false, /* required= */ false);
+// clang-format on
 
-  if (src_file == nullptr || dst_file == nullptr) {
-    print_usage(argv[0]);
+int main(int, const char* argv[]) {
+  if (!flags::Parse(argv)) {
     return 1;
   }
 
-  spvtools::diff::Options options;
-
-  if (allow_indent) options.indent = true;
-  if (no_header) options.no_header = true;
-  if (dump_id_map) options.dump_id_map = true;
-  if (ignore_set_binding) options.ignore_set_binding = true;
-  if (ignore_location) options.ignore_location = true;
-
-  if (color_is_possible && !force_no_color) {
-    bool output_is_tty = true;
-#if defined(_POSIX_VERSION)
-    output_is_tty = isatty(fileno(stdout));
-#endif
-    if (output_is_tty || force_color) {
-      options.color_output = true;
-    }
+  if (flags::h.value() || flags::help.value()) {
+    print_usage(argv[0]);
+    return 0;
   }
 
-  std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file);
-  std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file);
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 2) {
+    fprintf(stderr, "error: two input files required.\n");
+    return 1;
+  }
+
+#if defined(_POSIX_VERSION)
+  const bool output_is_tty = isatty(fileno(stdout));
+#else
+  const bool output_is_tty = true;
+#endif
+
+  const std::string& src_file = flags::positional_arguments[0];
+  const std::string& dst_file = flags::positional_arguments[1];
+
+  spvtools::diff::Options options;
+  options.color_output = (output_is_tty || flags::color.value()) &&
+                         !flags::no_color.value() && kColorIsPossible;
+  options.indent = !flags::no_indent.value();
+  options.no_header = flags::no_header.value();
+  options.dump_id_map = flags::with_id_map.value();
+  options.ignore_set_binding = flags::ignore_set_binding.value();
+  options.ignore_location = flags::ignore_location.value();
+
+  std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file.c_str());
+  std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file.c_str());
 
   if (!src) {
     fprintf(stderr, "error: Loading src file\n");
diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp
index 64380db..aacd37f 100644
--- a/tools/dis/dis.cpp
+++ b/tools/dis/dis.cpp
@@ -24,10 +24,9 @@
 
 #include "spirv-tools/libspirv.h"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
-static void print_usage(char* argv0) {
-  printf(
-      R"(%s - Disassemble a SPIR-V binary module
+static const std::string kHelpText = R"(%s - Disassemble a SPIR-V binary module
 
 Usage: %s [options] [<filename>]
 
@@ -58,15 +57,49 @@
   --offsets       Show byte offsets for each instruction.
 
   --comment       Add comments to make reading easier
-)",
-      argv0, argv0);
-}
+)";
+
+// clang-format off
+FLAG_SHORT_bool  (h,         /* default_value= */ false, /* required= */ false);
+FLAG_SHORT_string(o,         /* default_value= */ "-",   /* required= */ false);
+FLAG_LONG_bool   (help,      /* default_value= */ false, /* required= */false);
+FLAG_LONG_bool   (version,   /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (color,     /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (no_color,  /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (no_indent, /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (no_header, /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (raw_id,    /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (offsets,   /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (comment,   /* default_value= */ false, /* required= */ false);
+// clang-format on
 
 static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
 
-int main(int argc, char** argv) {
-  const char* inFile = nullptr;
-  const char* outFile = nullptr;
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
+  }
+
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpText.c_str(), argv[0], argv[0]);
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(kDefaultEnvironment));
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() > 1) {
+    fprintf(stderr, "error: more than one input file specified.\n");
+    return 1;
+  }
+
+  const std::string inFile = flags::positional_arguments.size() == 0
+                                 ? "-"
+                                 : flags::positional_arguments[0];
+  const std::string outFile = flags::o.value();
 
   bool color_is_possible =
 #if SPIRV_COLOR_TERMINAL
@@ -74,105 +107,30 @@
 #else
       false;
 #endif
-  bool force_color = false;
-  bool force_no_color = false;
-
-  bool allow_indent = true;
-  bool show_byte_offsets = false;
-  bool no_header = false;
-  bool friendly_names = true;
-  bool comments = false;
-
-  for (int argi = 1; argi < argc; ++argi) {
-    if ('-' == argv[argi][0]) {
-      switch (argv[argi][1]) {
-        case 'h':
-          print_usage(argv[0]);
-          return 0;
-        case 'o': {
-          if (!outFile && argi + 1 < argc) {
-            outFile = argv[++argi];
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case '-': {
-          // Long options
-          if (0 == strcmp(argv[argi], "--no-color")) {
-            force_no_color = true;
-            force_color = false;
-          } else if (0 == strcmp(argv[argi], "--color")) {
-            force_no_color = false;
-            force_color = true;
-          } else if (0 == strcmp(argv[argi], "--comment")) {
-            comments = true;
-          } else if (0 == strcmp(argv[argi], "--no-indent")) {
-            allow_indent = false;
-          } else if (0 == strcmp(argv[argi], "--offsets")) {
-            show_byte_offsets = true;
-          } else if (0 == strcmp(argv[argi], "--no-header")) {
-            no_header = true;
-          } else if (0 == strcmp(argv[argi], "--raw-id")) {
-            friendly_names = false;
-          } else if (0 == strcmp(argv[argi], "--help")) {
-            print_usage(argv[0]);
-            return 0;
-          } else if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s\n", spvSoftwareVersionDetailsString());
-            printf("Target: %s\n",
-                   spvTargetEnvDescription(kDefaultEnvironment));
-            return 0;
-          } else {
-            print_usage(argv[0]);
-            return 1;
-          }
-        } break;
-        case 0: {
-          // Setting a filename of "-" to indicate stdin.
-          if (!inFile) {
-            inFile = argv[argi];
-          } else {
-            fprintf(stderr, "error: More than one input file specified\n");
-            return 1;
-          }
-        } break;
-        default:
-          print_usage(argv[0]);
-          return 1;
-      }
-    } else {
-      if (!inFile) {
-        inFile = argv[argi];
-      } else {
-        fprintf(stderr, "error: More than one input file specified\n");
-        return 1;
-      }
-    }
-  }
 
   uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
 
-  if (allow_indent) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
+  if (!flags::no_indent.value()) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
 
-  if (show_byte_offsets) options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET;
+  if (flags::offsets.value())
+    options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET;
 
-  if (no_header) options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
+  if (flags::no_header.value()) options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
 
-  if (friendly_names) options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
+  if (!flags::raw_id.value())
+    options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
 
-  if (comments) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
+  if (flags::comment.value()) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
 
-  if (!outFile || (0 == strcmp("-", outFile))) {
+  if (flags::o.value() == "-") {
     // Print to standard output.
     options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
-
-    if (color_is_possible && !force_no_color) {
+    if (color_is_possible && !flags::no_color.value()) {
       bool output_is_tty = true;
 #if defined(_POSIX_VERSION)
       output_is_tty = isatty(fileno(stdout));
 #endif
-      if (output_is_tty || force_color) {
+      if (output_is_tty || flags::color.value()) {
         options |= SPV_BINARY_TO_TEXT_OPTION_COLOR;
       }
     }
@@ -180,7 +138,7 @@
 
   // Read the input binary.
   std::vector<uint32_t> contents;
-  if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1;
+  if (!ReadBinaryFile<uint32_t>(inFile.c_str(), &contents)) return 1;
 
   // If printing to standard output, then spvBinaryToText should
   // do the printing.  In particular, colour printing on Windows is
@@ -205,7 +163,7 @@
   }
 
   if (!print_to_stdout) {
-    if (!WriteFile<char>(outFile, "w", text->str, text->length)) {
+    if (!WriteFile<char>(outFile.c_str(), "w", text->str, text->length)) {
       spvTextDestroy(text);
       return 1;
     }
diff --git a/tools/io.h b/tools/io.h
index 9dc834e..8bbee3a 100644
--- a/tools/io.h
+++ b/tools/io.h
@@ -127,7 +127,7 @@
  public:
   // Opens |filename| in the given mode.  If |filename| is nullptr, the empty
   // string or "-", stdout will be set to the given mode.
-  OutputFile(const char* filename, const char* mode) {
+  OutputFile(const char* filename, const char* mode) : old_mode_(0) {
     const bool use_stdout =
         !filename || (filename[0] == '-' && filename[1] == '\0');
     if (use_stdout) {
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp
index bdddeb8..381d8b9 100644
--- a/tools/link/linker.cpp
+++ b/tools/link/linker.cpp
@@ -14,6 +14,7 @@
 
 #include "spirv-tools/linker.hpp"
 
+#include <cassert>
 #include <cstring>
 #include <iostream>
 #include <vector>
@@ -22,10 +23,11 @@
 #include "source/table.h"
 #include "spirv-tools/libspirv.hpp"
 #include "tools/io.h"
+#include "tools/util/flags.h"
 
 namespace {
 
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+constexpr auto kDefaultEnvironment = "spv1.6";
 
 void print_usage(const char* program) {
   std::string target_env_list = spvTargetEnvList(16, 80);
@@ -67,65 +69,58 @@
 
 }  // namespace
 
-int main(int argc, char** argv) {
-  std::vector<const char*> inFiles;
-  const char* outFile = nullptr;
-  spv_target_env target_env = kDefaultEnvironment;
-  spvtools::LinkerOptions options;
+// clang-format off
+FLAG_SHORT_bool(  h,                     /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   help,                  /* default_value= */ false,               /* required= */false);
+FLAG_LONG_bool(   version,               /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   verify_ids,            /* default_value= */ false,               /* required= */ false);
+FLAG_LONG_bool(   create_library,        /* default_value= */ false,               /* required= */ false);
+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);
+// clang-format on
 
-  for (int argi = 1; argi < argc; ++argi) {
-    const char* cur_arg = argv[argi];
-    if ('-' == cur_arg[0]) {
-      if (0 == strcmp(cur_arg, "-o")) {
-        if (argi + 1 < argc) {
-          if (!outFile) {
-            outFile = argv[++argi];
-          } else {
-            fprintf(stderr, "error: More than one output file specified\n");
-            return 1;
-          }
-        } else {
-          fprintf(stderr, "error: Missing argument to %s\n", cur_arg);
-          return 1;
-        }
-      } else if (0 == strcmp(cur_arg, "--allow-partial-linkage")) {
-        options.SetAllowPartialLinkage(true);
-      } else if (0 == strcmp(cur_arg, "--create-library")) {
-        options.SetCreateLibrary(true);
-      } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
-        print_usage(argv[0]);
-        return 0;
-      } else if (0 == strcmp(cur_arg, "--target-env")) {
-        if (argi + 1 < argc) {
-          const auto env_str = argv[++argi];
-          if (!spvParseTargetEnv(env_str, &target_env)) {
-            fprintf(stderr, "error: Unrecognized target env: %s\n", env_str);
-            return 1;
-          }
-        } else {
-          fprintf(stderr, "error: Missing argument to --target-env\n");
-          return 1;
-        }
-      } else if (0 == strcmp(cur_arg, "--verify-ids")) {
-        options.SetVerifyIds(true);
-      } else if (0 == strcmp(cur_arg, "--version")) {
-        printf("%s\n", spvSoftwareVersionDetailsString());
-        printf("Target: %s\n", spvTargetEnvDescription(target_env));
-        return 0;
-      } else {
-        fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]);
-        print_usage(argv[0]);
-        return 1;
-      }
-    } else {
-      inFiles.push_back(cur_arg);
+int main(int, const char* argv[]) {
+  if (!flags::Parse(argv)) {
+    return 1;
+  }
+
+  if (flags::h.value() || flags::help.value()) {
+    print_usage(argv[0]);
+    return 0;
+  }
+
+  if (flags::version.value()) {
+    spv_target_env target_env;
+    bool success = spvParseTargetEnv(kDefaultEnvironment, &target_env);
+    assert(success && "Default environment should always parse.");
+    if (!success) {
+      fprintf(stderr,
+              "error: invalid default target environment. Please report this "
+              "issue.");
+      return 1;
     }
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    printf("Target: %s\n", spvTargetEnvDescription(target_env));
+    return 0;
   }
 
-  if (!outFile) {
-    outFile = "out.spv";
+  spv_target_env target_env;
+  if (!spvParseTargetEnv(flags::target_env.value().c_str(), &target_env)) {
+    fprintf(stderr, "error: Unrecognized target env: %s\n",
+            flags::target_env.value().c_str());
+    return 1;
   }
 
+  const std::string outFile =
+      flags::o.value().empty() ? "out.spv" : flags::o.value();
+  const std::vector<std::string>& inFiles = flags::positional_arguments;
+
+  spvtools::LinkerOptions options;
+  options.SetAllowPartialLinkage(flags::allow_partial_linkage.value());
+  options.SetCreateLibrary(flags::create_library.value());
+  options.SetVerifyIds(flags::verify_ids.value());
+
   if (inFiles.empty()) {
     fprintf(stderr, "error: No input file specified\n");
     return 1;
@@ -133,7 +128,7 @@
 
   std::vector<std::vector<uint32_t>> contents(inFiles.size());
   for (size_t i = 0u; i < inFiles.size(); ++i) {
-    if (!ReadBinaryFile<uint32_t>(inFiles[i], &contents[i])) return 1;
+    if (!ReadBinaryFile<uint32_t>(inFiles[i].c_str(), &contents[i])) return 1;
   }
 
   const spvtools::MessageConsumer consumer = [](spv_message_level_t level,
@@ -165,7 +160,7 @@
   spv_result_t status = Link(context, contents, &linkingResult, options);
   if (status != SPV_SUCCESS && status != SPV_WARNING) return 1;
 
-  if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(),
+  if (!WriteFile<uint32_t>(outFile.c_str(), "wb", linkingResult.data(),
                            linkingResult.size()))
     return 1;
 
diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp
index d37df83..00c6cd2 100644
--- a/tools/lint/lint.cpp
+++ b/tools/lint/lint.cpp
@@ -18,58 +18,57 @@
 #include "spirv-tools/linter.hpp"
 #include "tools/io.h"
 #include "tools/util/cli_consumer.h"
-
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+#include "tools/util/flags.h"
 
 namespace {
-// Status and actions to perform after parsing command-line arguments.
-enum LintActions { LINT_CONTINUE, LINT_STOP };
 
-struct LintStatus {
-  LintActions action;
-  int code;
-};
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+constexpr auto kHelpTextFmt =
+    R"(%s - Lint a SPIR-V binary module.
 
-// Parses command-line flags. |argc| contains the number of command-line flags.
-// |argv| points to an array of strings holding the flags.
-//
-// On return, this function stores the name of the input program in |in_file|.
-// The return value indicates whether optimization should continue and a status
-// code indicating an error or success.
-LintStatus ParseFlags(int argc, const char** argv, const char** in_file) {
-  // TODO (dongja): actually parse flags, etc.
-  if (argc != 2) {
-    spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {},
-                    "expected exactly one argument: in_file");
-    return {LINT_STOP, 1};
-  }
+Usage: %s [options] <filename>
 
-  *in_file = argv[1];
+Options:
 
-  return {LINT_CONTINUE, 0};
-}
+  -h, --help      Print this help.
+  --version       Display assembler version information.
+)";
+
 }  // namespace
 
-int main(int argc, const char** argv) {
-  const char* in_file = nullptr;
+// clang-format off
+FLAG_SHORT_bool(  h,       /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   help,    /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   version, /* default_value= */ false, /* required= */ false);
+// clang-format on
 
-  spv_target_env target_env = kDefaultEnvironment;
-
-  spvtools::Linter linter(target_env);
-  linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
-
-  LintStatus status = ParseFlags(argc, argv, &in_file);
-
-  if (status.action == LINT_STOP) {
-    return status.code;
-  }
-
-  std::vector<uint32_t> binary;
-  if (!ReadBinaryFile(in_file, &binary)) {
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
     return 1;
   }
 
-  bool ok = linter.Run(binary.data(), binary.size());
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpTextFmt, argv[0], argv[0]);
+    return 0;
+  }
 
-  return ok ? 0 : 1;
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {},
+                    "expected exactly one input file.");
+    return 1;
+  }
+
+  spvtools::Linter linter(kDefaultEnvironment);
+  linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+  std::vector<uint32_t> binary;
+  if (!ReadBinaryFile(flags::positional_arguments[0].c_str(), &binary)) {
+    return 1;
+  }
+
+  return linter.Run(binary.data(), binary.size()) ? 0 : 1;
 }
diff --git a/tools/objdump/extract_source.cpp b/tools/objdump/extract_source.cpp
new file mode 100644
index 0000000..0295952
--- /dev/null
+++ b/tools/objdump/extract_source.cpp
@@ -0,0 +1,213 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "extract_source.h"
+
+#include <cassert>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "source/opt/log.h"
+#include "spirv-tools/libspirv.hpp"
+#include "spirv/unified1/spirv.hpp"
+#include "tools/util/cli_consumer.h"
+
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+// Extract a string literal from a given range.
+// Copies all the characters from `begin` to the first '\0' it encounters, while
+// removing escape patterns.
+// Not finding a '\0' before reaching `end` fails the extraction.
+//
+// Returns `true` if the extraction succeeded.
+// `output` value is undefined if false is returned.
+spv_result_t ExtractStringLiteral(const spv_position_t& loc, const char* begin,
+                                  const char* end, std::string* output) {
+  size_t sourceLength = std::distance(begin, end);
+  std::string escapedString;
+  escapedString.resize(sourceLength);
+
+  size_t writeIndex = 0;
+  size_t readIndex = 0;
+  for (; readIndex < sourceLength; writeIndex++, readIndex++) {
+    const char read = begin[readIndex];
+    if (read == '\0') {
+      escapedString.resize(writeIndex);
+      output->append(escapedString);
+      return SPV_SUCCESS;
+    }
+
+    if (read == '\\') {
+      ++readIndex;
+    }
+    escapedString[writeIndex] = begin[readIndex];
+  }
+
+  spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                  "Missing NULL terminator for literal string.");
+  return SPV_ERROR_INVALID_BINARY;
+}
+
+spv_result_t extractOpString(const spv_position_t& loc,
+                             const spv_parsed_instruction_t& instruction,
+                             std::string* output) {
+  assert(output != nullptr);
+  assert(instruction.opcode == spv::Op::OpString);
+  if (instruction.num_operands != 2) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                    "Missing operands for OpString.");
+    return SPV_ERROR_INVALID_BINARY;
+  }
+
+  const auto& operand = instruction.operands[1];
+  const char* stringBegin =
+      reinterpret_cast<const char*>(instruction.words + operand.offset);
+  const char* stringEnd = reinterpret_cast<const char*>(
+      instruction.words + operand.offset + operand.num_words);
+  return ExtractStringLiteral(loc, stringBegin, stringEnd, output);
+}
+
+spv_result_t extractOpSourceContinued(
+    const spv_position_t& loc, const spv_parsed_instruction_t& instruction,
+    std::string* output) {
+  assert(output != nullptr);
+  assert(instruction.opcode == spv::Op::OpSourceContinued);
+  if (instruction.num_operands != 1) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                    "Missing operands for OpSourceContinued.");
+    return SPV_ERROR_INVALID_BINARY;
+  }
+
+  const auto& operand = instruction.operands[0];
+  const char* stringBegin =
+      reinterpret_cast<const char*>(instruction.words + operand.offset);
+  const char* stringEnd = reinterpret_cast<const char*>(
+      instruction.words + operand.offset + operand.num_words);
+  return ExtractStringLiteral(loc, stringBegin, stringEnd, output);
+}
+
+spv_result_t extractOpSource(const spv_position_t& loc,
+                             const spv_parsed_instruction_t& instruction,
+                             spv::Id* filename, std::string* code) {
+  assert(filename != nullptr && code != nullptr);
+  assert(instruction.opcode == spv::Op::OpSource);
+  // OpCode [ Source Language | Version | File (optional) | Source (optional) ]
+  if (instruction.num_words < 3) {
+    spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                    "Missing operands for OpSource.");
+    return SPV_ERROR_INVALID_BINARY;
+  }
+
+  *filename = 0;
+  *code = "";
+  if (instruction.num_words < 4) {
+    return SPV_SUCCESS;
+  }
+  *filename = instruction.words[3];
+
+  if (instruction.num_words < 5) {
+    return SPV_SUCCESS;
+  }
+
+  const char* stringBegin =
+      reinterpret_cast<const char*>(instruction.words + 4);
+  const char* stringEnd =
+      reinterpret_cast<const char*>(instruction.words + instruction.num_words);
+  return ExtractStringLiteral(loc, stringBegin, stringEnd, code);
+}
+
+}  // namespace
+
+bool ExtractSourceFromModule(
+    const std::vector<uint32_t>& binary,
+    std::unordered_map<std::string, std::string>* output) {
+  auto context = spvtools::SpirvTools(kDefaultEnvironment);
+  context.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+
+  // There is nothing valuable in the header.
+  spvtools::HeaderParser headerParser = [](const spv_endianness_t,
+                                           const spv_parsed_header_t&) {
+    return SPV_SUCCESS;
+  };
+
+  std::unordered_map<uint32_t, std::string> stringMap;
+  std::vector<std::pair<spv::Id, std::string>> sources;
+  spv::Op lastOpcode = spv::Op::OpMax;
+  size_t instructionIndex = 0;
+
+  spvtools::InstructionParser instructionParser =
+      [&stringMap, &sources, &lastOpcode,
+       &instructionIndex](const spv_parsed_instruction_t& instruction) {
+        const spv_position_t loc = {0, 0, instructionIndex + 1};
+        spv_result_t result = SPV_SUCCESS;
+
+        if (instruction.opcode == spv::Op::OpString) {
+          std::string content;
+          result = extractOpString(loc, instruction, &content);
+          if (result == SPV_SUCCESS) {
+            stringMap.emplace(instruction.result_id, std::move(content));
+          }
+        } else if (instruction.opcode == spv::Op::OpSource) {
+          spv::Id filenameId;
+          std::string code;
+          result = extractOpSource(loc, instruction, &filenameId, &code);
+          if (result == SPV_SUCCESS) {
+            sources.emplace_back(std::make_pair(filenameId, std::move(code)));
+          }
+        } else if (instruction.opcode == spv::Op::OpSourceContinued) {
+          if (lastOpcode != spv::Op::OpSource) {
+            spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
+                            "OpSourceContinued MUST follow an OpSource.");
+            return SPV_ERROR_INVALID_BINARY;
+          }
+
+          assert(sources.size() > 0);
+          result = extractOpSourceContinued(loc, instruction,
+                                            &sources.back().second);
+        }
+
+        ++instructionIndex;
+        lastOpcode = static_cast<spv::Op>(instruction.opcode);
+        return result;
+      };
+
+  if (!context.Parse(binary, headerParser, instructionParser)) {
+    return false;
+  }
+
+  std::string defaultName = "unnamed-";
+  size_t unnamedCount = 0;
+  for (auto & [ id, code ] : sources) {
+    std::string filename;
+    const auto it = stringMap.find(id);
+    if (it == stringMap.cend() || it->second.empty()) {
+      filename = "unnamed-" + std::to_string(unnamedCount) + ".hlsl";
+      ++unnamedCount;
+    } else {
+      filename = it->second;
+    }
+
+    if (output->count(filename) != 0) {
+      spvtools::Error(spvtools::utils::CLIMessageConsumer, "", {},
+                      "Source file name conflict.");
+      return false;
+    }
+    output->insert({filename, code});
+  }
+
+  return true;
+}
diff --git a/tools/objdump/extract_source.h b/tools/objdump/extract_source.h
new file mode 100644
index 0000000..3e5ecfa
--- /dev/null
+++ b/tools/objdump/extract_source.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDE_SPIRV_TOOLS_EXTRACT_SOURCE_HPP_
+#define INCLUDE_SPIRV_TOOLS_EXTRACT_SOURCE_HPP_
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+// Parse a SPIR-V module, and extracts all HLSL source code from it.
+// This function doesn't lift the SPIR-V code, but only relies on debug symbols.
+// This means if the compiler didn't include some files, they won't show up.
+//
+// Returns a map of <filename, source_code> extracted from it.
+// - `binary`: a vector containing the whole SPIR-V binary to extract source
+// from.
+// - `output`: <filename, source_code> mapping, mapping each filename
+//            (if defined) to its code.
+//
+// Returns `true` if the extraction succeeded, `false` otherwise.
+// `output` value is undefined if `false` is returned.
+bool ExtractSourceFromModule(
+    const std::vector<uint32_t>& binary,
+    std::unordered_map<std::string, std::string>* output);
+
+#endif  // INCLUDE_SPIRV_TOOLS_EXTRACT_SOURCE_HPP_
diff --git a/tools/objdump/objdump.cpp b/tools/objdump/objdump.cpp
new file mode 100644
index 0000000..79181b0
--- /dev/null
+++ b/tools/objdump/objdump.cpp
@@ -0,0 +1,174 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <filesystem>
+#include <iostream>
+
+#include "extract_source.h"
+#include "source/opt/log.h"
+#include "tools/io.h"
+#include "tools/util/cli_consumer.h"
+#include "tools/util/flags.h"
+
+namespace {
+
+constexpr auto kHelpTextFmt =
+    R"(%s - Dumps information from a SPIR-V binary.
+
+Usage: %s [options] <filename>
+
+one of the following switches must be given:
+  --source        Extract source files obtained from debug symbols, output to stdout.
+  --entrypoint    Extracts the entrypoint name of the module, output to stdout.
+  --compiler-cmd  Extracts the command line used to compile this module, output to stdout.
+
+
+General options:
+  -h, --help      Print this help.
+  --version       Display assembler version information.
+  -f,--force      Allow output file overwrite.
+
+Source dump options:
+  --list          Do not extract source code, only print filenames to stdout.
+  --outdir        Where shall the exrtacted HLSL/HLSL files be written to?
+                  File written to stdout if '-' is given. Default is `-`.
+)";
+
+// Removes trailing '/' from `input`.
+// A behavior difference has been observed between libc++ implementations.
+// Fixing path to prevent this edge case to be reached.
+// (https://github.com/llvm/llvm-project/issues/60634)
+std::string fixPathForLLVM(std::string input) {
+  while (!input.empty() && input.back() == '/') input.resize(input.size() - 1);
+  return input;
+}
+
+// Write each HLSL file described in `sources` in a file in `outdirPath`.
+// Doesn't ovewrite existing files, unless `overwrite` is set to true. The
+// created HLSL file's filename is the path's filename obtained from `sources`.
+// Returns true if all files could be written. False otherwise.
+bool OutputSourceFiles(
+    const std::unordered_map<std::string, std::string>& sources,
+    const std::string& outdirPath, bool overwrite) {
+  std::filesystem::path outdir(fixPathForLLVM(outdirPath));
+  if (!std::filesystem::is_directory(outdir)) {
+    if (!std::filesystem::create_directories(outdir)) {
+      std::cerr << "error: could not create output directory " << outdir
+                << std::endl;
+      return false;
+    }
+  }
+
+  for (const auto & [ filepath, code ] : sources) {
+    if (code.empty()) {
+      std::cout << "Ignoring source for " << filepath
+                << ": no code source in debug infos." << std::endl;
+      continue;
+    }
+
+    std::filesystem::path old_path(filepath);
+    std::filesystem::path new_path = outdir / old_path.filename();
+
+    if (!overwrite && std::filesystem::exists(new_path)) {
+      std::cerr << "file " << filepath
+                << " already exists, aborting (use --overwrite to allow it)."
+                << std::endl;
+      return false;
+    }
+
+    std::cout << "Exporting " << new_path << std::endl;
+    if (!WriteFile<char>(new_path.string().c_str(), "w", code.c_str(),
+                         code.size())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+// clang-format off
+FLAG_SHORT_bool(  h,            /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   help,         /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   version,      /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   source,       /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   entrypoint,   /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   compiler_cmd, /* default_value= */ false, /* required= */ false);
+FLAG_SHORT_bool(  f,            /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool(   force,        /* default_value= */ false, /* required= */ false);
+FLAG_LONG_string( outdir,       /* default_value= */ "-",   /* required= */ false);
+FLAG_LONG_bool(   list,         /* default_value= */ false, /* required= */ false);
+// clang-format on
+
+int main(int, const char** argv) {
+  if (!flags::Parse(argv)) {
+    return 1;
+  }
+  if (flags::h.value() || flags::help.value()) {
+    printf(kHelpTextFmt, argv[0], argv[0]);
+    return 0;
+  }
+  if (flags::version.value()) {
+    printf("%s\n", spvSoftwareVersionDetailsString());
+    return 0;
+  }
+
+  if (flags::positional_arguments.size() != 1) {
+    std::cerr << "Expected exactly one input file." << std::endl;
+    return 1;
+  }
+  if (flags::entrypoint.value() || flags::compiler_cmd.value()) {
+    std::cerr << "Unimplemented flags." << std::endl;
+    return 1;
+  }
+
+  std::vector<uint32_t> binary;
+  if (!ReadBinaryFile(flags::positional_arguments[0].c_str(), &binary)) {
+    return 1;
+  }
+
+  if (flags::source.value()) {
+    std::unordered_map<std::string, std::string> sourceCode;
+    if (!ExtractSourceFromModule(binary, &sourceCode)) {
+      return 1;
+    }
+
+    if (flags::list.value()) {
+      for (const auto & [ filename, source ] : sourceCode) {
+        printf("%s\n", filename.c_str());
+      }
+      return 0;
+    }
+
+    const bool outputToConsole = flags::outdir.value() == "-";
+
+    if (outputToConsole) {
+      for (const auto & [ filename, source ] : sourceCode) {
+        std::cout << filename << ":" << std::endl
+                  << source << std::endl
+                  << std::endl;
+      }
+      return 0;
+    }
+
+    const std::filesystem::path outdirPath(flags::outdir.value());
+    if (!OutputSourceFiles(sourceCode, outdirPath.string(),
+                           flags::force.value())) {
+      return 1;
+    }
+  }
+
+  // FIXME: implement logic.
+  return 0;
+}
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
index e7b735e..2dc95d8 100644
--- a/tools/sva/yarn.lock
+++ b/tools/sva/yarn.lock
@@ -1700,9 +1700,9 @@
     mkdirp "^0.5.1"
 
 "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
-  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+  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"
diff --git a/tools/util/flags.cpp b/tools/util/flags.cpp
new file mode 100644
index 0000000..11b8967
--- /dev/null
+++ b/tools/util/flags.cpp
@@ -0,0 +1,242 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "flags.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <regex>
+#include <string>
+#include <unordered_set>
+#include <variant>
+#include <vector>
+
+namespace flags {
+
+std::vector<std::string> positional_arguments;
+
+namespace {
+
+using token_t = const char*;
+using token_iterator_t = token_t*;
+
+// Extracts the flag name from a potential token.
+// This function only looks for a '=', to split the flag name from the value for
+// long-form flags. Returns the name of the flag, prefixed with the hyphen(s).
+inline std::string get_flag_name(const std::string& flag, bool is_short_flag) {
+  if (is_short_flag) {
+    return flag;
+  }
+
+  size_t equal_index = flag.find('=');
+  if (equal_index == std::string::npos) {
+    return flag;
+  }
+  return flag.substr(0, equal_index);
+}
+
+// Parse a boolean flag. Returns `true` if the parsing succeeded, `false`
+// otherwise.
+bool parse_bool_flag(Flag<bool>& flag, bool is_short_flag,
+                     const std::string& token) {
+  if (is_short_flag) {
+    flag.value() = true;
+    return true;
+  }
+
+  const std::string raw_flag(token);
+  size_t equal_index = raw_flag.find('=');
+  if (equal_index == std::string::npos) {
+    flag.value() = true;
+    return true;
+  }
+
+  const std::string value = raw_flag.substr(equal_index + 1);
+  if (value == "true") {
+    flag.value() = true;
+    return true;
+  }
+
+  if (value == "false") {
+    flag.value() = false;
+    return true;
+  }
+
+  return false;
+}
+
+// Parse a uint32_t flag value.
+bool parse_flag_value(Flag<uint32_t>& flag, const std::string& value) {
+  std::regex unsigned_pattern("^ *[0-9]+ *$");
+  if (!std::regex_match(value, unsigned_pattern)) {
+    std::cerr << "'" << value << "' is not a unsigned number." << std::endl;
+    return false;
+  }
+
+  errno = 0;
+  char* end_ptr = nullptr;
+  const uint64_t number = strtoull(value.c_str(), &end_ptr, 10);
+  if (end_ptr == nullptr || end_ptr != value.c_str() + value.size() ||
+      errno == EINVAL) {
+    std::cerr << "'" << value << "' is not a unsigned number." << std::endl;
+    return false;
+  }
+
+  if (errno == ERANGE || number > static_cast<size_t>(UINT32_MAX)) {
+    std::cerr << "'" << value << "' cannot be represented as a 32bit unsigned."
+              << std::endl;
+    return false;
+  }
+
+  flag.value() = static_cast<uint32_t>(number);
+  return true;
+}
+
+// "Parse" a string flag value (assigns it, cannot fail).
+bool parse_flag_value(Flag<std::string>& flag, const std::string& value) {
+  flag.value() = value;
+  return true;
+}
+
+// Parse a potential multi-token flag. Moves the iterator to the last flag's
+// token if it's a multi-token flag. Returns `true` if the parsing succeeded.
+// The iterator is moved to the last parsed token.
+template <typename T>
+bool parse_flag(Flag<T>& flag, bool is_short_flag, const char*** iterator) {
+  const std::string raw_flag(**iterator);
+  std::string raw_value;
+  const size_t equal_index = raw_flag.find('=');
+
+  if (is_short_flag || equal_index == std::string::npos) {
+    if ((*iterator)[1] == nullptr) {
+      return false;
+    }
+
+    // This is a bi-token flag. Moving iterator to the last parsed token.
+    raw_value = (*iterator)[1];
+    *iterator += 1;
+  } else {
+    // This is a mono-token flag, no need to move the iterator.
+    raw_value = raw_flag.substr(equal_index + 1);
+  }
+
+  return parse_flag_value(flag, raw_value);
+}
+
+}  // namespace
+
+// This is the function to expand if you want to support a new type.
+bool FlagList::parse_flag_info(FlagInfo& info, token_iterator_t* iterator) {
+  bool success = false;
+
+  std::visit(
+      [&](auto&& item) {
+        using T = std::decay_t<decltype(item.get())>;
+        if constexpr (std::is_same_v<T, Flag<bool>>) {
+          success = parse_bool_flag(item.get(), info.is_short, **iterator);
+        } else if constexpr (std::is_same_v<T, Flag<std::string>>) {
+          success = parse_flag(item.get(), info.is_short, iterator);
+        } else if constexpr (std::is_same_v<T, Flag<uint32_t>>) {
+          success = parse_flag(item.get(), info.is_short, iterator);
+        } else {
+          static_assert(always_false_v<T>, "Unsupported flag type.");
+        }
+      },
+      info.flag);
+
+  return success;
+}
+
+bool FlagList::parse(token_t* argv) {
+  flags::positional_arguments.clear();
+  std::unordered_set<const FlagInfo*> parsed_flags;
+
+  bool ignore_flags = false;
+  for (const char** it = argv + 1; *it != nullptr; it++) {
+    if (ignore_flags) {
+      flags::positional_arguments.emplace_back(*it);
+      continue;
+    }
+
+    // '--' alone is used to mark the end of the flags.
+    if (std::strcmp(*it, "--") == 0) {
+      ignore_flags = true;
+      continue;
+    }
+
+    // '-' alone is not a flag, but often used to say 'stdin'.
+    if (std::strcmp(*it, "-") == 0) {
+      flags::positional_arguments.emplace_back(*it);
+      continue;
+    }
+
+    const std::string raw_flag(*it);
+    if (raw_flag.size() == 0) {
+      continue;
+    }
+
+    if (raw_flag[0] != '-') {
+      flags::positional_arguments.emplace_back(*it);
+      continue;
+    }
+
+    // Only case left: flags (long and shorts).
+    if (raw_flag.size() < 2) {
+      std::cerr << "Unknown flag " << raw_flag << std::endl;
+      return false;
+    }
+    const bool is_short_flag = std::strncmp(*it, "--", 2) != 0;
+    const std::string flag_name = get_flag_name(raw_flag, is_short_flag);
+
+    auto needle = std::find_if(
+        get_flags().begin(), get_flags().end(),
+        [&flag_name](const auto& item) { return item.name == flag_name; });
+    if (needle == get_flags().end()) {
+      std::cerr << "Unknown flag " << flag_name << std::endl;
+      return false;
+    }
+
+    if (parsed_flags.count(&*needle) != 0) {
+      std::cerr << "The flag " << flag_name << " was specified multiple times."
+                << std::endl;
+      return false;
+    }
+    parsed_flags.insert(&*needle);
+
+    if (!parse_flag_info(*needle, &it)) {
+      std::cerr << "Invalid usage for flag " << flag_name << std::endl;
+      return false;
+    }
+  }
+
+  // Check that we parsed all required flags.
+  for (const auto& flag : get_flags()) {
+    if (!flag.required) {
+      continue;
+    }
+
+    if (parsed_flags.count(&flag) == 0) {
+      std::cerr << "Missing required flag " << flag.name << std::endl;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// Just the public wrapper around the parse function.
+bool Parse(const char** argv) { return FlagList::parse(argv); }
+
+}  // namespace flags
diff --git a/tools/util/flags.h b/tools/util/flags.h
new file mode 100644
index 0000000..20bb369
--- /dev/null
+++ b/tools/util/flags.h
@@ -0,0 +1,262 @@
+// Copyright (c) 2023 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
+#define INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
+
+#include <stdint.h>
+
+#include <functional>
+#include <string>
+#include <variant>
+#include <vector>
+
+// This file provides some utils to define a command-line interface with
+// required and optional flags.
+//  - Flag order is not checked.
+//  - Currently supported flag types: BOOLEAN, STRING
+//  - As with most nix tools, using '--' in the command-line means all following
+//  tokens will be considered positional
+//    arguments.
+//    Example: binary -g -- -g --some-other-flag
+//      - the first `-g` is a flag.
+//      - the second `-g` is not a flag.
+//      - `--some-other-flag` is not a flag.
+//  - Both long-form and short-form flags are supported, but boolean flags don't
+//    support split boolean literals (short and long form).
+//    Example:
+//        -g              : allowed, sets g to true.
+//        --my-flag       : allowed, sets --my-flag to true.
+//        --my-flag=true  : allowed, sets --my-flag to true.
+//        --my-flag true  : NOT allowed.
+//        -g true         : NOT allowed.
+//        --my-flag=TRUE  : NOT allowed.
+//
+//  - This implementation also supports string flags:
+//        -o myfile.spv       : allowed, sets -o to `myfile.spv`.
+//        --output=myfile.spv : allowed, sets --output to `myfile.spv`.
+//        --output myfile.spv : allowd, sets --output to `myfile.spv`.
+//
+//    Note: then second token is NOT checked for hyphens.
+//          --output -file.spv
+//          flag name:  `output`
+//          flag value: `-file.spv`
+//
+//  - This implementation generates flag at compile time. Meaning flag names
+//  must be valid C++ identifiers.
+//    However, flags are usually using hyphens for word separation. Hence
+//    renaming is done behind the scenes. Example:
+//      // Declaring a long-form flag.
+//      FLAG_LONG_bool(my_flag, [...])
+//
+//      ->  in the code: flags::my_flag.value()
+//      -> command-line: --my-flag
+//
+//  - The only additional lexing done is around '='. Otherwise token list is
+//  processed as received in the Parse()
+//    function.
+//    Lexing the '=' sign:
+//      - This is only done when parsing a long-form flag name.
+//      - the first '=' found is considered a marker for long-form, splitting
+//      the token into 2.
+//        Example: --option=value=abc -> [--option, value=abc]
+//
+// In most cases, you want to define some flags, parse them, and query them.
+// Here is a small code sample:
+//
+// ```c
+//  // Defines a '-h' boolean flag for help printing, optional.
+//  FLAG_SHORT_bool(h, /*default=*/ false, "Print the help.", false);
+//  // Defines a '--my-flag' string flag, required.
+//  FLAG_LONG_string(my_flag, /*default=*/ "", "A magic flag!", true);
+//
+//  int main(int argc, const char** argv) {
+//    if (!flags::Parse(argv)) {
+//      return -1;
+//    }
+//
+//    if (flags::h.value()) {
+//      printf("usage: my-bin --my-flag=<value>\n");
+//      return 0;
+//    }
+//
+//    printf("flag value: %s\n", flags::my_flag.value().c_str());
+//    for (const std::string& arg : flags::positional_arguments) {
+//      printf("arg: %s\n", arg.c_str());
+//    }
+//    return 0;
+//  }
+// ```c
+
+// Those macros can be used to define flags.
+// - They should be used in the global scope.
+// - Underscores in the flag variable name are replaced with hyphens ('-').
+//
+// Example:
+//  FLAG_SHORT_bool(my_flag, false, "some help", false);
+//    -  in the code: flags::my_flag
+//    - command line: --my-flag=true
+//
+#define FLAG_LONG_string(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_LONG(std::string, Name, Default, Required)
+#define FLAG_LONG_bool(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_LONG(bool, Name, Default, Required)
+#define FLAG_LONG_uint(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_LONG(uint32_t, Name, Default, Required)
+
+#define FLAG_SHORT_string(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_SHORT(std::string, Name, Default, Required)
+#define FLAG_SHORT_bool(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_SHORT(bool, Name, Default, Required)
+#define FLAG_SHORT_uint(Name, Default, Required) \
+  UTIL_FLAGS_FLAG_SHORT(uint32_t, Name, Default, Required)
+
+namespace flags {
+
+// Parse the command-line arguments, checking flags, and separating positional
+// arguments from flags.
+//
+// * argv: the argv array received in the main function. This utility expects
+// the last pointer to
+//         be NULL, as it should if coming from the main() function.
+//
+// Returns `true` if the parsing succeeds, `false` otherwise.
+bool Parse(const char** argv);
+
+}  // namespace flags
+
+// ===================== BEGIN NON-PUBLIC SECTION =============================
+// All the code below belongs to the implementation, and there is no guaranteed
+// around the API stability. Please do not use it directly.
+
+// Defines the static variable holding the flag, allowing access like
+// flags::my_flag.
+// By creating the FlagRegistration object, the flag can be added to
+// the global list.
+// The final `extern` definition is ONLY useful for clang-format:
+//  - if the macro doesn't ends with a semicolon, clang-format goes wild.
+//  - cannot disable clang-format for those macros on clang < 16.
+//    (https://github.com/llvm/llvm-project/issues/54522)
+//  - cannot allow trailing semi (-Wextra-semi).
+#define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort)     \
+  namespace flags {                                                         \
+  Flag<Type> Name(Default);                                                 \
+  namespace {                                                               \
+  static FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
+                                              IsShort);                     \
+  }                                                                         \
+  }                                                                         \
+  extern flags::Flag<Type> flags::Name
+
+#define UTIL_FLAGS_FLAG_LONG(Type, Name, Default, Required) \
+  UTIL_FLAGS_FLAG(Type, "--", Name, Default, Required, false)
+#define UTIL_FLAGS_FLAG_SHORT(Type, Name, Default, Required) \
+  UTIL_FLAGS_FLAG(Type, "-", Name, Default, Required, true)
+
+namespace flags {
+
+// Just a wrapper around the flag value.
+template <typename T>
+struct Flag {
+ public:
+  Flag(T&& default_value) : value_(default_value) {}
+  Flag(Flag&& other) = delete;
+  Flag(const Flag& other) = delete;
+
+  const T& value() const { return value_; }
+  T& value() { return value_; }
+
+ private:
+  T value_;
+};
+
+// To add support for new flag-types, this needs to be extended, and the visitor
+// below.
+using FlagType = std::variant<std::reference_wrapper<Flag<std::string>>,
+                              std::reference_wrapper<Flag<bool>>,
+                              std::reference_wrapper<Flag<uint32_t>>>;
+
+template <class>
+inline constexpr bool always_false_v = false;
+
+extern std::vector<std::string> positional_arguments;
+
+// Static class keeping track of the flags/arguments values.
+class FlagList {
+  struct FlagInfo {
+    FlagInfo(FlagType&& flag_, std::string&& name_, bool required_,
+             bool is_short_)
+        : flag(std::move(flag_)),
+          name(std::move(name_)),
+          required(required_),
+          is_short(is_short_) {}
+
+    FlagType flag;
+    std::string name;
+    bool required;
+    bool is_short;
+  };
+
+ public:
+  template <typename T>
+  static void register_flag(Flag<T>& flag, std::string&& name, bool required,
+                            bool is_short) {
+    get_flags().emplace_back(flag, std::move(name), required, is_short);
+  }
+
+  static bool parse(const char** argv);
+
+#ifdef TESTING
+  // Flags are supposed to be constant for the whole app execution, hence the
+  // static storage. Gtest doesn't fork before running a test, meaning we have
+  // to manually clear the context at teardown.
+  static void reset() {
+    get_flags().clear();
+    positional_arguments.clear();
+  }
+#endif
+
+ private:
+  static std::vector<FlagInfo>& get_flags() {
+    static std::vector<FlagInfo> flags;
+    return flags;
+  }
+
+  static bool parse_flag_info(FlagInfo& info, const char*** iterator);
+  static void print_usage(const char* binary_name,
+                          const std::string& usage_format);
+};
+
+template <typename T>
+struct FlagRegistration {
+  FlagRegistration(Flag<T>& flag, std::string&& name, bool required,
+                   bool is_short) {
+    std::string fixed_name = name;
+    for (auto& c : fixed_name) {
+      if (c == '_') {
+        c = '-';
+      }
+    }
+
+    FlagList::register_flag(flag, std::move(fixed_name), required, is_short);
+  }
+};
+
+// Explicit deduction guide to avoid `-Wctad-maybe-unsupported`.
+template <typename T>
+FlagRegistration(Flag<T>&, std::string&&, bool, bool) -> FlagRegistration<T>;
+
+}  // namespace flags
+
+#endif  // INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py
index 7795d72..e1ca0b7 100755
--- a/utils/check_symbol_exports.py
+++ b/utils/check_symbol_exports.py
@@ -67,7 +67,7 @@
     # by the protobuf compiler:
     #   - AddDescriptors_spvtoolsfuzz_2eproto()
     #   - InitDefaults_spvtoolsfuzz_2eproto()
-    symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
+    symbol_allowlist_pattern = re.compile(r'_Z[0-9]+.*spvtoolsfuzz_2eproto.*')
 
     symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)')
     # Compilaion for Arm has various thunks for constructors, destructors, vtables.
diff --git a/utils/generate_changelog.py b/utils/generate_changelog.py
new file mode 100644
index 0000000..54db728
--- /dev/null
+++ b/utils/generate_changelog.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+# 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.
+
+# Args: <CHANGES-file> <tag> <output-file>
+# Updates an output file with changelog from the given CHANGES file and tag.
+#  - search for first line matching <tag> in file <CHANGES-file>
+#  - search for the next line with a tag
+#  - writes all the lines in between those 2 tags into <output-file>
+
+import errno
+import os
+import os.path
+import re
+import subprocess
+import logging
+import sys
+
+# Regex to match the SPIR-V version tag.
+# Example of matching tags:
+#  - v2020.1
+#  - v2020.1-dev
+#  - v2020.1.rc1
+VERSION_REGEX = re.compile(r'^(v\d+\.\d+) +[0-9]+-[0-9]+-[0-9]+$')
+
+def mkdir_p(directory):
+    """Make the directory, and all its ancestors as required.  Any of the
+    directories are allowed to already exist."""
+
+    if directory == "":
+        # We're being asked to make the current directory.
+        return
+
+    try:
+        os.makedirs(directory)
+    except OSError as e:
+        if e.errno == errno.EEXIST and os.path.isdir(directory):
+            pass
+        else:
+            raise
+
+def main():
+    FORMAT = '%(asctime)s %(message)s'
+    logging.basicConfig(format="[%(asctime)s][%(levelname)-8s] %(message)s", datefmt="%H:%M:%S")
+    if len(sys.argv) != 4:
+        logging.error("usage: {} <CHANGES-path> <tag> <output-file>".format(sys.argv[0]))
+        sys.exit(1)
+
+    changes_path = sys.argv[1]
+    start_tag = sys.argv[2]
+    output_file_path = sys.argv[3]
+
+    changelog = []
+    has_found_start = False
+    with open(changes_path, "r") as file:
+      for line in file.readlines():
+        m = VERSION_REGEX.match(line)
+        if m:
+          print(m.groups()[0])
+          print(start_tag)
+          if has_found_start:
+            break;
+          if start_tag == m.groups()[0]:
+            has_found_start = True
+          continue
+
+        if has_found_start:
+          changelog.append(line)
+
+    if not has_found_start:
+      logging.error("No tag matching {} found.".format(start_tag))
+      sys.exit(1)
+
+    content = "".join(changelog)
+    if os.path.isfile(output_file_path):
+      with open(output_file_path, 'r') as f:
+        if content == f.read():
+          sys.exit(0)
+
+    mkdir_p(os.path.dirname(output_file_path))
+    with open(output_file_path, 'w') as f:
+        f.write(content)
+    sys.exit(0)
+
+if __name__ == '__main__':
+    main()
diff --git a/utils/update_build_version.py b/utils/update_build_version.py
index ec475ea..5a78ada 100755
--- a/utils/update_build_version.py
+++ b/utils/update_build_version.py
@@ -17,13 +17,16 @@
 # Updates an output file with version info unless the new content is the same
 # as the existing content.
 #
-# Args: <repo-path> <output-file>
+# Args: <changes-file> <output-file>
 #
 # The output file will contain a line of text consisting of two C source syntax
 # string literals separated by a comma:
-#  - The software version deduced from the last release tag.
+#  - The software version deduced from the given CHANGES file.
 #  - A longer string with the project name, the software version number, and
-#    git commit information for this release.
+#    git commit information for the CHANGES file's directory.  The commit
+#    information is the output of "git describe" if that succeeds, or "git
+#    rev-parse HEAD" if that succeeds, or otherwise a message containing the
+#    phrase "unknown hash".
 # The string contents are escaped as necessary.
 
 import datetime
@@ -36,13 +39,6 @@
 import sys
 import time
 
-# Regex to match the SPIR-V version tag.
-# Example of matching tags:
-#  - v2020.1
-#  - v2020.1-dev
-#  - v2020.1.rc1
-VERSION_REGEX = re.compile(r'^v(\d+)\.(\d+)(-dev|rc\d+)?$')
-
 # Format of the output generated by this script. Example:
 # "v2023.1", "SPIRV-Tools v2023.1 0fc5526f2b01a0cc89192c10cf8bef77f1007a62, 2023-01-18T14:51:49"
 OUTPUT_FORMAT = '"{version_tag}", "SPIRV-Tools {version_tag} {description}"\n'
@@ -71,10 +67,13 @@
     Raises a RuntimeError 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
+      # 'git' is invoked.
       p = subprocess.Popen(cmd,
                            cwd=directory,
                            stdout=subprocess.PIPE,
-                           stderr=subprocess.PIPE)
+                           stderr=subprocess.PIPE,
+                           shell=os.name == 'nt')
       (stdout, stderr) = p.communicate()
       if p.returncode != 0:
         logging.error('Failed to run "{}" in "{}": {}'.format(cmd, directory, stderr.decode()))
@@ -83,66 +82,38 @@
         return False, None
     return p.returncode == 0, stdout
 
-def deduce_last_release(repo_path):
-    """Returns a software version number parsed from git tags."""
+def deduce_software_version(changes_file):
+    """Returns a tuple (success, software version number) parsed from the
+    given CHANGES file.
 
-    success, tag_list = command_output(['git', 'tag', '--sort=-v:refname'], repo_path)
-    if not success:
-      return False, None
-
-    latest_version_tag = None
-    for tag in tag_list.decode().splitlines():
-      if VERSION_REGEX.match(tag):
-        latest_version_tag = tag
-        break
-
-    if latest_version_tag is None:
-      logging.error("No tag matching version regex matching.")
-      return False, None
-    return True, latest_version_tag
-
-def get_last_release_tuple(repo_path):
-  success, version = deduce_last_release(repo_path)
-  if not success:
-    return False, None
-
-  m = VERSION_REGEX.match(version)
-  if len(m.groups()) != 3:
-    return False, None
-  return True, (int(m.groups()[0]), int(m.groups()[1]))
-
-def deduce_current_release(repo_path):
-  status, version_tuple = get_last_release_tuple(repo_path)
-  if not status:
-    return False, None
-
-  last_release_tag = "v{}.{}-dev".format(*version_tuple)
-  success, tag_list = command_output(['git', 'tag', '--contains'], repo_path)
-  if success:
-    if last_release_tag in set(tag_list.decode().splitlines()):
-      return True, last_release_tag
-  else:
-    logging.warning("Could not check tags for commit. Assuming -dev version.")
-
-  now_year = datetime.datetime.now().year
-  if version_tuple[0] == now_year:
-    version_tuple =  (now_year, version_tuple[1] + 1)
-  else:
-    version_tuple = (now_year, 1)
-
-  return True, "v{}.{}-dev".format(*version_tuple)
-
-def get_description_for_head(repo_path):
-    """Returns a string describing the current Git HEAD version as descriptively
-    as possible, in order of priority:
-      - git describe output
-      - git rev-parse HEAD output
-      - "unknown-hash, <date>"
+    Success is set to True if the software version could be deduced.
+    Software version is undefined if success if False.
+    Function expects the CHANGES file to describes most recent versions first.
     """
 
+    # Match the first well-formed version-and-date line
+    # Allow trailing whitespace in the checked-out source code has
+    # unexpected carriage returns on a linefeed-only system such as
+    # Linux.
+    pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$')
+    with open(changes_file, mode='r') as f:
+        for line in f.readlines():
+            match = pattern.match(line)
+            if match:
+                return True, match.group(1)
+    return False, None
+
+
+def describe(repo_path):
+    """Returns a string describing the current Git HEAD version as descriptively
+    as possible.
+
+    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:
-      success, output = command_output(['git', 'rev-parse', 'HEAD'], repo_path)
+      output = command_output(['git', 'rev-parse', 'HEAD'], repo_path)
 
     if success:
       # decode() is needed here for Python3 compatibility. In Python2,
@@ -158,12 +129,9 @@
     # reproducible builds, allow the builder to override the wall
     # clock time with environment variable SOURCE_DATE_EPOCH
     # containing a (presumably) fixed timestamp.
-    if 'SOURCE_DATE_EPOCH' in os.environ:
-      timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
-      iso_date = datetime.datetime.utcfromtimestamp(timestamp).isoformat()
-    else:
-      iso_date = datetime.datetime.now().isoformat()
-    return "unknown_hash, {}".format(iso_date)
+    timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
+    iso_date = datetime.datetime.utcfromtimestamp(timestamp).isoformat()
+    return "unknown hash, {}".format(iso_date)
 
 def main():
     FORMAT = '%(asctime)s %(message)s'
@@ -172,15 +140,16 @@
         logging.error("usage: {} <repo-path> <output-file>".format(sys.argv[0]))
         sys.exit(1)
 
-    repo_path = os.path.realpath(sys.argv[1])
+    changes_file_path = os.path.realpath(sys.argv[1])
     output_file_path = sys.argv[2]
 
-    success, version = deduce_current_release(repo_path)
+    success, version = deduce_software_version(changes_file_path)
     if not success:
-      logging.warning("Could not deduce latest release version from history.")
-      version = "unknown_version"
+      logging.error("Could not deduce latest release version from {}.".format(changes_file_path))
+      sys.exit(1)
 
-    description = get_description_for_head(repo_path)
+    repo_path = os.path.dirname(changes_file_path)
+    description = describe(repo_path)
     content = OUTPUT_FORMAT.format(version_tag=version, description=description)
 
     # Escape file content.